@parhelia/core 0.1.12882 → 0.1.12884
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/AgentsSidebar.js +1 -1
- package/dist/agents-view/AgentsSidebar.js.map +1 -1
- package/dist/agents-view/AgentsTitlebar.d.ts +1 -1
- package/dist/agents-view/AgentsTitlebar.js +3 -6
- package/dist/agents-view/AgentsTitlebar.js.map +1 -1
- package/dist/agents-view/AgentsView.d.ts +2 -2
- package/dist/agents-view/AgentsView.js +2 -2
- package/dist/agents-view/AgentsView.js.map +1 -1
- package/dist/agents-view/AgentsWorkspaceView.js +1 -12
- package/dist/agents-view/AgentsWorkspaceView.js.map +1 -1
- package/dist/agents-view/CreateAgentView.d.ts +1 -1
- package/dist/agents-view/CreateAgentView.js +1 -1
- package/dist/agents-view/DateAgentsGroup.js +12 -1
- package/dist/agents-view/DateAgentsGroup.js.map +1 -1
- package/dist/agents-view/ProfileAgentsGroup.js +16 -4
- package/dist/agents-view/ProfileAgentsGroup.js.map +1 -1
- package/dist/components/ui/card.d.ts +3 -1
- package/dist/components/ui/card.js +2 -2
- package/dist/components/ui/card.js.map +1 -1
- package/dist/components/ui/checkbox.js +1 -1
- package/dist/components/ui/checkbox.js.map +1 -1
- package/dist/components/ui/context-menu.d.ts +2 -1
- package/dist/components/ui/context-menu.js +6 -3
- package/dist/components/ui/context-menu.js.map +1 -1
- package/dist/components/ui/input.js +2 -2
- package/dist/components/ui/input.js.map +1 -1
- package/dist/components/ui/select.js +1 -1
- package/dist/components/ui/select.js.map +1 -1
- package/dist/components/ui/textarea.js +2 -2
- package/dist/components/ui/textarea.js.map +1 -1
- package/dist/config/config.js +107 -12
- package/dist/config/config.js.map +1 -1
- package/dist/editor/ContextMenu.d.ts +1 -0
- package/dist/editor/ContextMenu.js +4 -4
- package/dist/editor/ContextMenu.js.map +1 -1
- package/dist/editor/FieldActionsOverlay.d.ts +0 -1
- package/dist/editor/FieldActionsOverlay.js +1 -45
- package/dist/editor/FieldActionsOverlay.js.map +1 -1
- package/dist/editor/FieldHistory.d.ts +2 -1
- package/dist/editor/FieldHistory.js +13 -12
- package/dist/editor/FieldHistory.js.map +1 -1
- package/dist/editor/FieldListField.d.ts +1 -1
- package/dist/editor/FieldListField.js +24 -36
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/ImageEditor.d.ts +6 -1
- package/dist/editor/ImageEditor.js +19 -3
- package/dist/editor/ImageEditor.js.map +1 -1
- package/dist/editor/LinkEditorDialog.d.ts +9 -2
- package/dist/editor/LinkEditorDialog.js +174 -70
- package/dist/editor/LinkEditorDialog.js.map +1 -1
- package/dist/editor/MainLayout.js +49 -6
- package/dist/editor/MainLayout.js.map +1 -1
- package/dist/editor/MobileLayout.js +33 -1
- package/dist/editor/MobileLayout.js.map +1 -1
- package/dist/editor/PictureCropper.js +45 -28
- package/dist/editor/PictureCropper.js.map +1 -1
- package/dist/editor/PictureEditor.d.ts +2 -1
- package/dist/editor/PictureEditor.js +5 -14
- package/dist/editor/PictureEditor.js.map +1 -1
- package/dist/editor/ai/AgentProfileSelector.js +7 -7
- package/dist/editor/ai/AgentProfileSelector.js.map +1 -1
- package/dist/editor/ai/Agents.js +20 -6
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/GuidanceOverlay.js +1 -11
- package/dist/editor/ai/GuidanceOverlay.js.map +1 -1
- package/dist/editor/ai/InlineAiDialog.d.ts +1 -0
- package/dist/editor/ai/InlineAiDialog.js +254 -202
- package/dist/editor/ai/InlineAiDialog.js.map +1 -1
- package/dist/editor/ai/InlineAiTextEditTooltip.d.ts +8 -0
- package/dist/editor/ai/InlineAiTextEditTooltip.js +10 -0
- package/dist/editor/ai/InlineAiTextEditTooltip.js.map +1 -0
- package/dist/editor/ai/InlineAiTrigger.js +158 -31
- package/dist/editor/ai/InlineAiTrigger.js.map +1 -1
- package/dist/editor/ai/dialogs/capturePageDom.js +66 -36
- package/dist/editor/ai/dialogs/capturePageDom.js.map +1 -1
- package/dist/editor/ai/dialogs/capturePageScreenshot.js +281 -162
- package/dist/editor/ai/dialogs/capturePageScreenshot.js.map +1 -1
- package/dist/editor/ai/inlineAiTextEditLabels.d.ts +2 -0
- package/dist/editor/ai/inlineAiTextEditLabels.js +8 -0
- package/dist/editor/ai/inlineAiTextEditLabels.js.map +1 -0
- package/dist/editor/ai/prepareInlineAiTextSelection.d.ts +5 -0
- package/dist/editor/ai/prepareInlineAiTextSelection.js +86 -0
- package/dist/editor/ai/prepareInlineAiTextSelection.js.map +1 -0
- package/dist/editor/ai/terminal/agentSessionState.d.ts +3 -0
- package/dist/editor/ai/terminal/agentSessionState.js +3 -1
- package/dist/editor/ai/terminal/agentSessionState.js.map +1 -1
- package/dist/editor/ai/terminal/agentStartRequest.d.ts +2 -1
- package/dist/editor/ai/terminal/agentStartRequest.js +2 -1
- package/dist/editor/ai/terminal/agentStartRequest.js.map +1 -1
- package/dist/editor/ai/terminal/components/AgentCostDisplay.js +1 -1
- package/dist/editor/ai/terminal/components/AgentCostDisplay.js.map +1 -1
- package/dist/editor/ai/terminal/components/AgentDocumentList.d.ts +7 -0
- package/dist/editor/ai/terminal/components/AgentDocumentList.js +55 -13
- package/dist/editor/ai/terminal/components/AgentDocumentList.js.map +1 -1
- package/dist/editor/ai/terminal/components/AgentEditHistoryButton.d.ts +5 -0
- package/dist/editor/ai/terminal/components/AgentEditHistoryButton.js +12 -0
- package/dist/editor/ai/terminal/components/AgentEditHistoryButton.js.map +1 -0
- package/dist/editor/ai/terminal/components/AgentFullPromptControls.d.ts +3 -1
- package/dist/editor/ai/terminal/components/AgentFullPromptControls.js +22 -14
- package/dist/editor/ai/terminal/components/AgentFullPromptControls.js.map +1 -1
- package/dist/editor/ai/terminal/components/AgentModeSelector.js +4 -4
- package/dist/editor/ai/terminal/components/AgentModeSelector.js.map +1 -1
- package/dist/editor/ai/terminal/components/AgentPromptActionButtons.js +4 -4
- package/dist/editor/ai/terminal/components/AgentPromptActionButtons.js.map +1 -1
- package/dist/editor/ai/terminal/components/AgentPromptComposer.js +1 -1
- package/dist/editor/ai/terminal/components/AgentPromptComposer.js.map +1 -1
- package/dist/editor/ai/terminal/components/AgentPromptInputArea.d.ts +2 -1
- package/dist/editor/ai/terminal/components/AgentPromptInputArea.js +8 -11
- package/dist/editor/ai/terminal/components/AgentPromptInputArea.js.map +1 -1
- package/dist/editor/ai/terminal/components/AgentPromptTrayPopovers.d.ts +1 -4
- package/dist/editor/ai/terminal/components/AgentPromptTrayPopovers.js +31 -14
- package/dist/editor/ai/terminal/components/AgentPromptTrayPopovers.js.map +1 -1
- package/dist/editor/ai/terminal/components/AgentSettingsPopover.js +1 -1
- package/dist/editor/ai/terminal/components/AgentSettingsPopover.js.map +1 -1
- package/dist/editor/ai/terminal/components/AgentTerminalFullLayout.d.ts +2 -1
- package/dist/editor/ai/terminal/components/AgentTerminalFullLayout.js +2 -4
- package/dist/editor/ai/terminal/components/AgentTerminalFullLayout.js.map +1 -1
- package/dist/editor/ai/terminal/components/AgentTerminalMessageGroups.js +1 -1
- package/dist/editor/ai/terminal/components/AgentTerminalMessageGroups.js.map +1 -1
- package/dist/editor/ai/terminal/components/AgentTerminalView.js +13 -2
- package/dist/editor/ai/terminal/components/AgentTerminalView.js.map +1 -1
- package/dist/editor/ai/terminal/components/AiResponseMessage.js +11 -9
- package/dist/editor/ai/terminal/components/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/terminal/components/ContextInfoBar.js +22 -2
- package/dist/editor/ai/terminal/components/ContextInfoBar.js.map +1 -1
- package/dist/editor/ai/terminal/components/QueuedPromptsPanel.js +37 -26
- package/dist/editor/ai/terminal/components/QueuedPromptsPanel.js.map +1 -1
- package/dist/editor/ai/terminal/components/ToolCallDisplay.js +3 -1
- package/dist/editor/ai/terminal/components/ToolCallDisplay.js.map +1 -1
- package/dist/editor/ai/terminal/components/UserMessage.d.ts +2 -1
- package/dist/editor/ai/terminal/components/UserMessage.js +144 -8
- package/dist/editor/ai/terminal/components/UserMessage.js.map +1 -1
- package/dist/editor/ai/terminal/useAgentPromptComposerHandlers.js +1 -1
- package/dist/editor/ai/terminal/useAgentPromptComposerHandlers.js.map +1 -1
- package/dist/editor/ai/terminal/useAgentSessionSync.d.ts +1 -0
- package/dist/editor/ai/terminal/useAgentSubmitHandlers.d.ts +3 -1
- package/dist/editor/ai/terminal/useAgentSubmitHandlers.js +9 -3
- package/dist/editor/ai/terminal/useAgentSubmitHandlers.js.map +1 -1
- package/dist/editor/ai/terminal/useAgentTerminalController.js +7 -0
- package/dist/editor/ai/terminal/useAgentTerminalController.js.map +1 -1
- package/dist/editor/ai/terminal/useAgentTerminalUiState.js +1 -1
- package/dist/editor/ai/terminal/useAgentTerminalUiState.js.map +1 -1
- package/dist/editor/ai/terminal/useAgentUserMessageSocketHandler.js +3 -1
- package/dist/editor/ai/terminal/useAgentUserMessageSocketHandler.js.map +1 -1
- package/dist/editor/ai/useActiveAgentConversation.d.ts +3 -0
- package/dist/editor/ai/useActiveAgentConversation.js +32 -0
- package/dist/editor/ai/useActiveAgentConversation.js.map +1 -0
- package/dist/editor/ai/useInlineAiPosition.d.ts +10 -2
- package/dist/editor/ai/useInlineAiPosition.js +32 -71
- package/dist/editor/ai/useInlineAiPosition.js.map +1 -1
- package/dist/editor/ai-image-editor/AiImageResultOverlay.js +30 -62
- package/dist/editor/ai-image-editor/AiImageResultOverlay.js.map +1 -1
- package/dist/editor/bridge/BridgeClient.d.ts +80 -0
- package/dist/editor/bridge/BridgeClient.js +417 -0
- package/dist/editor/bridge/BridgeClient.js.map +1 -0
- package/dist/editor/client/EditorShell.d.ts +5 -1
- package/dist/editor/client/EditorShell.js +295 -127
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +58 -5
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/client/fieldModificationStore.d.ts +1 -0
- package/dist/editor/client/fieldModificationStore.js +7 -2
- package/dist/editor/client/fieldModificationStore.js.map +1 -1
- package/dist/editor/client/hooks/useSocketMessageHandler.js +14 -17
- package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
- package/dist/editor/client/itemsRepository.d.ts +2 -0
- package/dist/editor/client/itemsRepository.js +18 -9
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/client/operations.d.ts +1 -1
- package/dist/editor/client/operations.js +67 -21
- package/dist/editor/client/operations.js.map +1 -1
- package/dist/editor/client/pageModelBuilder.js +24 -7
- package/dist/editor/client/pageModelBuilder.js.map +1 -1
- package/dist/editor/client/ui/EditorChrome.js +1 -1
- package/dist/editor/client/ui/EditorChrome.js.map +1 -1
- package/dist/editor/commands/componentCommands.d.ts +3 -1
- package/dist/editor/commands/componentCommands.js +8 -3
- package/dist/editor/commands/componentCommands.js.map +1 -1
- package/dist/editor/field-types/DateFieldEditor.js +1 -1
- package/dist/editor/field-types/DateFieldEditor.js.map +1 -1
- package/dist/editor/field-types/DateTimeFieldEditor.js +1 -1
- package/dist/editor/field-types/DateTimeFieldEditor.js.map +1 -1
- package/dist/editor/field-types/DropLinkEditor.js +1 -1
- package/dist/editor/field-types/DropLinkEditor.js.map +1 -1
- package/dist/editor/field-types/DropListEditor.js +1 -1
- package/dist/editor/field-types/DropListEditor.js.map +1 -1
- package/dist/editor/field-types/ImageFieldEditor.js +1 -1
- package/dist/editor/field-types/ImageFieldEditor.js.map +1 -1
- package/dist/editor/field-types/InternalLinkFieldEditor.js +1 -1
- package/dist/editor/field-types/InternalLinkFieldEditor.js.map +1 -1
- package/dist/editor/field-types/LinkFieldEditor.js +15 -3
- package/dist/editor/field-types/LinkFieldEditor.js.map +1 -1
- package/dist/editor/field-types/MultiLineText.js +11 -4
- package/dist/editor/field-types/MultiLineText.js.map +1 -1
- package/dist/editor/field-types/NameValueListEditor.js +1 -1
- package/dist/editor/field-types/NameValueListEditor.js.map +1 -1
- package/dist/editor/field-types/PictureFieldEditor.js +2 -2
- package/dist/editor/field-types/PictureFieldEditor.js.map +1 -1
- package/dist/editor/field-types/RawEditor.js +9 -2
- package/dist/editor/field-types/RawEditor.js.map +1 -1
- package/dist/editor/field-types/RichTextEditorComponent.js +170 -77
- package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
- package/dist/editor/field-types/SingleLineText.js +10 -3
- package/dist/editor/field-types/SingleLineText.js.map +1 -1
- package/dist/editor/field-types/TreeListEditor.js +1 -1
- package/dist/editor/field-types/TreeListEditor.js.map +1 -1
- package/dist/editor/field-types/richtext/bridgeRichTextProfile.d.ts +21 -0
- package/dist/editor/field-types/richtext/bridgeRichTextProfile.js +96 -0
- package/dist/editor/field-types/richtext/bridgeRichTextProfile.js.map +1 -0
- package/dist/editor/field-types/richtext/components/ReactSlate.css +44 -6
- package/dist/editor/field-types/richtext/components/ReactSlate.js +191 -36
- package/dist/editor/field-types/richtext/components/ReactSlate.js.map +1 -1
- package/dist/editor/field-types/richtext/components/SimpleRichTextEditor.css +5 -2
- package/dist/editor/field-types/richtext/components/SimpleToolbar.js +5 -4
- package/dist/editor/field-types/richtext/components/SimpleToolbar.js.map +1 -1
- package/dist/editor/field-types/richtext/contextMenuFactory.d.ts +2 -15
- package/dist/editor/field-types/richtext/contextMenuFactory.js +4 -435
- package/dist/editor/field-types/richtext/contextMenuFactory.js.map +1 -1
- package/dist/editor/field-types/richtext/richTextToolbarIcons.d.ts +7 -0
- package/dist/editor/field-types/richtext/richTextToolbarIcons.js +49 -0
- package/dist/editor/field-types/richtext/richTextToolbarIcons.js.map +1 -0
- package/dist/editor/field-types/richtext/types.d.ts +2 -0
- package/dist/editor/field-types/richtext/types.js.map +1 -1
- package/dist/editor/field-types/richtext/utils/conversion.js +23 -2
- package/dist/editor/field-types/richtext/utils/conversion.js.map +1 -1
- package/dist/editor/field-types/useFormFieldCaretPresence.d.ts +13 -0
- package/dist/editor/field-types/useFormFieldCaretPresence.js +92 -0
- package/dist/editor/field-types/useFormFieldCaretPresence.js.map +1 -0
- package/dist/editor/fieldTypes.d.ts +2 -0
- package/dist/editor/media-selector/TreeSelector.js +15 -15
- package/dist/editor/media-selector/TreeSelector.js.map +1 -1
- package/dist/editor/menubar/PageSelector.js +8 -2
- package/dist/editor/menubar/PageSelector.js.map +1 -1
- package/dist/editor/menubar/VersionPreviewCard.js +4 -249
- package/dist/editor/menubar/VersionPreviewCard.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/EditControls.js +2 -2
- package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/ManualBrowser.js +338 -187
- package/dist/editor/menubar/toolbar-sections/ManualBrowser.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js +3 -1
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/ViewportControls.js +1 -1
- package/dist/editor/page-editor-chrome/BridgeInlineFormatOverlay.d.ts +8 -0
- package/dist/editor/page-editor-chrome/BridgeInlineFormatOverlay.js +407 -0
- package/dist/editor/page-editor-chrome/BridgeInlineFormatOverlay.js.map +1 -0
- package/dist/editor/page-editor-chrome/CommentHighlightings.d.ts +5 -2
- package/dist/editor/page-editor-chrome/CommentHighlightings.js +340 -215
- package/dist/editor/page-editor-chrome/CommentHighlightings.js.map +1 -1
- package/dist/editor/page-editor-chrome/FeedbackHighlightBadge.d.ts +5 -1
- package/dist/editor/page-editor-chrome/FeedbackHighlightBadge.js +11 -4
- package/dist/editor/page-editor-chrome/FeedbackHighlightBadge.js.map +1 -1
- package/dist/editor/page-editor-chrome/FieldActionIndicator.js +21 -13
- package/dist/editor/page-editor-chrome/FieldActionIndicator.js.map +1 -1
- package/dist/editor/page-editor-chrome/FieldEditedIndicator.js +23 -29
- package/dist/editor/page-editor-chrome/FieldEditedIndicator.js.map +1 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js +110 -19
- package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
- package/dist/editor/page-editor-chrome/LockedFieldIndicator.d.ts +3 -2
- package/dist/editor/page-editor-chrome/LockedFieldIndicator.js +148 -45
- package/dist/editor/page-editor-chrome/LockedFieldIndicator.js.map +1 -1
- package/dist/editor/page-editor-chrome/PageEditorChrome.d.ts +2 -0
- package/dist/editor/page-editor-chrome/PageEditorChrome.js +25 -21
- package/dist/editor/page-editor-chrome/PageEditorChrome.js.map +1 -1
- package/dist/editor/page-editor-chrome/PictureEditorOverlay.js +163 -128
- package/dist/editor/page-editor-chrome/PictureEditorOverlay.js.map +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.d.ts +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +6 -3
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js.map +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.d.ts +1 -2
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.js +83 -146
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.js.map +1 -1
- package/dist/editor/page-editor-chrome/SuggestionHighlightings.d.ts +5 -2
- package/dist/editor/page-editor-chrome/SuggestionHighlightings.js +144 -63
- package/dist/editor/page-editor-chrome/SuggestionHighlightings.js.map +1 -1
- package/dist/editor/page-editor-chrome/VersionDiffHighlightings.d.ts +1 -2
- package/dist/editor/page-editor-chrome/VersionDiffHighlightings.js +101 -30
- package/dist/editor/page-editor-chrome/VersionDiffHighlightings.js.map +1 -1
- package/dist/editor/page-editor-chrome/bridgeInlineFormatToolbarLayout.d.ts +24 -0
- package/dist/editor/page-editor-chrome/bridgeInlineFormatToolbarLayout.js +89 -0
- package/dist/editor/page-editor-chrome/bridgeInlineFormatToolbarLayout.js.map +1 -0
- package/dist/editor/page-editor-chrome/overlay/IframeOverlayProvider.d.ts +10 -1
- package/dist/editor/page-editor-chrome/overlay/IframeOverlayProvider.js +105 -122
- package/dist/editor/page-editor-chrome/overlay/IframeOverlayProvider.js.map +1 -1
- package/dist/editor/page-editor-chrome/overlay/geometry.d.ts +11 -4
- package/dist/editor/page-editor-chrome/overlay/geometry.js +139 -36
- package/dist/editor/page-editor-chrome/overlay/geometry.js.map +1 -1
- package/dist/editor/page-editor-chrome/useBridgeInlineEditing.d.ts +26 -0
- package/dist/editor/page-editor-chrome/useBridgeInlineEditing.js +228 -0
- package/dist/editor/page-editor-chrome/useBridgeInlineEditing.js.map +1 -0
- package/dist/editor/page-viewer/EditorForm.js +17 -1
- package/dist/editor/page-viewer/EditorForm.js.map +1 -1
- package/dist/editor/page-viewer/MiniMap.d.ts +2 -2
- package/dist/editor/page-viewer/MiniMap.js +176 -364
- package/dist/editor/page-viewer/MiniMap.js.map +1 -1
- package/dist/editor/page-viewer/PageViewer.js +63 -17
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.d.ts +0 -5
- package/dist/editor/page-viewer/PageViewerFrame.js +1685 -1512
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/page-viewer/bridgeFieldPatch.d.ts +20 -0
- package/dist/editor/page-viewer/bridgeFieldPatch.js +33 -0
- package/dist/editor/page-viewer/bridgeFieldPatch.js.map +1 -0
- package/dist/editor/page-viewer/pageViewContext.d.ts +32 -0
- package/dist/editor/page-viewer/pageViewContext.js +37 -6
- package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
- package/dist/editor/reviews/Comment.d.ts +2 -1
- package/dist/editor/reviews/Comment.js +10 -5
- package/dist/editor/reviews/Comment.js.map +1 -1
- package/dist/editor/reviews/CommentDisplayPopover.js +2 -1
- package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -1
- package/dist/editor/reviews/CommentEditor.d.ts +1 -0
- package/dist/editor/reviews/CommentEditor.js +3 -2
- package/dist/editor/reviews/CommentEditor.js.map +1 -1
- package/dist/editor/reviews/CommentPopover.js +69 -10
- package/dist/editor/reviews/CommentPopover.js.map +1 -1
- package/dist/editor/reviews/CommentView.js +24 -4
- package/dist/editor/reviews/CommentView.js.map +1 -1
- package/dist/editor/reviews/Comments.d.ts +0 -2
- package/dist/editor/reviews/Comments.js +31 -31
- package/dist/editor/reviews/Comments.js.map +1 -1
- package/dist/editor/reviews/FeedbackCard.d.ts +4 -2
- package/dist/editor/reviews/FeedbackCard.js +8 -10
- package/dist/editor/reviews/FeedbackCard.js.map +1 -1
- package/dist/editor/reviews/SuggestedEdit.js +4 -6
- package/dist/editor/reviews/SuggestedEdit.js.map +1 -1
- package/dist/editor/reviews/SuggestionCommentThread.js +3 -3
- package/dist/editor/reviews/SuggestionCommentThread.js.map +1 -1
- package/dist/editor/reviews/SuggestionDisplayPopover.js +3 -2
- package/dist/editor/reviews/SuggestionDisplayPopover.js.map +1 -1
- package/dist/editor/reviews/commentAi.js +96 -27
- package/dist/editor/reviews/commentAi.js.map +1 -1
- package/dist/editor/reviews/commentTransientSelection.d.ts +23 -0
- package/dist/editor/reviews/commentTransientSelection.js +7 -0
- package/dist/editor/reviews/commentTransientSelection.js.map +1 -0
- package/dist/editor/reviews/feedbackOrdering.d.ts +5 -0
- package/dist/editor/reviews/feedbackOrdering.js +27 -0
- package/dist/editor/reviews/feedbackOrdering.js.map +1 -0
- package/dist/editor/reviews/feedbackSelection.js +32 -4
- package/dist/editor/reviews/feedbackSelection.js.map +1 -1
- package/dist/editor/reviews/suggestedEditState.d.ts +12 -0
- package/dist/editor/reviews/suggestedEditState.js +90 -0
- package/dist/editor/reviews/suggestedEditState.js.map +1 -0
- package/dist/editor/reviews/suggestionDisplayValue.d.ts +43 -0
- package/dist/editor/reviews/suggestionDisplayValue.js +93 -0
- package/dist/editor/reviews/suggestionDisplayValue.js.map +1 -0
- package/dist/editor/services/agentService.d.ts +15 -0
- package/dist/editor/services/agentService.js +11 -1
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/reviewsService.d.ts +2 -2
- package/dist/editor/services/reviewsService.js.map +1 -1
- package/dist/editor/settings/SettingsView.js +2 -2
- package/dist/editor/settings/SettingsView.js.map +1 -1
- package/dist/editor/settings/panels/ProjectTemplatesPanel.js +1 -1
- package/dist/editor/settings/panels/ProjectTemplatesPanel.js.map +1 -1
- package/dist/editor/settings/panels/ProvidersPanel.js +2 -3
- package/dist/editor/settings/panels/ProvidersPanel.js.map +1 -1
- package/dist/editor/sidebar/MorePanelsButton.js +1 -1
- package/dist/editor/sidebar/MorePanelsButton.js.map +1 -1
- package/dist/editor/sidebar/Validation.js +4 -1
- package/dist/editor/sidebar/Validation.js.map +1 -1
- package/dist/editor/sidebar/Workbox.js +1 -1
- package/dist/editor/sidebar/Workbox.js.map +1 -1
- package/dist/editor/template-wizard/TemplateStructureInlineEditor.js +1 -1
- package/dist/editor/template-wizard/TemplateStructureInlineEditor.js.map +1 -1
- package/dist/editor/ui/IconSelectorDialog.js +1 -1
- package/dist/editor/ui/IconSelectorDialog.js.map +1 -1
- package/dist/editor/ui/SimpleIconButton.d.ts +2 -2
- package/dist/editor/ui/SimpleIconButton.js +7 -1
- package/dist/editor/ui/SimpleIconButton.js.map +1 -1
- package/dist/editor/ui/Splitter.d.ts +1 -0
- package/dist/editor/ui/Splitter.js +12 -2
- package/dist/editor/ui/Splitter.js.map +1 -1
- package/dist/editor/ui/animationSettle.d.ts +32 -0
- package/dist/editor/ui/animationSettle.js +85 -0
- package/dist/editor/ui/animationSettle.js.map +1 -0
- package/dist/editor/utils/expandSelectionAtCaret.d.ts +15 -0
- package/dist/editor/utils/expandSelectionAtCaret.js +183 -0
- package/dist/editor/utils/expandSelectionAtCaret.js.map +1 -0
- package/dist/editor/utils.d.ts +1 -17
- package/dist/editor/utils.js +0 -143
- package/dist/editor/utils.js.map +1 -1
- package/dist/editor/version-diff/versionDiffTargets.d.ts +3 -8
- package/dist/editor/version-diff/versionDiffTargets.js +37 -94
- package/dist/editor/version-diff/versionDiffTargets.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/splash-screen/ModernSplashScreen.js +11 -3
- package/dist/splash-screen/ModernSplashScreen.js.map +1 -1
- package/dist/splash-screen/NewPage.js +7 -5
- package/dist/splash-screen/NewPage.js.map +1 -1
- package/dist/splash-screen/OpenPage.js +5 -3
- package/dist/splash-screen/OpenPage.js.map +1 -1
- package/dist/splash-screen/RecentPages.js +3 -3
- package/dist/splash-screen/RecentPages.js.map +1 -1
- package/dist/task-board/components/TaskDetailPanel.js +2 -1
- package/dist/task-board/components/TaskDetailPanel.js.map +1 -1
- package/dist/task-board/views/DependencyGraphView.d.ts +42 -1
- package/dist/task-board/views/DependencyGraphView.js +94 -0
- package/dist/task-board/views/DependencyGraphView.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/package.json +2 -1
- package/styles.css +96 -0
- package/dist/editor/page-editor-chrome/InlineEditor.d.ts +0 -7
- package/dist/editor/page-editor-chrome/InlineEditor.js +0 -1719
- package/dist/editor/page-editor-chrome/InlineEditor.js.map +0 -1
- package/dist/editor/page-editor-chrome/overlay/iframeAccess.d.ts +0 -2
- package/dist/editor/page-editor-chrome/overlay/iframeAccess.js +0 -21
- package/dist/editor/page-editor-chrome/overlay/iframeAccess.js.map +0 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.d.ts +0 -7
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js +0 -758
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +0 -1
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.d.ts +0 -3
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.js +0 -796
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.js.map +0 -1
|
@@ -1,1719 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef, useState } from "react";
|
|
3
|
-
import { createPortal } from "react-dom";
|
|
4
|
-
import { useEditContext, useFieldsEditContext, useEditContextRef, } from "../client/editContext";
|
|
5
|
-
import { findFieldElement, getFieldDescriptorFromElement, hasFieldLock, getSessionWithFieldLock, } from "../utils";
|
|
6
|
-
import { applyPatch, diffWords } from "diff";
|
|
7
|
-
import { createPatch } from "diff";
|
|
8
|
-
// import { useDebouncedCallback } from "use-debounce";
|
|
9
|
-
// import { executePrompt } from "../services/aiService";
|
|
10
|
-
import { useInlineAiCompletion } from "./useInlineAICompletion";
|
|
11
|
-
import { getComponentById } from "../componentTreeHelper";
|
|
12
|
-
import { useSlotContext } from "../views/editorSlotContext";
|
|
13
|
-
const INLINE_FORMAT_QUERY_COMMANDS_BY_MARK = {
|
|
14
|
-
bold: "bold",
|
|
15
|
-
italic: "italic",
|
|
16
|
-
underline: "underline",
|
|
17
|
-
strikethrough: "strikeThrough",
|
|
18
|
-
subscript: "subscript",
|
|
19
|
-
superscript: "superscript",
|
|
20
|
-
};
|
|
21
|
-
function getInlineFormatMarkId(menuItemId) {
|
|
22
|
-
return menuItemId
|
|
23
|
-
.replace(/^slate-format-mark-/, "")
|
|
24
|
-
.replace(/^format-mark-/, "");
|
|
25
|
-
}
|
|
26
|
-
function getInlineFieldKey(fieldElement) {
|
|
27
|
-
const fieldId = fieldElement.getAttribute("data-fieldid") || "";
|
|
28
|
-
const itemId = fieldElement.getAttribute("data-itemid") || "";
|
|
29
|
-
const language = fieldElement.getAttribute("data-language") || "";
|
|
30
|
-
const version = fieldElement.getAttribute("data-version") || "";
|
|
31
|
-
return `${itemId}:${language}:${version}:${fieldId}`;
|
|
32
|
-
}
|
|
33
|
-
function getSelectedTextAnchorRect(range, root) {
|
|
34
|
-
const ownerDocument = root.ownerDocument;
|
|
35
|
-
const nodeFilter = ownerDocument.defaultView?.NodeFilter.SHOW_TEXT ?? NodeFilter.SHOW_TEXT;
|
|
36
|
-
const walker = ownerDocument.createTreeWalker(root, nodeFilter);
|
|
37
|
-
let currentNode = walker.nextNode();
|
|
38
|
-
while (currentNode) {
|
|
39
|
-
if (range.intersectsNode(currentNode)) {
|
|
40
|
-
const textNode = currentNode;
|
|
41
|
-
const start = textNode === range.startContainer ? range.startOffset : 0;
|
|
42
|
-
const end = textNode === range.endContainer ? range.endOffset : textNode.length;
|
|
43
|
-
if (end > start) {
|
|
44
|
-
const probeRange = ownerDocument.createRange();
|
|
45
|
-
probeRange.setStart(textNode, start);
|
|
46
|
-
probeRange.setEnd(textNode, end);
|
|
47
|
-
const rect = Array.from(probeRange.getClientRects()).find((candidate) => candidate.width > 0 && candidate.height > 0);
|
|
48
|
-
probeRange.detach();
|
|
49
|
-
if (rect)
|
|
50
|
-
return rect;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
currentNode = walker.nextNode();
|
|
54
|
-
}
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
function getFirstVisibleSelectionLineRect(range, viewportWidth, viewportHeight) {
|
|
58
|
-
const visibleRects = Array.from(range.getClientRects())
|
|
59
|
-
.filter((rect) => rect.width > 0 &&
|
|
60
|
-
rect.height > 0 &&
|
|
61
|
-
rect.bottom >= 0 &&
|
|
62
|
-
rect.top <= viewportHeight &&
|
|
63
|
-
rect.right >= 0 &&
|
|
64
|
-
rect.left <= viewportWidth)
|
|
65
|
-
.sort((a, b) => a.top - b.top || a.left - b.left);
|
|
66
|
-
const firstRect = visibleRects[0];
|
|
67
|
-
if (!firstRect)
|
|
68
|
-
return null;
|
|
69
|
-
const rowTolerance = Math.max(2, firstRect.height / 2);
|
|
70
|
-
const firstLineRects = visibleRects.filter((rect) => Math.abs(rect.top - firstRect.top) <= rowTolerance);
|
|
71
|
-
const left = Math.min(...firstLineRects.map((rect) => rect.left));
|
|
72
|
-
const top = Math.min(...firstLineRects.map((rect) => rect.top));
|
|
73
|
-
const right = Math.max(...firstLineRects.map((rect) => rect.right));
|
|
74
|
-
const bottom = Math.max(...firstLineRects.map((rect) => rect.bottom));
|
|
75
|
-
return DOMRect.fromRect({
|
|
76
|
-
x: left,
|
|
77
|
-
y: top,
|
|
78
|
-
width: right - left,
|
|
79
|
-
height: bottom - top,
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
function hasUsableRect(rect) {
|
|
83
|
-
return (!!rect &&
|
|
84
|
-
Number.isFinite(rect.left) &&
|
|
85
|
-
Number.isFinite(rect.top) &&
|
|
86
|
-
rect.width > 0 &&
|
|
87
|
-
rect.height > 0);
|
|
88
|
-
}
|
|
89
|
-
function getInlineFormatAnchorRect(range, fieldElement, viewportWidth, viewportHeight) {
|
|
90
|
-
const candidates = [
|
|
91
|
-
getFirstVisibleSelectionLineRect(range, viewportWidth, viewportHeight),
|
|
92
|
-
getSelectedTextAnchorRect(range, fieldElement),
|
|
93
|
-
range.getBoundingClientRect(),
|
|
94
|
-
fieldElement.getBoundingClientRect(),
|
|
95
|
-
].filter(hasUsableRect);
|
|
96
|
-
return (candidates.find((rect) => rect.bottom >= 0 &&
|
|
97
|
-
rect.top <= viewportHeight &&
|
|
98
|
-
rect.right >= 0 &&
|
|
99
|
-
rect.left <= viewportWidth) ??
|
|
100
|
-
candidates[0] ??
|
|
101
|
-
null);
|
|
102
|
-
}
|
|
103
|
-
function getTextNodeAtTextOffset(root, offset, affinity = "backward") {
|
|
104
|
-
const ownerDocument = root.ownerDocument || document;
|
|
105
|
-
const nodeFilter = ownerDocument.defaultView?.NodeFilter.SHOW_TEXT ?? NodeFilter.SHOW_TEXT;
|
|
106
|
-
const walker = ownerDocument.createTreeWalker(root, nodeFilter);
|
|
107
|
-
let currentNode;
|
|
108
|
-
let boundaryNode = null;
|
|
109
|
-
let remainingOffset = offset;
|
|
110
|
-
while ((currentNode = walker.nextNode())) {
|
|
111
|
-
const nodeLength = currentNode.textContent?.length || 0;
|
|
112
|
-
if (remainingOffset < nodeLength) {
|
|
113
|
-
return { node: currentNode, offset: remainingOffset };
|
|
114
|
-
}
|
|
115
|
-
if (remainingOffset === nodeLength) {
|
|
116
|
-
if (affinity === "backward") {
|
|
117
|
-
return { node: currentNode, offset: remainingOffset };
|
|
118
|
-
}
|
|
119
|
-
boundaryNode = currentNode;
|
|
120
|
-
remainingOffset = 0;
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
remainingOffset -= nodeLength;
|
|
124
|
-
}
|
|
125
|
-
return boundaryNode
|
|
126
|
-
? { node: boundaryNode, offset: boundaryNode.textContent?.length || 0 }
|
|
127
|
-
: null;
|
|
128
|
-
}
|
|
129
|
-
function createRangeFromTextOffsets(fieldElement, startOffset, endOffset) {
|
|
130
|
-
const startData = getTextNodeAtTextOffset(fieldElement, startOffset, "forward");
|
|
131
|
-
const endData = getTextNodeAtTextOffset(fieldElement, endOffset, "backward");
|
|
132
|
-
if (!startData || !endData)
|
|
133
|
-
return null;
|
|
134
|
-
try {
|
|
135
|
-
const range = fieldElement.ownerDocument.createRange();
|
|
136
|
-
range.setStart(startData.node, startData.offset);
|
|
137
|
-
range.setEnd(endData.node, endData.offset);
|
|
138
|
-
return range;
|
|
139
|
-
}
|
|
140
|
-
catch {
|
|
141
|
-
return null;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
function getAccessibleIframeDocument(iframe) {
|
|
145
|
-
if (!iframe)
|
|
146
|
-
return null;
|
|
147
|
-
try {
|
|
148
|
-
return iframe.contentDocument || iframe.contentWindow?.document || null;
|
|
149
|
-
}
|
|
150
|
-
catch {
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
function getAccessibleIframeSelection(iframe) {
|
|
155
|
-
if (!iframe)
|
|
156
|
-
return null;
|
|
157
|
-
try {
|
|
158
|
-
return iframe.contentWindow?.getSelection() ?? null;
|
|
159
|
-
}
|
|
160
|
-
catch {
|
|
161
|
-
return null;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
export function InlineEditor(props) {
|
|
165
|
-
const context = useEditContext();
|
|
166
|
-
if (!context)
|
|
167
|
-
return null;
|
|
168
|
-
return _jsx(InlineEditorContent, { ...props, context: context });
|
|
169
|
-
}
|
|
170
|
-
function InlineEditorContent({ pageViewContext, compareView, context, }) {
|
|
171
|
-
const contextRef = useEditContextRef();
|
|
172
|
-
const modifiedFieldsContext = useFieldsEditContext();
|
|
173
|
-
const slotContext = useSlotContext();
|
|
174
|
-
const [cursorSpanId] = useState(() => `cursor-indicator-${Math.random().toString(36).substring(2, 9)}`);
|
|
175
|
-
const isUpdatingRef = useRef(false);
|
|
176
|
-
const mutationObserverRef = useRef(null);
|
|
177
|
-
const selectionChangeListenerRef = useRef(null);
|
|
178
|
-
const lastQueuedInlineValueRef = useRef(new Map());
|
|
179
|
-
const lastInlineUserEditAtRef = useRef(0);
|
|
180
|
-
const [inlineFormatOverlay, setInlineFormatOverlay] = useState({
|
|
181
|
-
visible: false,
|
|
182
|
-
left: 0,
|
|
183
|
-
top: 0,
|
|
184
|
-
activeCommandIds: [],
|
|
185
|
-
});
|
|
186
|
-
const inlineFormatOverlayRef = useRef(null);
|
|
187
|
-
const inlineFormatSelectionRef = useRef(null);
|
|
188
|
-
const inlineFormatCommandsCacheRef = useRef(new Map());
|
|
189
|
-
const inlineFormatCommandsLoadingRef = useRef(new Set());
|
|
190
|
-
const inlineFormatPointerDownRef = useRef(false);
|
|
191
|
-
const caretUpdateFrameRef = useRef(null);
|
|
192
|
-
const lastSentCaretRef = useRef(null);
|
|
193
|
-
const refreshCompletion = useInlineAiCompletion({
|
|
194
|
-
pageViewContext,
|
|
195
|
-
cursorSpanId,
|
|
196
|
-
isUpdatingRef,
|
|
197
|
-
});
|
|
198
|
-
const isTextFieldType = (fieldType) => {
|
|
199
|
-
const fieldTypeLower = fieldType.toLowerCase();
|
|
200
|
-
return (fieldTypeLower === "single-line text" ||
|
|
201
|
-
fieldTypeLower === "multi-line text" ||
|
|
202
|
-
fieldTypeLower === "rich text");
|
|
203
|
-
};
|
|
204
|
-
const hideInlineFormatOverlay = () => {
|
|
205
|
-
inlineFormatSelectionRef.current = null;
|
|
206
|
-
setInlineFormatOverlay((current) => current.visible
|
|
207
|
-
? { visible: false, left: 0, top: 0, activeCommandIds: [] }
|
|
208
|
-
: current);
|
|
209
|
-
};
|
|
210
|
-
const getSelectedInlineRichTextField = (range) => {
|
|
211
|
-
const candidate = range.commonAncestorContainer instanceof Element
|
|
212
|
-
? range.commonAncestorContainer
|
|
213
|
-
: range.commonAncestorContainer.parentElement;
|
|
214
|
-
const fieldElement = candidate?.closest?.('[data-is-richtext="true"][data-fieldid][data-itemid][data-language][data-version]');
|
|
215
|
-
if (!fieldElement ||
|
|
216
|
-
!fieldElement.contains(range.startContainer) ||
|
|
217
|
-
!fieldElement.contains(range.endContainer)) {
|
|
218
|
-
return null;
|
|
219
|
-
}
|
|
220
|
-
return fieldElement;
|
|
221
|
-
};
|
|
222
|
-
const getInlineRichTextFieldForSelectedRange = (iframeDocument, selectedRange) => {
|
|
223
|
-
if (!selectedRange?.fieldId || !selectedRange.itemId)
|
|
224
|
-
return null;
|
|
225
|
-
const fields = Array.from(iframeDocument.querySelectorAll('[data-is-richtext="true"][data-fieldid][data-itemid][data-language][data-version]'));
|
|
226
|
-
return (fields.find((fieldElement) => {
|
|
227
|
-
const version = fieldElement.getAttribute("data-version");
|
|
228
|
-
return (fieldElement.getAttribute("data-fieldid") === selectedRange.fieldId &&
|
|
229
|
-
fieldElement.getAttribute("data-itemid") === selectedRange.itemId &&
|
|
230
|
-
(!selectedRange.language ||
|
|
231
|
-
fieldElement.getAttribute("data-language") ===
|
|
232
|
-
selectedRange.language) &&
|
|
233
|
-
(selectedRange.version == null ||
|
|
234
|
-
version === String(selectedRange.version)));
|
|
235
|
-
}) ?? null);
|
|
236
|
-
};
|
|
237
|
-
const loadInlineFormatCommands = async (fieldElement) => {
|
|
238
|
-
const key = getInlineFieldKey(fieldElement);
|
|
239
|
-
if (inlineFormatCommandsCacheRef.current.has(key) ||
|
|
240
|
-
inlineFormatCommandsLoadingRef.current.has(key)) {
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
inlineFormatCommandsLoadingRef.current.add(key);
|
|
244
|
-
try {
|
|
245
|
-
const currentContext = contextRef.current;
|
|
246
|
-
if (!currentContext)
|
|
247
|
-
return;
|
|
248
|
-
const field = await currentContext.itemsRepository.getField(getFieldDescriptorFromElement(fieldElement));
|
|
249
|
-
const contextMenuFactory = field
|
|
250
|
-
? currentContext.configuration.fieldTypes[field.type]
|
|
251
|
-
?.contextMenuFactory
|
|
252
|
-
: undefined;
|
|
253
|
-
if (!field || !contextMenuFactory) {
|
|
254
|
-
inlineFormatCommandsCacheRef.current.set(key, []);
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
const menuItems = await contextMenuFactory(field, currentContext);
|
|
258
|
-
const formatSubmenu = menuItems.find((item) => item.items?.length &&
|
|
259
|
-
(item.id === "format-submenu" ||
|
|
260
|
-
item.id === "slate-format-submenu" ||
|
|
261
|
-
item.label === "Format"));
|
|
262
|
-
const commands = formatSubmenu?.items
|
|
263
|
-
?.filter((item) => !item.separator && !item.disabled && !!item.command)
|
|
264
|
-
.map((item) => ({
|
|
265
|
-
id: item.id,
|
|
266
|
-
label: item.label ?? "",
|
|
267
|
-
icon: item.icon,
|
|
268
|
-
command: item.command,
|
|
269
|
-
queryCommand: INLINE_FORMAT_QUERY_COMMANDS_BY_MARK[getInlineFormatMarkId(item.id)],
|
|
270
|
-
})) ?? [];
|
|
271
|
-
inlineFormatCommandsCacheRef.current.set(key, commands);
|
|
272
|
-
}
|
|
273
|
-
catch (error) {
|
|
274
|
-
console.error("[InlineEditor] Failed to load format overlay:", error);
|
|
275
|
-
inlineFormatCommandsCacheRef.current.set(key, []);
|
|
276
|
-
}
|
|
277
|
-
finally {
|
|
278
|
-
inlineFormatCommandsLoadingRef.current.delete(key);
|
|
279
|
-
window.requestAnimationFrame(updateInlineFormatOverlay);
|
|
280
|
-
}
|
|
281
|
-
};
|
|
282
|
-
const getActiveInlineFormatCommandIds = (iframeDocument, range, commands) => {
|
|
283
|
-
const selection = iframeDocument.getSelection();
|
|
284
|
-
if (!selection)
|
|
285
|
-
return [];
|
|
286
|
-
const queryActiveCommands = () => commands
|
|
287
|
-
.filter((command) => {
|
|
288
|
-
if (!command.queryCommand)
|
|
289
|
-
return false;
|
|
290
|
-
try {
|
|
291
|
-
return iframeDocument.queryCommandState(command.queryCommand);
|
|
292
|
-
}
|
|
293
|
-
catch {
|
|
294
|
-
return false;
|
|
295
|
-
}
|
|
296
|
-
})
|
|
297
|
-
.map((command) => command.id);
|
|
298
|
-
const currentRange = selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
|
|
299
|
-
if (!currentRange ||
|
|
300
|
-
currentRange.startContainer !== range.startContainer ||
|
|
301
|
-
currentRange.startOffset !== range.startOffset ||
|
|
302
|
-
currentRange.endContainer !== range.endContainer ||
|
|
303
|
-
currentRange.endOffset !== range.endOffset) {
|
|
304
|
-
return [];
|
|
305
|
-
}
|
|
306
|
-
return queryActiveCommands();
|
|
307
|
-
};
|
|
308
|
-
const updateInlineFormatOverlay = () => {
|
|
309
|
-
if (inlineFormatPointerDownRef.current) {
|
|
310
|
-
hideInlineFormatOverlay();
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
if (compareView ||
|
|
314
|
-
contextRef.current?.readonly ||
|
|
315
|
-
(contextRef.current?.mode !== "edit" &&
|
|
316
|
-
contextRef.current?.mode !== "suggestions")) {
|
|
317
|
-
hideInlineFormatOverlay();
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
const iframe = pageViewContext.editorIframe;
|
|
321
|
-
const iframeDocument = getAccessibleIframeDocument(iframe);
|
|
322
|
-
if (!iframe || !iframeDocument) {
|
|
323
|
-
hideInlineFormatOverlay();
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
const selection = getAccessibleIframeSelection(iframe);
|
|
327
|
-
const selectedRange = contextRef.current?.selectedRange;
|
|
328
|
-
if (selection?.rangeCount && selection.isCollapsed) {
|
|
329
|
-
hideInlineFormatOverlay();
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
|
-
const liveRange = selection && !selection.isCollapsed && selection.rangeCount > 0
|
|
333
|
-
? selection.getRangeAt(0)
|
|
334
|
-
: null;
|
|
335
|
-
const liveRangeText = liveRange?.toString().trim();
|
|
336
|
-
if (!liveRangeText && !selectedRange?.text?.trim()) {
|
|
337
|
-
hideInlineFormatOverlay();
|
|
338
|
-
return;
|
|
339
|
-
}
|
|
340
|
-
const liveRangeFieldElement = liveRange
|
|
341
|
-
? getSelectedInlineRichTextField(liveRange)
|
|
342
|
-
: null;
|
|
343
|
-
const fieldElement = liveRangeFieldElement ??
|
|
344
|
-
getInlineRichTextFieldForSelectedRange(iframeDocument, selectedRange);
|
|
345
|
-
if (!fieldElement || !fieldElement.isContentEditable) {
|
|
346
|
-
hideInlineFormatOverlay();
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
const range = (liveRangeFieldElement ? liveRange : null) ??
|
|
350
|
-
createRangeFromTextOffsets(fieldElement, selectedRange?.startOffset ?? 0, selectedRange?.endOffset ?? 0);
|
|
351
|
-
if (!range) {
|
|
352
|
-
hideInlineFormatOverlay();
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
const commandKey = getInlineFieldKey(fieldElement);
|
|
356
|
-
const inlineFormatCommands = inlineFormatCommandsCacheRef.current.get(commandKey);
|
|
357
|
-
if (!inlineFormatCommands) {
|
|
358
|
-
void loadInlineFormatCommands(fieldElement);
|
|
359
|
-
hideInlineFormatOverlay();
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
if (inlineFormatCommands.length === 0) {
|
|
363
|
-
hideInlineFormatOverlay();
|
|
364
|
-
return;
|
|
365
|
-
}
|
|
366
|
-
const iframeViewportWidth = iframe.contentWindow?.innerWidth ?? iframe.clientWidth;
|
|
367
|
-
const iframeViewportHeight = iframe.contentWindow?.innerHeight ?? iframe.clientHeight;
|
|
368
|
-
const anchorRect = getInlineFormatAnchorRect(range, fieldElement, iframeViewportWidth, iframeViewportHeight) ?? fieldElement.getBoundingClientRect();
|
|
369
|
-
const iframeRect = iframe.getBoundingClientRect();
|
|
370
|
-
const overlayWidth = inlineFormatOverlayRef.current?.offsetWidth ?? 180;
|
|
371
|
-
const overlayHeight = inlineFormatOverlayRef.current?.offsetHeight ?? 34;
|
|
372
|
-
const viewportPadding = 8;
|
|
373
|
-
const selectionGap = 8;
|
|
374
|
-
const anchorCenter = iframeRect.left +
|
|
375
|
-
anchorRect.left +
|
|
376
|
-
Math.min(anchorRect.width, overlayWidth) / 2;
|
|
377
|
-
let top = iframeRect.top + anchorRect.top - overlayHeight - selectionGap;
|
|
378
|
-
if (top < viewportPadding) {
|
|
379
|
-
top = iframeRect.top + anchorRect.bottom + selectionGap + 36;
|
|
380
|
-
}
|
|
381
|
-
top = Math.max(viewportPadding, Math.min(top, window.innerHeight - overlayHeight - viewportPadding));
|
|
382
|
-
const left = Math.max(viewportPadding, Math.min(anchorCenter - overlayWidth / 2, window.innerWidth - overlayWidth - viewportPadding));
|
|
383
|
-
inlineFormatSelectionRef.current = {
|
|
384
|
-
range: range.cloneRange(),
|
|
385
|
-
fieldElement,
|
|
386
|
-
};
|
|
387
|
-
setInlineFormatOverlay({
|
|
388
|
-
visible: true,
|
|
389
|
-
left,
|
|
390
|
-
top,
|
|
391
|
-
activeCommandIds: getActiveInlineFormatCommandIds(iframeDocument, range, inlineFormatCommands),
|
|
392
|
-
});
|
|
393
|
-
};
|
|
394
|
-
const applyInlineFormatCommand = async (command, event) => {
|
|
395
|
-
event.preventDefault();
|
|
396
|
-
event.stopPropagation();
|
|
397
|
-
const selectionContext = inlineFormatSelectionRef.current;
|
|
398
|
-
if (!selectionContext?.range || !command.command)
|
|
399
|
-
return;
|
|
400
|
-
const iframeWindow = pageViewContext.editorIframe?.contentWindow;
|
|
401
|
-
const iframeDocument = selectionContext.range.startContainer.ownerDocument;
|
|
402
|
-
if (!iframeDocument)
|
|
403
|
-
return;
|
|
404
|
-
const selection = iframeDocument.getSelection();
|
|
405
|
-
if (!selection)
|
|
406
|
-
return;
|
|
407
|
-
iframeWindow?.focus();
|
|
408
|
-
selectionContext.fieldElement.focus({ preventScroll: true });
|
|
409
|
-
selection.removeAllRanges();
|
|
410
|
-
selection.addRange(selectionContext.range);
|
|
411
|
-
await command.command(event);
|
|
412
|
-
if (selection.rangeCount > 0) {
|
|
413
|
-
inlineFormatSelectionRef.current = {
|
|
414
|
-
range: selection.getRangeAt(0).cloneRange(),
|
|
415
|
-
fieldElement: selectionContext.fieldElement,
|
|
416
|
-
};
|
|
417
|
-
}
|
|
418
|
-
window.requestAnimationFrame(updateInlineFormatOverlay);
|
|
419
|
-
};
|
|
420
|
-
// Stable identity check: compare by field key (not by DOM node reference).
|
|
421
|
-
// This avoids stomping the active field if the iframe DOM is replaced/morphed.
|
|
422
|
-
const getFieldKeyFromElement = (el) => {
|
|
423
|
-
const fieldId = el.getAttribute("data-fieldid") || "";
|
|
424
|
-
const itemId = el.getAttribute("data-itemid") || "";
|
|
425
|
-
const language = el.getAttribute("data-language") || "";
|
|
426
|
-
const versionRaw = el.getAttribute("data-version");
|
|
427
|
-
const version = versionRaw ? parseInt(versionRaw, 10) : NaN;
|
|
428
|
-
return { fieldId, itemId, language, version };
|
|
429
|
-
};
|
|
430
|
-
const isSameFieldKey = (el, key) => {
|
|
431
|
-
if (!key)
|
|
432
|
-
return false;
|
|
433
|
-
const k = getFieldKeyFromElement(el);
|
|
434
|
-
return (k.fieldId === key.fieldId &&
|
|
435
|
-
k.itemId === key.itemId &&
|
|
436
|
-
k.language === key.language &&
|
|
437
|
-
k.version === key.version);
|
|
438
|
-
};
|
|
439
|
-
// Only skip DOM updates for fields being edited inline in the iframe.
|
|
440
|
-
// Do NOT skip updates for sidebar-focused fields - we want those to update the preview.
|
|
441
|
-
const activeFieldKey = modifiedFieldsContext?.inlineEditingFieldElement
|
|
442
|
-
? getFieldKeyFromElement(modifiedFieldsContext.inlineEditingFieldElement)
|
|
443
|
-
: context.selectedRange
|
|
444
|
-
? {
|
|
445
|
-
fieldId: context.selectedRange.fieldId,
|
|
446
|
-
itemId: context.selectedRange.itemId,
|
|
447
|
-
language: context.selectedRange.language,
|
|
448
|
-
version: context.selectedRange.version,
|
|
449
|
-
}
|
|
450
|
-
: undefined;
|
|
451
|
-
const isElementActiveInIframe = (doc, element) => {
|
|
452
|
-
if (!doc || !element)
|
|
453
|
-
return false;
|
|
454
|
-
const activeElement = doc.activeElement;
|
|
455
|
-
return (activeElement === element ||
|
|
456
|
-
(activeElement ? element.contains(activeElement) : false));
|
|
457
|
-
};
|
|
458
|
-
const isSelectionInsideElement = (element) => {
|
|
459
|
-
if (!element)
|
|
460
|
-
return false;
|
|
461
|
-
const iframe = pageViewContext.editorIframe;
|
|
462
|
-
const iframeDocument = getAccessibleIframeDocument(iframe);
|
|
463
|
-
const iframeHasFocus = iframe?.ownerDocument.activeElement === iframe ||
|
|
464
|
-
iframeDocument?.hasFocus() === true;
|
|
465
|
-
if (!iframeHasFocus)
|
|
466
|
-
return false;
|
|
467
|
-
const selection = getAccessibleIframeSelection(pageViewContext.editorIframe);
|
|
468
|
-
return !!(selection?.anchorNode &&
|
|
469
|
-
selection?.focusNode &&
|
|
470
|
-
element.contains(selection.anchorNode) &&
|
|
471
|
-
element.contains(selection.focusNode));
|
|
472
|
-
};
|
|
473
|
-
const isElementActiveOrSelectedInIframe = (doc, element) => isElementActiveInIframe(doc, element) || isSelectionInsideElement(element);
|
|
474
|
-
const clearRemoteCaretPosition = () => {
|
|
475
|
-
if (caretUpdateFrameRef.current != null) {
|
|
476
|
-
window.cancelAnimationFrame(caretUpdateFrameRef.current);
|
|
477
|
-
caretUpdateFrameRef.current = null;
|
|
478
|
-
}
|
|
479
|
-
if (lastSentCaretRef.current === "clear")
|
|
480
|
-
return;
|
|
481
|
-
context.sendSocketMessage({
|
|
482
|
-
type: "caret-position",
|
|
483
|
-
payload: { offset: null },
|
|
484
|
-
});
|
|
485
|
-
lastSentCaretRef.current = "clear";
|
|
486
|
-
};
|
|
487
|
-
const scheduleCaretPositionUpdate = (element, fieldDescriptor) => {
|
|
488
|
-
if (caretUpdateFrameRef.current != null)
|
|
489
|
-
return;
|
|
490
|
-
caretUpdateFrameRef.current = window.requestAnimationFrame(() => {
|
|
491
|
-
caretUpdateFrameRef.current = null;
|
|
492
|
-
const iframeDocument = getAccessibleIframeDocument(pageViewContext.editorIframe);
|
|
493
|
-
if (!isElementActiveOrSelectedInIframe(iframeDocument, element)) {
|
|
494
|
-
clearRemoteCaretPosition();
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
const selection = getAccessibleIframeSelection(pageViewContext.editorIframe);
|
|
498
|
-
if (!selection || selection.rangeCount === 0 || !selection.isCollapsed) {
|
|
499
|
-
clearRemoteCaretPosition();
|
|
500
|
-
return;
|
|
501
|
-
}
|
|
502
|
-
const caret = saveCaretPosition(element);
|
|
503
|
-
if (!caret || caret.startOffset !== caret.endOffset) {
|
|
504
|
-
clearRemoteCaretPosition();
|
|
505
|
-
return;
|
|
506
|
-
}
|
|
507
|
-
const value = sanitizeEditableValue(element, element.getAttribute("data-is-richtext") === "true");
|
|
508
|
-
const nextKey = `${fieldDescriptor.fieldId}:${fieldDescriptor.item.id}:${fieldDescriptor.item.language}:${fieldDescriptor.item.version}:${caret.startOffset}:${value}`;
|
|
509
|
-
if (lastSentCaretRef.current === nextKey)
|
|
510
|
-
return;
|
|
511
|
-
context.sendSocketMessage({
|
|
512
|
-
type: "caret-position",
|
|
513
|
-
payload: {
|
|
514
|
-
fieldId: fieldDescriptor.fieldId,
|
|
515
|
-
item: fieldDescriptor.item,
|
|
516
|
-
offset: caret.startOffset,
|
|
517
|
-
value,
|
|
518
|
-
},
|
|
519
|
-
});
|
|
520
|
-
lastSentCaretRef.current = nextKey;
|
|
521
|
-
});
|
|
522
|
-
};
|
|
523
|
-
const isInlineFieldBeingEdited = (doc, element) => {
|
|
524
|
-
if (!doc || !element)
|
|
525
|
-
return false;
|
|
526
|
-
const iframe = pageViewContext.editorIframe;
|
|
527
|
-
const iframeHasFocus = iframe?.ownerDocument.activeElement === iframe || doc.hasFocus() === true;
|
|
528
|
-
return ((iframeHasFocus && isElementActiveInIframe(doc, element)) ||
|
|
529
|
-
isSelectionInsideElement(element));
|
|
530
|
-
};
|
|
531
|
-
const focusInlineElement = (element) => {
|
|
532
|
-
const iframeWindow = pageViewContext.editorIframe?.contentWindow;
|
|
533
|
-
const selection = getAccessibleIframeSelection(pageViewContext.editorIframe);
|
|
534
|
-
iframeWindow?.focus();
|
|
535
|
-
element.focus({ preventScroll: true });
|
|
536
|
-
if (!selection || isSelectionInsideElement(element))
|
|
537
|
-
return;
|
|
538
|
-
const range = element.ownerDocument.createRange();
|
|
539
|
-
range.selectNodeContents(element);
|
|
540
|
-
range.collapse(false);
|
|
541
|
-
selection.removeAllRanges();
|
|
542
|
-
selection.addRange(range);
|
|
543
|
-
};
|
|
544
|
-
function hasRemoteCaretForField({ fieldId, itemId, language, version, }) {
|
|
545
|
-
if (!fieldId || !itemId || !language || version == null)
|
|
546
|
-
return false;
|
|
547
|
-
return Object.values(contextRef.current?.remoteCarets ?? context.remoteCarets).some((caret) => caret.offset != null &&
|
|
548
|
-
caret.fieldId === fieldId &&
|
|
549
|
-
caret.item?.id === itemId &&
|
|
550
|
-
caret.item?.language === language &&
|
|
551
|
-
caret.item?.version === version);
|
|
552
|
-
}
|
|
553
|
-
// Helper: set innerHTML when content changes
|
|
554
|
-
function setInnerHTMLWithStackTrace(element, nextHTML, meta) {
|
|
555
|
-
const prevHTML = element.innerHTML ?? "";
|
|
556
|
-
if (prevHTML === nextHTML)
|
|
557
|
-
return false;
|
|
558
|
-
if (meta.source !== "remote-caret:value" &&
|
|
559
|
-
hasRemoteCaretForField({
|
|
560
|
-
fieldId: meta.fieldId ?? element.getAttribute("data-fieldid"),
|
|
561
|
-
itemId: meta.itemId ?? element.getAttribute("data-itemid"),
|
|
562
|
-
language: meta.language ?? element.getAttribute("data-language"),
|
|
563
|
-
version: meta.version ??
|
|
564
|
-
(element.getAttribute("data-version")
|
|
565
|
-
? parseInt(element.getAttribute("data-version"), 10)
|
|
566
|
-
: null),
|
|
567
|
-
})) {
|
|
568
|
-
return false;
|
|
569
|
-
}
|
|
570
|
-
if (contextRef.current?.mode === "suggestions") {
|
|
571
|
-
const iframeDocument = getAccessibleIframeDocument(pageViewContext.editorIframe);
|
|
572
|
-
const iframeHasFocus = pageViewContext.editorIframe?.ownerDocument.activeElement ===
|
|
573
|
-
pageViewContext.editorIframe || iframeDocument?.hasFocus() === true;
|
|
574
|
-
const recentlyEdited = Date.now() - lastInlineUserEditAtRef.current < 1500;
|
|
575
|
-
const isFocusedEditable = iframeHasFocus &&
|
|
576
|
-
(isElementActiveInIframe(iframeDocument, element) ||
|
|
577
|
-
isSelectionInsideElement(element));
|
|
578
|
-
const willSkipRewrite = isFocusedEditable || (element.isContentEditable && recentlyEdited);
|
|
579
|
-
if (willSkipRewrite) {
|
|
580
|
-
return false;
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
isUpdatingRef.current = true;
|
|
584
|
-
element.innerHTML = nextHTML;
|
|
585
|
-
setTimeout(() => {
|
|
586
|
-
isUpdatingRef.current = false;
|
|
587
|
-
}, 0);
|
|
588
|
-
// try {
|
|
589
|
-
// const resolvedFieldId =
|
|
590
|
-
// meta.fieldId ?? element.getAttribute("data-fieldid");
|
|
591
|
-
// const resolvedItemId = meta.itemId ?? element.getAttribute("data-itemid");
|
|
592
|
-
// const resolvedLanguage =
|
|
593
|
-
// meta.language ?? element.getAttribute("data-language");
|
|
594
|
-
// const resolvedVersion =
|
|
595
|
-
// meta.version ??
|
|
596
|
-
// (element.getAttribute("data-version")
|
|
597
|
-
// ? parseInt(element.getAttribute("data-version") as string, 10)
|
|
598
|
-
// : null);
|
|
599
|
-
// // Log concise info and stack trace
|
|
600
|
-
// // Limit previews to keep console tidy
|
|
601
|
-
// const preview = (s: string) =>
|
|
602
|
-
// s.length > 200 ? s.slice(0, 200) + "…" : s;
|
|
603
|
-
// console.groupCollapsed(
|
|
604
|
-
// `[InlineEditor] DOM field update (${meta.source}) ${nextHTML} `,
|
|
605
|
-
// );
|
|
606
|
-
// const modifiedFieldValue = modifiedFieldsContext?.modifiedFields.find(
|
|
607
|
-
// (x) =>
|
|
608
|
-
// x.fieldId === resolvedFieldId &&
|
|
609
|
-
// x.item.id === resolvedItemId &&
|
|
610
|
-
// x.item.language === resolvedLanguage &&
|
|
611
|
-
// x.item.version === resolvedVersion,
|
|
612
|
-
// )?.value;
|
|
613
|
-
// console.log({
|
|
614
|
-
// fieldId: resolvedFieldId,
|
|
615
|
-
// itemId: resolvedItemId,
|
|
616
|
-
// language: resolvedLanguage,
|
|
617
|
-
// version: resolvedVersion,
|
|
618
|
-
// prevLength: prevHTML.length,
|
|
619
|
-
// nextLength: nextHTML.length,
|
|
620
|
-
// prevPreview: preview(prevHTML),
|
|
621
|
-
// nextPreview: preview(nextHTML),
|
|
622
|
-
// modifiedFieldsContext: modifiedFieldsContext?.modifiedFields,
|
|
623
|
-
// repository: contextRef.current?.itemsRepository,
|
|
624
|
-
// modifiedFieldValue,
|
|
625
|
-
// });
|
|
626
|
-
// console.trace();
|
|
627
|
-
// console.groupEnd();
|
|
628
|
-
// } catch {
|
|
629
|
-
// // no-op logging failure
|
|
630
|
-
// }
|
|
631
|
-
return true;
|
|
632
|
-
}
|
|
633
|
-
function setEditableElementValue(element, nextValue, isRichText, meta) {
|
|
634
|
-
if (isRichText) {
|
|
635
|
-
return setInnerHTMLWithStackTrace(element, nextValue, meta);
|
|
636
|
-
}
|
|
637
|
-
if ((element.innerText ?? "") === nextValue)
|
|
638
|
-
return false;
|
|
639
|
-
isUpdatingRef.current = true;
|
|
640
|
-
element.innerText = nextValue;
|
|
641
|
-
setTimeout(() => {
|
|
642
|
-
isUpdatingRef.current = false;
|
|
643
|
-
}, 0);
|
|
644
|
-
return true;
|
|
645
|
-
}
|
|
646
|
-
function isCurrentInlineEditSource(sourceElement, key) {
|
|
647
|
-
if (!sourceElement.isConnected)
|
|
648
|
-
return false;
|
|
649
|
-
const iframeDocument = getAccessibleIframeDocument(pageViewContext.editorIframe);
|
|
650
|
-
if (iframeDocument && sourceElement.ownerDocument !== iframeDocument) {
|
|
651
|
-
return false;
|
|
652
|
-
}
|
|
653
|
-
const activePage = pageViewContext.pageItemDescriptor;
|
|
654
|
-
if (activePage?.language && activePage.language !== key.language) {
|
|
655
|
-
return false;
|
|
656
|
-
}
|
|
657
|
-
const activeElement = modifiedFieldsContext?.inlineEditingFieldElement;
|
|
658
|
-
if (activeElement && isSameFieldKey(sourceElement, key))
|
|
659
|
-
return true;
|
|
660
|
-
if (key.fromUserInput &&
|
|
661
|
-
contextRef.current?.mode === "suggestions" &&
|
|
662
|
-
sourceElement instanceof HTMLElement &&
|
|
663
|
-
sourceElement.isContentEditable &&
|
|
664
|
-
isSameFieldKey(sourceElement, key)) {
|
|
665
|
-
return true;
|
|
666
|
-
}
|
|
667
|
-
if (contextRef.current?.mode === "suggestions" &&
|
|
668
|
-
sourceElement instanceof HTMLElement &&
|
|
669
|
-
sourceElement.isContentEditable &&
|
|
670
|
-
isSameFieldKey(sourceElement, key) &&
|
|
671
|
-
isInlineFieldBeingEdited(iframeDocument, sourceElement)) {
|
|
672
|
-
return true;
|
|
673
|
-
}
|
|
674
|
-
if (!activeElement || !isSameFieldKey(sourceElement, key))
|
|
675
|
-
return false;
|
|
676
|
-
return true;
|
|
677
|
-
}
|
|
678
|
-
function queueInlineFieldValue({ value, fieldId, fieldName, itemId, language, version, sourceElement, fromUserInput = false, source = "unknown", }) {
|
|
679
|
-
const isCurrentSource = isCurrentInlineEditSource(sourceElement, {
|
|
680
|
-
fieldId,
|
|
681
|
-
itemId,
|
|
682
|
-
language,
|
|
683
|
-
version,
|
|
684
|
-
fromUserInput,
|
|
685
|
-
});
|
|
686
|
-
if (!isCurrentSource) {
|
|
687
|
-
return;
|
|
688
|
-
}
|
|
689
|
-
const fieldValueKey = `${itemId}:${language}:${version}:${fieldId}`;
|
|
690
|
-
if (lastQueuedInlineValueRef.current.get(fieldValueKey) === value)
|
|
691
|
-
return;
|
|
692
|
-
lastQueuedInlineValueRef.current.set(fieldValueKey, value);
|
|
693
|
-
lastInlineUserEditAtRef.current = Date.now();
|
|
694
|
-
contextRef.current?.operations.editField({
|
|
695
|
-
field: {
|
|
696
|
-
fieldId,
|
|
697
|
-
fieldName: fieldName ?? undefined,
|
|
698
|
-
item: {
|
|
699
|
-
id: itemId,
|
|
700
|
-
language,
|
|
701
|
-
version,
|
|
702
|
-
},
|
|
703
|
-
},
|
|
704
|
-
forceMode: contextRef.current?.mode === "suggestions" ||
|
|
705
|
-
context.mode === "suggestions"
|
|
706
|
-
? "suggestions"
|
|
707
|
-
: undefined,
|
|
708
|
-
originatingSlotId: slotContext?.slotId,
|
|
709
|
-
refresh: "none",
|
|
710
|
-
value,
|
|
711
|
-
});
|
|
712
|
-
}
|
|
713
|
-
const sanitizeEditableValue = (element, isRichText) => {
|
|
714
|
-
let currentValue = isRichText ? element.innerHTML : element.innerText;
|
|
715
|
-
if (isRichText) {
|
|
716
|
-
const clone = element.cloneNode(true);
|
|
717
|
-
const cursorElem = clone.querySelector(`#${cursorSpanId}`);
|
|
718
|
-
if (cursorElem)
|
|
719
|
-
cursorElem.parentNode?.removeChild(cursorElem);
|
|
720
|
-
const ownerDoc = clone.ownerDocument || document;
|
|
721
|
-
const walker = ownerDoc.createTreeWalker(clone, NodeFilter.SHOW_TEXT);
|
|
722
|
-
const toClean = [];
|
|
723
|
-
while (walker.nextNode()) {
|
|
724
|
-
const tn = walker.currentNode;
|
|
725
|
-
if (tn.nodeValue && tn.nodeValue.includes("\u200B")) {
|
|
726
|
-
toClean.push(tn);
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
toClean.forEach((tn) => (tn.nodeValue = tn.nodeValue?.replaceAll("\u200B", "") || ""));
|
|
730
|
-
currentValue = clone.innerHTML;
|
|
731
|
-
}
|
|
732
|
-
else {
|
|
733
|
-
currentValue = currentValue.replaceAll("\u200B", "");
|
|
734
|
-
}
|
|
735
|
-
return currentValue;
|
|
736
|
-
};
|
|
737
|
-
useEffect(() => {
|
|
738
|
-
const editorIframe = pageViewContext.editorIframe;
|
|
739
|
-
if (compareView || !editorIframe)
|
|
740
|
-
return;
|
|
741
|
-
context.activeSessions.forEach((session) => {
|
|
742
|
-
if (session.sessionId === context.sessionId || !session.fieldLock)
|
|
743
|
-
return;
|
|
744
|
-
const remoteCaret = context.remoteCarets[session.sessionId];
|
|
745
|
-
if (remoteCaret?.value == null)
|
|
746
|
-
return;
|
|
747
|
-
const caretMatchesLock = remoteCaret.fieldId === session.fieldLock.fieldId &&
|
|
748
|
-
remoteCaret.item?.id === session.fieldLock.item.id &&
|
|
749
|
-
remoteCaret.item?.language === session.fieldLock.item.language &&
|
|
750
|
-
remoteCaret.item?.version === session.fieldLock.item.version;
|
|
751
|
-
if (!caretMatchesLock)
|
|
752
|
-
return;
|
|
753
|
-
const element = findFieldElement(editorIframe, session.fieldLock);
|
|
754
|
-
if (!element || element === modifiedFieldsContext?.inlineEditingFieldElement)
|
|
755
|
-
return;
|
|
756
|
-
setEditableElementValue(element, remoteCaret.value, element.getAttribute("data-is-richtext") === "true", {
|
|
757
|
-
source: "remote-caret:value",
|
|
758
|
-
fieldId: session.fieldLock.fieldId,
|
|
759
|
-
itemId: session.fieldLock.item.id,
|
|
760
|
-
language: session.fieldLock.item.language,
|
|
761
|
-
version: session.fieldLock.item.version,
|
|
762
|
-
});
|
|
763
|
-
});
|
|
764
|
-
}, [
|
|
765
|
-
compareView,
|
|
766
|
-
context.activeSessions,
|
|
767
|
-
context.remoteCarets,
|
|
768
|
-
context.sessionId,
|
|
769
|
-
modifiedFieldsContext?.inlineEditingFieldElement,
|
|
770
|
-
pageViewContext.editorIframe,
|
|
771
|
-
]);
|
|
772
|
-
useEffect(() => {
|
|
773
|
-
if (compareView || context.mode === "preview")
|
|
774
|
-
return;
|
|
775
|
-
const queueElementValue = (element, fromUserInput = false) => {
|
|
776
|
-
if (contextRef.current?.mode !== "suggestions" &&
|
|
777
|
-
context.mode !== "suggestions") {
|
|
778
|
-
return;
|
|
779
|
-
}
|
|
780
|
-
if (isUpdatingRef.current)
|
|
781
|
-
return;
|
|
782
|
-
if (!element?.isContentEditable)
|
|
783
|
-
return;
|
|
784
|
-
const fieldId = element.getAttribute("data-fieldid");
|
|
785
|
-
const fieldName = element.getAttribute("data-fieldname");
|
|
786
|
-
const itemId = element.getAttribute("data-itemid");
|
|
787
|
-
const language = element.getAttribute("data-language");
|
|
788
|
-
const versionText = element.getAttribute("data-version");
|
|
789
|
-
const version = versionText ? parseInt(versionText) : undefined;
|
|
790
|
-
if (!fieldId || !itemId || !language || !version)
|
|
791
|
-
return;
|
|
792
|
-
queueInlineFieldValue({
|
|
793
|
-
value: sanitizeEditableValue(element, element.getAttribute("data-is-richtext") === "true"),
|
|
794
|
-
fieldId,
|
|
795
|
-
fieldName,
|
|
796
|
-
itemId,
|
|
797
|
-
language,
|
|
798
|
-
version,
|
|
799
|
-
sourceElement: element,
|
|
800
|
-
fromUserInput,
|
|
801
|
-
});
|
|
802
|
-
};
|
|
803
|
-
const getFieldElement = (target) => {
|
|
804
|
-
const element = target instanceof Element ? target : target?.parentElement;
|
|
805
|
-
return element?.closest?.("[data-fieldid][data-itemid][data-language][data-version]");
|
|
806
|
-
};
|
|
807
|
-
const inputHandler = (event) => {
|
|
808
|
-
queueElementValue(getFieldElement(event.target), true);
|
|
809
|
-
};
|
|
810
|
-
let detachDocumentInput = null;
|
|
811
|
-
const attachToCurrentIframeDocument = () => {
|
|
812
|
-
detachDocumentInput?.();
|
|
813
|
-
const iframeDocument = getAccessibleIframeDocument(pageViewContext.editorIframe);
|
|
814
|
-
if (!iframeDocument) {
|
|
815
|
-
detachDocumentInput = null;
|
|
816
|
-
return;
|
|
817
|
-
}
|
|
818
|
-
iframeDocument.addEventListener("input", inputHandler, true);
|
|
819
|
-
const observer = new MutationObserver((mutations) => {
|
|
820
|
-
const queuedElements = new Set();
|
|
821
|
-
for (const mutation of mutations) {
|
|
822
|
-
const fieldElement = getFieldElement(mutation.target);
|
|
823
|
-
if (fieldElement)
|
|
824
|
-
queuedElements.add(fieldElement);
|
|
825
|
-
mutation.addedNodes.forEach((node) => {
|
|
826
|
-
const addedFieldElement = getFieldElement(node);
|
|
827
|
-
if (addedFieldElement)
|
|
828
|
-
queuedElements.add(addedFieldElement);
|
|
829
|
-
});
|
|
830
|
-
}
|
|
831
|
-
queuedElements.forEach((element) => queueElementValue(element));
|
|
832
|
-
});
|
|
833
|
-
observer.observe(iframeDocument.body || iframeDocument.documentElement, {
|
|
834
|
-
childList: true,
|
|
835
|
-
characterData: true,
|
|
836
|
-
subtree: true,
|
|
837
|
-
});
|
|
838
|
-
const pollFocusedField = () => {
|
|
839
|
-
if (contextRef.current?.mode !== "suggestions" &&
|
|
840
|
-
context.mode !== "suggestions") {
|
|
841
|
-
return;
|
|
842
|
-
}
|
|
843
|
-
const activeField = iframeDocument.activeElement?.closest?.("[data-fieldid][data-itemid][data-language][data-version]");
|
|
844
|
-
if (activeField) {
|
|
845
|
-
queueElementValue(activeField, true);
|
|
846
|
-
return;
|
|
847
|
-
}
|
|
848
|
-
const selection = iframeDocument.defaultView?.getSelection();
|
|
849
|
-
const selectionElement = selection?.anchorNode instanceof Element
|
|
850
|
-
? selection.anchorNode
|
|
851
|
-
: selection?.anchorNode?.parentElement;
|
|
852
|
-
const selectedField = selectionElement?.closest?.("[data-fieldid][data-itemid][data-language][data-version]");
|
|
853
|
-
if (selectedField) {
|
|
854
|
-
queueElementValue(selectedField, true);
|
|
855
|
-
return;
|
|
856
|
-
}
|
|
857
|
-
iframeDocument
|
|
858
|
-
.querySelectorAll('[contenteditable="true"][data-fieldid][data-itemid][data-language][data-version]')
|
|
859
|
-
.forEach((fieldElement) => queueElementValue(fieldElement, true));
|
|
860
|
-
};
|
|
861
|
-
const pollIntervalId = iframeDocument.defaultView?.setInterval(pollFocusedField, 250);
|
|
862
|
-
detachDocumentInput = () => {
|
|
863
|
-
iframeDocument.removeEventListener("input", inputHandler, true);
|
|
864
|
-
observer.disconnect();
|
|
865
|
-
if (pollIntervalId !== undefined) {
|
|
866
|
-
iframeDocument.defaultView?.clearInterval(pollIntervalId);
|
|
867
|
-
}
|
|
868
|
-
};
|
|
869
|
-
};
|
|
870
|
-
attachToCurrentIframeDocument();
|
|
871
|
-
pageViewContext.editorIframe?.addEventListener("load", attachToCurrentIframeDocument);
|
|
872
|
-
return () => {
|
|
873
|
-
pageViewContext.editorIframe?.removeEventListener("load", attachToCurrentIframeDocument);
|
|
874
|
-
detachDocumentInput?.();
|
|
875
|
-
};
|
|
876
|
-
}, [compareView, context.mode, pageViewContext.editorIframe]);
|
|
877
|
-
useEffect(() => {
|
|
878
|
-
if (!context || compareView || context.mode === "preview")
|
|
879
|
-
return;
|
|
880
|
-
const iframeDocument = getAccessibleIframeDocument(pageViewContext.editorIframe);
|
|
881
|
-
let element = modifiedFieldsContext?.inlineEditingFieldElement;
|
|
882
|
-
if (!element && context.mode === "suggestions" && iframeDocument) {
|
|
883
|
-
const iframeHasFocus = pageViewContext.editorIframe?.ownerDocument.activeElement ===
|
|
884
|
-
pageViewContext.editorIframe || iframeDocument.hasFocus() === true;
|
|
885
|
-
if (iframeHasFocus) {
|
|
886
|
-
const activeFieldElement = iframeDocument.activeElement?.closest?.("[data-fieldid][data-itemid][data-language][data-version]") ?? null;
|
|
887
|
-
const selection = getAccessibleIframeSelection(pageViewContext.editorIframe);
|
|
888
|
-
const selectionElement = selection?.anchorNode instanceof Element
|
|
889
|
-
? selection.anchorNode
|
|
890
|
-
: selection?.anchorNode?.parentElement;
|
|
891
|
-
const selectedFieldElement = selectionElement?.closest?.("[data-fieldid][data-itemid][data-language][data-version]") ?? null;
|
|
892
|
-
const recoveredElement = (activeFieldElement ??
|
|
893
|
-
selectedFieldElement);
|
|
894
|
-
if (recoveredElement?.isConnected) {
|
|
895
|
-
element = recoveredElement;
|
|
896
|
-
modifiedFieldsContext?.setInlineEditingFieldElement(recoveredElement);
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
const editableElements = iframeDocument?.querySelectorAll('[contenteditable="true"]');
|
|
901
|
-
if (element) {
|
|
902
|
-
editableElements?.forEach((editableElement) => {
|
|
903
|
-
if (editableElement !== element) {
|
|
904
|
-
editableElement.setAttribute("contenteditable", "false");
|
|
905
|
-
}
|
|
906
|
-
});
|
|
907
|
-
}
|
|
908
|
-
if (element) {
|
|
909
|
-
lastQueuedInlineValueRef.current.clear();
|
|
910
|
-
// Clean up any existing cursor indicators
|
|
911
|
-
const iframeDocument = getAccessibleIframeDocument(pageViewContext.editorIframe);
|
|
912
|
-
const existingCursors = iframeDocument?.querySelectorAll('[data-cursor-indicator="true"]');
|
|
913
|
-
existingCursors?.forEach((el) => el.parentNode?.removeChild(el));
|
|
914
|
-
const fieldId = element.getAttribute("data-fieldid");
|
|
915
|
-
const fieldName = element.getAttribute("data-fieldname");
|
|
916
|
-
const itemId = element.getAttribute("data-itemid");
|
|
917
|
-
const language = element.getAttribute("data-language");
|
|
918
|
-
const versionText = element.getAttribute("data-version");
|
|
919
|
-
const version = versionText ? parseInt(versionText) : undefined;
|
|
920
|
-
const isRichText = element.getAttribute("data-is-richtext") === "true";
|
|
921
|
-
if (!fieldId || !itemId || !language || !version)
|
|
922
|
-
return;
|
|
923
|
-
// Guard: if layout components are hidden, do not allow editing fields of layout components
|
|
924
|
-
if (contextRef.current?.showLayoutComponents === false &&
|
|
925
|
-
contextRef.current?.page) {
|
|
926
|
-
const owner = getComponentById(itemId, contextRef.current.page);
|
|
927
|
-
if (owner?.layoutId)
|
|
928
|
-
return;
|
|
929
|
-
}
|
|
930
|
-
const fieldDescriptor = getFieldDescriptorFromElement(element);
|
|
931
|
-
// Check if another session has this field locked
|
|
932
|
-
const fieldLockedBySession = getSessionWithFieldLock(fieldDescriptor, context);
|
|
933
|
-
const isLockedByAnotherUser = fieldLockedBySession &&
|
|
934
|
-
fieldLockedBySession.sessionId !== context.sessionId;
|
|
935
|
-
// If another user has the lock, don't allow editing
|
|
936
|
-
if (isLockedByAnotherUser)
|
|
937
|
-
return;
|
|
938
|
-
const hasFielLock = context.mode === "suggestions" ||
|
|
939
|
-
hasFieldLock(fieldDescriptor, context);
|
|
940
|
-
if (!hasFielLock)
|
|
941
|
-
return;
|
|
942
|
-
element.setAttribute("contenteditable", "true");
|
|
943
|
-
const iframeWindow = pageViewContext.editorIframe?.contentWindow;
|
|
944
|
-
const sanitizeCurrentValue = () => sanitizeEditableValue(element, isRichText);
|
|
945
|
-
const inputHandler = () => {
|
|
946
|
-
const currentValue = sanitizeCurrentValue();
|
|
947
|
-
queueInlineFieldValue({
|
|
948
|
-
value: currentValue,
|
|
949
|
-
fieldId,
|
|
950
|
-
fieldName,
|
|
951
|
-
itemId,
|
|
952
|
-
language,
|
|
953
|
-
version,
|
|
954
|
-
sourceElement: element,
|
|
955
|
-
fromUserInput: true,
|
|
956
|
-
source: "input",
|
|
957
|
-
});
|
|
958
|
-
scheduleCaretPositionUpdate(element, fieldDescriptor);
|
|
959
|
-
};
|
|
960
|
-
element.addEventListener("input", inputHandler);
|
|
961
|
-
// Setup a more robust mutation observer
|
|
962
|
-
mutationObserverRef.current = new MutationObserver((mutations) => {
|
|
963
|
-
// If we're already processing an update, don't trigger another one
|
|
964
|
-
if (isUpdatingRef.current)
|
|
965
|
-
return;
|
|
966
|
-
// Check if mutations only affect our cursor span
|
|
967
|
-
const hasRealChanges = mutations.some((mutation) => {
|
|
968
|
-
// Skip our cursor span
|
|
969
|
-
if (mutation.target.nodeType === Node.ELEMENT_NODE &&
|
|
970
|
-
mutation.target.id === cursorSpanId) {
|
|
971
|
-
console.log("skipping cursor span");
|
|
972
|
-
return false;
|
|
973
|
-
}
|
|
974
|
-
// Skip mutations where target is our span
|
|
975
|
-
if (mutation.target.nodeType === Node.ELEMENT_NODE &&
|
|
976
|
-
mutation.target.getAttribute &&
|
|
977
|
-
mutation.target.getAttribute("data-cursor-indicator") === "true") {
|
|
978
|
-
console.log("skipping cursor span 2");
|
|
979
|
-
return false;
|
|
980
|
-
}
|
|
981
|
-
// Skip if all added/removed nodes are just our cursor span
|
|
982
|
-
if (mutation.type === "childList") {
|
|
983
|
-
const nonCursorChanges = Array.from(mutation.addedNodes)
|
|
984
|
-
.concat(Array.from(mutation.removedNodes))
|
|
985
|
-
.filter((node) => {
|
|
986
|
-
return (node.nodeType !== Node.ELEMENT_NODE ||
|
|
987
|
-
!node.id ||
|
|
988
|
-
node.id !== cursorSpanId);
|
|
989
|
-
});
|
|
990
|
-
return nonCursorChanges.length > 0;
|
|
991
|
-
}
|
|
992
|
-
return true;
|
|
993
|
-
});
|
|
994
|
-
if (hasRealChanges) {
|
|
995
|
-
// Capture and sanitize the value at mutation time
|
|
996
|
-
const capturedValue = sanitizeCurrentValue();
|
|
997
|
-
queueInlineFieldValue({
|
|
998
|
-
value: capturedValue,
|
|
999
|
-
fieldId,
|
|
1000
|
-
fieldName,
|
|
1001
|
-
itemId,
|
|
1002
|
-
language,
|
|
1003
|
-
version,
|
|
1004
|
-
sourceElement: element,
|
|
1005
|
-
source: "mutation",
|
|
1006
|
-
});
|
|
1007
|
-
}
|
|
1008
|
-
// Always update cursor position after any mutation,
|
|
1009
|
-
// but delay to let the mutation processing complete
|
|
1010
|
-
// if (!isUpdatingRef.current) {
|
|
1011
|
-
// setTimeout(updateCursorSpan, 0);
|
|
1012
|
-
// // updateCompletion();
|
|
1013
|
-
// }
|
|
1014
|
-
});
|
|
1015
|
-
const focusIfNeeded = () => {
|
|
1016
|
-
// Only focus if no other important element has focus (like the AI dialog)
|
|
1017
|
-
const activeEl = document.activeElement;
|
|
1018
|
-
const isInDialog = activeEl?.closest(".agent-inline-dialog") ||
|
|
1019
|
-
activeEl?.closest('[role="dialog"]');
|
|
1020
|
-
const iframeDocument = getAccessibleIframeDocument(pageViewContext.editorIframe);
|
|
1021
|
-
const alreadyFocused = isElementActiveInIframe(iframeDocument, element);
|
|
1022
|
-
if (!isInDialog && !alreadyFocused) {
|
|
1023
|
-
focusInlineElement(element);
|
|
1024
|
-
}
|
|
1025
|
-
};
|
|
1026
|
-
focusIfNeeded();
|
|
1027
|
-
setTimeout(focusIfNeeded, 50);
|
|
1028
|
-
scheduleCaretPositionUpdate(element, fieldDescriptor);
|
|
1029
|
-
// Initial cursor placement
|
|
1030
|
-
// setTimeout(updateCursorSpan, 10);
|
|
1031
|
-
// Add listener for selection changes
|
|
1032
|
-
if (iframeWindow) {
|
|
1033
|
-
const selectionChangeHandler = () => {
|
|
1034
|
-
scheduleCaretPositionUpdate(element, fieldDescriptor);
|
|
1035
|
-
};
|
|
1036
|
-
const keyOrMouseHandler = () => {
|
|
1037
|
-
scheduleCaretPositionUpdate(element, fieldDescriptor);
|
|
1038
|
-
};
|
|
1039
|
-
const blurHandler = () => {
|
|
1040
|
-
clearRemoteCaretPosition();
|
|
1041
|
-
};
|
|
1042
|
-
iframeWindow.document.addEventListener("selectionchange", selectionChangeHandler);
|
|
1043
|
-
iframeWindow.document.addEventListener("keyup", keyOrMouseHandler);
|
|
1044
|
-
iframeWindow.document.addEventListener("mouseup", keyOrMouseHandler);
|
|
1045
|
-
element.addEventListener("blur", blurHandler);
|
|
1046
|
-
selectionChangeListenerRef.current = () => {
|
|
1047
|
-
iframeWindow.document.removeEventListener("selectionchange", selectionChangeHandler);
|
|
1048
|
-
iframeWindow.document.removeEventListener("keyup", keyOrMouseHandler);
|
|
1049
|
-
iframeWindow.document.removeEventListener("mouseup", keyOrMouseHandler);
|
|
1050
|
-
element.removeEventListener("blur", blurHandler);
|
|
1051
|
-
};
|
|
1052
|
-
mutationObserverRef.current.observe(element, {
|
|
1053
|
-
childList: true, // observe direct children changes
|
|
1054
|
-
subtree: true, // observe all descendants changes
|
|
1055
|
-
characterData: true, // observe text changes
|
|
1056
|
-
});
|
|
1057
|
-
// Cleanup
|
|
1058
|
-
return () => {
|
|
1059
|
-
element.removeEventListener("input", inputHandler);
|
|
1060
|
-
if (mutationObserverRef.current) {
|
|
1061
|
-
mutationObserverRef.current.disconnect();
|
|
1062
|
-
mutationObserverRef.current = null;
|
|
1063
|
-
}
|
|
1064
|
-
if (selectionChangeListenerRef.current) {
|
|
1065
|
-
selectionChangeListenerRef.current();
|
|
1066
|
-
selectionChangeListenerRef.current = null;
|
|
1067
|
-
}
|
|
1068
|
-
// iframeWindow.document.removeEventListener("keydown", keyHandler);
|
|
1069
|
-
// Clean up any cursor indicators on unmount
|
|
1070
|
-
const cursorElement = iframeDocument?.getElementById(cursorSpanId);
|
|
1071
|
-
if (cursorElement) {
|
|
1072
|
-
cursorElement.parentNode?.removeChild(cursorElement);
|
|
1073
|
-
}
|
|
1074
|
-
clearRemoteCaretPosition();
|
|
1075
|
-
};
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
async function updateFocusedFieldContent() {
|
|
1079
|
-
const element = modifiedFieldsContext?.inlineEditingFieldElement;
|
|
1080
|
-
if (!element)
|
|
1081
|
-
return;
|
|
1082
|
-
const iframeDocument = getAccessibleIframeDocument(pageViewContext.editorIframe);
|
|
1083
|
-
if (isElementActiveOrSelectedInIframe(iframeDocument, element))
|
|
1084
|
-
return;
|
|
1085
|
-
if (Date.now() - lastInlineUserEditAtRef.current < 1000)
|
|
1086
|
-
return;
|
|
1087
|
-
const savedPosition = saveCaretPosition(element);
|
|
1088
|
-
let contentWasUpdated = false;
|
|
1089
|
-
const fieldId = element.getAttribute("data-fieldid");
|
|
1090
|
-
const itemId = element.getAttribute("data-itemid");
|
|
1091
|
-
const language = element.getAttribute("data-language");
|
|
1092
|
-
const versionStr = element.getAttribute("data-version");
|
|
1093
|
-
if (!fieldId || !itemId || !language || !versionStr)
|
|
1094
|
-
return;
|
|
1095
|
-
const version = parseInt(versionStr, 10);
|
|
1096
|
-
const descriptor = { id: itemId, language, version };
|
|
1097
|
-
// Retrieve the current field value from the repository.
|
|
1098
|
-
const loadedItem = await contextRef.current?.itemsRepository.getItem(descriptor);
|
|
1099
|
-
if (!loadedItem)
|
|
1100
|
-
return;
|
|
1101
|
-
// Get the baseline value from the repository.
|
|
1102
|
-
const repositoryField = loadedItem.fields.find((f) => f.id === fieldId);
|
|
1103
|
-
let baseValue = repositoryField ? repositoryField.rawValue || "" : "";
|
|
1104
|
-
// Override with the modified field value if it exists.
|
|
1105
|
-
const modField = modifiedFieldsContext?.modifiedFields.find((mod) => mod.fieldId === fieldId &&
|
|
1106
|
-
mod.item.id === itemId &&
|
|
1107
|
-
mod.item.language === language &&
|
|
1108
|
-
mod.item.version === version);
|
|
1109
|
-
if (modField) {
|
|
1110
|
-
baseValue = modField.value || "";
|
|
1111
|
-
}
|
|
1112
|
-
// If suggestions mode is active, merge all suggestions for this field.
|
|
1113
|
-
if (contextRef.current?.mode === "suggestions") {
|
|
1114
|
-
const pageVer = pageViewContext.pageItemDescriptor?.version;
|
|
1115
|
-
const fieldSuggestions = (contextRef.current?.suggestedEdits ?? []).filter((s) => s.fieldId === fieldId &&
|
|
1116
|
-
s.itemId === itemId &&
|
|
1117
|
-
s.mainItemLanguage === language &&
|
|
1118
|
-
(pageVer != null
|
|
1119
|
-
? s.mainItemVersion === pageVer || s.mainItemVersion === 0
|
|
1120
|
-
: s.mainItemVersion === version));
|
|
1121
|
-
// Sort suggestions in chronological order (oldest first).
|
|
1122
|
-
fieldSuggestions.sort((a, b) => new Date(a.created).getTime() - new Date(b.created).getTime());
|
|
1123
|
-
let mergedValue = baseValue;
|
|
1124
|
-
for (const suggestion of fieldSuggestions) {
|
|
1125
|
-
const patch = createPatch("field", suggestion.oldValue, suggestion.newValue);
|
|
1126
|
-
const patchedCandidate = applyPatch(mergedValue, patch);
|
|
1127
|
-
if (patchedCandidate !== false &&
|
|
1128
|
-
typeof patchedCandidate === "string") {
|
|
1129
|
-
mergedValue = patchedCandidate;
|
|
1130
|
-
}
|
|
1131
|
-
else {
|
|
1132
|
-
mergedValue = suggestion.newValue;
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
1135
|
-
// Update focused field element with the merged plain text value.
|
|
1136
|
-
setInnerHTMLWithStackTrace(element, mergedValue, {
|
|
1137
|
-
source: "updateFocusedFieldContent:mergedValue",
|
|
1138
|
-
fieldId,
|
|
1139
|
-
itemId,
|
|
1140
|
-
language,
|
|
1141
|
-
version,
|
|
1142
|
-
});
|
|
1143
|
-
contentWasUpdated = true;
|
|
1144
|
-
}
|
|
1145
|
-
else {
|
|
1146
|
-
if (element.innerHTML !== baseValue) {
|
|
1147
|
-
// If suggestions mode is off, update with the base value.
|
|
1148
|
-
setInnerHTMLWithStackTrace(element, baseValue, {
|
|
1149
|
-
source: "updateFocusedFieldContent:baseValue",
|
|
1150
|
-
fieldId,
|
|
1151
|
-
itemId,
|
|
1152
|
-
language,
|
|
1153
|
-
version,
|
|
1154
|
-
});
|
|
1155
|
-
contentWasUpdated = true;
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
if (contentWasUpdated) {
|
|
1159
|
-
setTimeout(() => {
|
|
1160
|
-
if (savedPosition)
|
|
1161
|
-
restoreCaretPosition(element, savedPosition);
|
|
1162
|
-
}, 0);
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
// Avoid rewriting the focused editable DOM while the user is typing in normal edit mode.
|
|
1166
|
-
// This prevents lost/reordered characters on slow machines.
|
|
1167
|
-
if (contextRef.current?.mode === "suggestions" ||
|
|
1168
|
-
contextRef.current?.showSuggestedEdits) {
|
|
1169
|
-
setTimeout(() => {
|
|
1170
|
-
updateFocusedFieldContent();
|
|
1171
|
-
}, 50);
|
|
1172
|
-
}
|
|
1173
|
-
return () => {
|
|
1174
|
-
if (mutationObserverRef.current) {
|
|
1175
|
-
mutationObserverRef.current.disconnect();
|
|
1176
|
-
mutationObserverRef.current = null;
|
|
1177
|
-
}
|
|
1178
|
-
if (selectionChangeListenerRef.current) {
|
|
1179
|
-
selectionChangeListenerRef.current();
|
|
1180
|
-
selectionChangeListenerRef.current = null;
|
|
1181
|
-
}
|
|
1182
|
-
// Clean up any cursor indicators on unmount
|
|
1183
|
-
const iframeDocument = getAccessibleIframeDocument(pageViewContext.editorIframe);
|
|
1184
|
-
const cursorElement = iframeDocument?.getElementById(cursorSpanId);
|
|
1185
|
-
if (cursorElement) {
|
|
1186
|
-
cursorElement.parentNode?.removeChild(cursorElement);
|
|
1187
|
-
}
|
|
1188
|
-
clearRemoteCaretPosition();
|
|
1189
|
-
};
|
|
1190
|
-
}, [context.mode, modifiedFieldsContext?.inlineEditingFieldElement]);
|
|
1191
|
-
function saveCaretPosition(editableElement) {
|
|
1192
|
-
const selection = getAccessibleIframeSelection(pageViewContext.editorIframe);
|
|
1193
|
-
if (!selection ||
|
|
1194
|
-
selection.rangeCount === 0 ||
|
|
1195
|
-
!editableElement.contains(selection.anchorNode)) {
|
|
1196
|
-
return null;
|
|
1197
|
-
}
|
|
1198
|
-
// Get the current range
|
|
1199
|
-
const range = selection.getRangeAt(0);
|
|
1200
|
-
const getGlobalOffset = (node, offset) => {
|
|
1201
|
-
let absoluteOffset = 0;
|
|
1202
|
-
function walk(current) {
|
|
1203
|
-
if (current === node) {
|
|
1204
|
-
absoluteOffset += offset;
|
|
1205
|
-
return true;
|
|
1206
|
-
}
|
|
1207
|
-
if (current.nodeType === Node.TEXT_NODE) {
|
|
1208
|
-
absoluteOffset += current.length;
|
|
1209
|
-
}
|
|
1210
|
-
else {
|
|
1211
|
-
for (let i = 0; i < current.childNodes.length; i++) {
|
|
1212
|
-
if (walk(current.childNodes[i]))
|
|
1213
|
-
return true;
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
return false;
|
|
1217
|
-
}
|
|
1218
|
-
walk(editableElement);
|
|
1219
|
-
return absoluteOffset;
|
|
1220
|
-
};
|
|
1221
|
-
return {
|
|
1222
|
-
startOffset: getGlobalOffset(range.startContainer, range.startOffset),
|
|
1223
|
-
endOffset: getGlobalOffset(range.endContainer, range.endOffset),
|
|
1224
|
-
};
|
|
1225
|
-
}
|
|
1226
|
-
function restoreCaretPosition(element, position) {
|
|
1227
|
-
const offsets = typeof position === "number"
|
|
1228
|
-
? { startOffset: position, endOffset: position }
|
|
1229
|
-
: position;
|
|
1230
|
-
if (!offsets)
|
|
1231
|
-
return;
|
|
1232
|
-
const selection = getAccessibleIframeSelection(pageViewContext.editorIframe);
|
|
1233
|
-
if (!selection)
|
|
1234
|
-
return;
|
|
1235
|
-
selection.removeAllRanges();
|
|
1236
|
-
function findNodeAndOffset(targetPos) {
|
|
1237
|
-
let currentPos = 0;
|
|
1238
|
-
function walk(node) {
|
|
1239
|
-
if (node.nodeType === Node.TEXT_NODE) {
|
|
1240
|
-
const length = node.length;
|
|
1241
|
-
if (currentPos + length >= targetPos) {
|
|
1242
|
-
return { node, offset: targetPos - currentPos };
|
|
1243
|
-
}
|
|
1244
|
-
currentPos += length;
|
|
1245
|
-
}
|
|
1246
|
-
else {
|
|
1247
|
-
for (let i = 0; i < node.childNodes.length; i++) {
|
|
1248
|
-
const res = walk(node.childNodes[i]);
|
|
1249
|
-
if (res)
|
|
1250
|
-
return res;
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
|
-
return null;
|
|
1254
|
-
}
|
|
1255
|
-
return walk(element);
|
|
1256
|
-
}
|
|
1257
|
-
const start = findNodeAndOffset(offsets.startOffset);
|
|
1258
|
-
const end = findNodeAndOffset(offsets.endOffset);
|
|
1259
|
-
if (start && end) {
|
|
1260
|
-
const range = new Range();
|
|
1261
|
-
range.setStart(start.node, start.offset);
|
|
1262
|
-
range.setEnd(end.node, end.offset);
|
|
1263
|
-
selection.addRange(range);
|
|
1264
|
-
}
|
|
1265
|
-
else if (element.firstChild) {
|
|
1266
|
-
// Fallback to end of content
|
|
1267
|
-
const range = new Range();
|
|
1268
|
-
range.selectNodeContents(element);
|
|
1269
|
-
range.collapse(false);
|
|
1270
|
-
selection.addRange(range);
|
|
1271
|
-
}
|
|
1272
|
-
}
|
|
1273
|
-
useEffect(() => {
|
|
1274
|
-
const updateFieldsWithSuggestions = async () => {
|
|
1275
|
-
const iframeWindow = pageViewContext.editorIframe?.contentWindow;
|
|
1276
|
-
if (!iframeWindow)
|
|
1277
|
-
return;
|
|
1278
|
-
const doc = iframeWindow.document;
|
|
1279
|
-
// First, process modified fields directly (from updateFields functionality)
|
|
1280
|
-
// IMPORTANT: Only update fields that belong to this page's language/version context.
|
|
1281
|
-
// There can be multiple InlineEditor instances (main + comparison), and each should only
|
|
1282
|
-
// update its own iframe's fields to avoid cross-contamination.
|
|
1283
|
-
const currentPageLang = pageViewContext.pageItemDescriptor?.language;
|
|
1284
|
-
const currentPageVer = pageViewContext.pageItemDescriptor?.version;
|
|
1285
|
-
for (const modField of modifiedFieldsContext?.modifiedFields ?? []) {
|
|
1286
|
-
// Skip fields that don't belong to this page's context
|
|
1287
|
-
if (currentPageLang && modField.item.language !== currentPageLang)
|
|
1288
|
-
continue;
|
|
1289
|
-
if (currentPageVer && modField.item.version !== currentPageVer)
|
|
1290
|
-
continue;
|
|
1291
|
-
// In SXA markup, the same field can appear multiple times for a component.
|
|
1292
|
-
// Update all matching nodes and only skip the exact node currently edited inline.
|
|
1293
|
-
const allMatchingElements = doc.querySelectorAll(`[data-fieldid="${modField.fieldId}"][data-itemid="${modField.item.id}"]`);
|
|
1294
|
-
const inlineEditingElement = modifiedFieldsContext?.inlineEditingFieldElement ?? null;
|
|
1295
|
-
const isInlineEditActive = inlineEditingElement &&
|
|
1296
|
-
isElementActiveOrSelectedInIframe(doc, inlineEditingElement);
|
|
1297
|
-
const targetElements = Array.from(allMatchingElements).filter((candidate) => !isInlineEditActive || candidate !== inlineEditingElement);
|
|
1298
|
-
if (targetElements.length > 0) {
|
|
1299
|
-
const realField = await context.itemsRepository.getField({
|
|
1300
|
-
fieldId: modField.fieldId,
|
|
1301
|
-
item: modField.item,
|
|
1302
|
-
});
|
|
1303
|
-
const fieldType = realField?.type;
|
|
1304
|
-
if (fieldType && isTextFieldType(fieldType)) {
|
|
1305
|
-
const next = modField.value ? modField.value : "";
|
|
1306
|
-
let wroteAtLeastOne = false;
|
|
1307
|
-
for (const targetElement of targetElements) {
|
|
1308
|
-
const currentHTML = targetElement.innerHTML;
|
|
1309
|
-
if (currentHTML === next)
|
|
1310
|
-
continue;
|
|
1311
|
-
wroteAtLeastOne = true;
|
|
1312
|
-
// Suppress MutationObserver to prevent feedback loop where server-driven
|
|
1313
|
-
// updates get re-sent to the backend as new operations
|
|
1314
|
-
isUpdatingRef.current = true;
|
|
1315
|
-
setInnerHTMLWithStackTrace(targetElement, next, {
|
|
1316
|
-
source: "updateFieldsWithSuggestions:modifiedField",
|
|
1317
|
-
fieldId: modField.fieldId,
|
|
1318
|
-
itemId: modField.item.id,
|
|
1319
|
-
language: modField.item.language,
|
|
1320
|
-
version: modField.item.version,
|
|
1321
|
-
});
|
|
1322
|
-
}
|
|
1323
|
-
if (wroteAtLeastOne) {
|
|
1324
|
-
// Reset after macrotask to ensure MutationObserver callbacks (which are microtasks) are suppressed
|
|
1325
|
-
setTimeout(() => {
|
|
1326
|
-
isUpdatingRef.current = false;
|
|
1327
|
-
}, 0);
|
|
1328
|
-
}
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
}
|
|
1332
|
-
if (context.mode !== "suggestions" && !context.showSuggestedEdits) {
|
|
1333
|
-
return;
|
|
1334
|
-
}
|
|
1335
|
-
// Now proceed with the original updateFieldsWithSuggestions logic
|
|
1336
|
-
const allFieldElements = doc.querySelectorAll("[data-fieldid][data-itemid][data-language][data-version]");
|
|
1337
|
-
const fieldItems = await context.itemsRepository.getItems(Array.from(allFieldElements)
|
|
1338
|
-
.map((element) => {
|
|
1339
|
-
const fieldId = element.getAttribute("data-fieldid");
|
|
1340
|
-
return fieldId
|
|
1341
|
-
? {
|
|
1342
|
-
id: fieldId,
|
|
1343
|
-
language: "en",
|
|
1344
|
-
version: 0,
|
|
1345
|
-
}
|
|
1346
|
-
: null;
|
|
1347
|
-
})
|
|
1348
|
-
.filter((item) => item !== null));
|
|
1349
|
-
const validFields = fieldItems.filter((item) => {
|
|
1350
|
-
const typeField = item.fields.find((f) => f.name === "Type");
|
|
1351
|
-
if (!typeField)
|
|
1352
|
-
return false;
|
|
1353
|
-
const typeValue = typeField.value;
|
|
1354
|
-
return typeValue && isTextFieldType(typeValue);
|
|
1355
|
-
});
|
|
1356
|
-
const validFieldElements = Array.from(allFieldElements).filter((element) => validFields.some((item) => element.getAttribute("data-fieldid") === item.id));
|
|
1357
|
-
validFields.forEach(async (item, index) => {
|
|
1358
|
-
const fieldElement = validFieldElements[index];
|
|
1359
|
-
if (!fieldElement)
|
|
1360
|
-
return;
|
|
1361
|
-
// Do not update if this field is currently being edited inline.
|
|
1362
|
-
// In suggestions mode we show suggested text in all fields, but must NOT overwrite
|
|
1363
|
-
// the focused field while the user is actively typing - that resets the cursor.
|
|
1364
|
-
// Treat stale iframe activeElement state as inactive once focus has left
|
|
1365
|
-
// the iframe, otherwise persisted suggestions can overwrite active typing.
|
|
1366
|
-
const fieldId = fieldElement.getAttribute("data-fieldid");
|
|
1367
|
-
const itemId = fieldElement.getAttribute("data-itemid");
|
|
1368
|
-
const language = fieldElement.getAttribute("data-language");
|
|
1369
|
-
const versionStr = fieldElement.getAttribute("data-version");
|
|
1370
|
-
if (!fieldId || !itemId || !language || !versionStr)
|
|
1371
|
-
return;
|
|
1372
|
-
const version = parseInt(versionStr, 10);
|
|
1373
|
-
const isModifiedNow = modifiedFieldsContext?.modifiedFields?.some((m) => m.fieldId === fieldId &&
|
|
1374
|
-
m.item.id === itemId &&
|
|
1375
|
-
m.item.language === language &&
|
|
1376
|
-
m.item.version === version);
|
|
1377
|
-
const iframeDoc = getAccessibleIframeDocument(pageViewContext.editorIframe);
|
|
1378
|
-
const isFieldBeingEdited = isInlineFieldBeingEdited(iframeDoc, fieldElement);
|
|
1379
|
-
if (isFieldBeingEdited) {
|
|
1380
|
-
return;
|
|
1381
|
-
}
|
|
1382
|
-
if (isSameFieldKey(fieldElement, activeFieldKey)) {
|
|
1383
|
-
if (context.mode !== "suggestions") {
|
|
1384
|
-
return;
|
|
1385
|
-
}
|
|
1386
|
-
}
|
|
1387
|
-
// Skip if this field currently has local modifications to avoid stomping fresh edits.
|
|
1388
|
-
// In suggestions mode, don't skip after the edit has been persisted - we need to
|
|
1389
|
-
// re-apply the merged suggestion even if the iframe still reports the field as active.
|
|
1390
|
-
if (isModifiedNow && context.mode !== "suggestions") {
|
|
1391
|
-
return;
|
|
1392
|
-
}
|
|
1393
|
-
// Build an item descriptor.
|
|
1394
|
-
const descriptor = { id: itemId, language, version };
|
|
1395
|
-
// Fetch the current item from the repository.
|
|
1396
|
-
const loadedItem = await context.itemsRepository.getItem(descriptor);
|
|
1397
|
-
if (!loadedItem)
|
|
1398
|
-
return;
|
|
1399
|
-
// Get the baseline from repository.
|
|
1400
|
-
const repositoryField = loadedItem.fields.find((f) => f.id === fieldId);
|
|
1401
|
-
let originalValue = repositoryField
|
|
1402
|
-
? repositoryField.rawValue || ""
|
|
1403
|
-
: "";
|
|
1404
|
-
// If the field is modified locally, use that value.
|
|
1405
|
-
const modField = modifiedFieldsContext?.modifiedFields.find((mod) => mod.fieldId === fieldId &&
|
|
1406
|
-
mod.item.id === itemId &&
|
|
1407
|
-
mod.item.language === language &&
|
|
1408
|
-
mod.item.version === version);
|
|
1409
|
-
if (modField) {
|
|
1410
|
-
originalValue = modField.value || "";
|
|
1411
|
-
}
|
|
1412
|
-
// If showSuggestedEdits is false, update with the base value.
|
|
1413
|
-
if (!context.showSuggestedEdits && context.mode !== "suggestions") {
|
|
1414
|
-
if (fieldElement.innerHTML !== originalValue) {
|
|
1415
|
-
setInnerHTMLWithStackTrace(fieldElement, originalValue, {
|
|
1416
|
-
source: "updateFieldsWithSuggestions:baselineOnly",
|
|
1417
|
-
fieldId,
|
|
1418
|
-
itemId,
|
|
1419
|
-
language,
|
|
1420
|
-
version,
|
|
1421
|
-
});
|
|
1422
|
-
}
|
|
1423
|
-
return;
|
|
1424
|
-
}
|
|
1425
|
-
// Otherwise, gather all suggestions for this field.
|
|
1426
|
-
const pageId = pageViewContext.pageItemDescriptor?.id;
|
|
1427
|
-
const pageVer = pageViewContext.pageItemDescriptor?.version;
|
|
1428
|
-
const fieldSuggestions = context.suggestedEdits.filter((s) => s.status === "pending" &&
|
|
1429
|
-
s.fieldId === fieldId &&
|
|
1430
|
-
s.itemId === itemId &&
|
|
1431
|
-
s.mainItemLanguage === language &&
|
|
1432
|
-
s.mainItemId === pageId &&
|
|
1433
|
-
(pageVer != null
|
|
1434
|
-
? s.mainItemVersion === pageVer || s.mainItemVersion === 0
|
|
1435
|
-
: s.mainItemVersion === version));
|
|
1436
|
-
if (!fieldSuggestions.length)
|
|
1437
|
-
return;
|
|
1438
|
-
// Sort suggestions in chronological order (oldest first).
|
|
1439
|
-
fieldSuggestions.sort((a, b) => new Date(a.created).getTime() - new Date(b.created).getTime());
|
|
1440
|
-
// Apply suggestions sequentially to generate the merged value.
|
|
1441
|
-
let mergedValue = originalValue;
|
|
1442
|
-
let couldApplyPatch = true;
|
|
1443
|
-
for (const suggestion of fieldSuggestions) {
|
|
1444
|
-
// Compute a patch from the suggestion's baseline to its intended new value.
|
|
1445
|
-
const patch = createPatch("field", suggestion.oldValue, suggestion.newValue);
|
|
1446
|
-
const patchedCandidate = applyPatch(mergedValue, patch);
|
|
1447
|
-
if (patchedCandidate !== false &&
|
|
1448
|
-
typeof patchedCandidate === "string") {
|
|
1449
|
-
mergedValue = patchedCandidate;
|
|
1450
|
-
}
|
|
1451
|
-
else {
|
|
1452
|
-
couldApplyPatch = false;
|
|
1453
|
-
}
|
|
1454
|
-
}
|
|
1455
|
-
if (!couldApplyPatch)
|
|
1456
|
-
mergedValue = fieldSuggestions[0]?.newValue || originalValue;
|
|
1457
|
-
// If showSuggestedEditsDiff is false, show only the merged text
|
|
1458
|
-
if (!context.showSuggestedEditsDiff) {
|
|
1459
|
-
if (fieldElement.innerHTML !== mergedValue) {
|
|
1460
|
-
setInnerHTMLWithStackTrace(fieldElement, mergedValue, {
|
|
1461
|
-
source: "updateFieldsWithSuggestions:mergedNoDiff",
|
|
1462
|
-
fieldId,
|
|
1463
|
-
itemId,
|
|
1464
|
-
language,
|
|
1465
|
-
version,
|
|
1466
|
-
});
|
|
1467
|
-
}
|
|
1468
|
-
return;
|
|
1469
|
-
}
|
|
1470
|
-
// Compute a word-based diff between originalValue and mergedValue.
|
|
1471
|
-
const diffParts = diffWords(originalValue, mergedValue);
|
|
1472
|
-
// Build HTML markup from the diff:
|
|
1473
|
-
let diffHTML = "";
|
|
1474
|
-
diffParts.forEach((part) => {
|
|
1475
|
-
let style = "";
|
|
1476
|
-
if (part.added) {
|
|
1477
|
-
style = "color: green;";
|
|
1478
|
-
}
|
|
1479
|
-
else if (part.removed) {
|
|
1480
|
-
style = "color: red; text-decoration: line-through;";
|
|
1481
|
-
}
|
|
1482
|
-
else {
|
|
1483
|
-
style = "color: gray;";
|
|
1484
|
-
}
|
|
1485
|
-
// Escape any HTML in part.value if needed.
|
|
1486
|
-
diffHTML += `<span style="${style}">${part.value}</span>`;
|
|
1487
|
-
});
|
|
1488
|
-
// Update the element's innerHTML with the diff markup.
|
|
1489
|
-
if (fieldElement.innerHTML !== diffHTML) {
|
|
1490
|
-
setInnerHTMLWithStackTrace(fieldElement, diffHTML, {
|
|
1491
|
-
source: "updateFieldsWithSuggestions:diffMarkup",
|
|
1492
|
-
fieldId,
|
|
1493
|
-
itemId,
|
|
1494
|
-
language,
|
|
1495
|
-
version,
|
|
1496
|
-
});
|
|
1497
|
-
}
|
|
1498
|
-
});
|
|
1499
|
-
};
|
|
1500
|
-
updateFieldsWithSuggestions();
|
|
1501
|
-
}, [
|
|
1502
|
-
context.mode,
|
|
1503
|
-
modifiedFieldsContext?.modifiedFields,
|
|
1504
|
-
context?.itemsRepository.revision,
|
|
1505
|
-
modifiedFieldsContext?.inlineEditingFieldElement,
|
|
1506
|
-
context?.showSuggestedEdits,
|
|
1507
|
-
context?.suggestedEdits,
|
|
1508
|
-
pageViewContext.pageItemDescriptor,
|
|
1509
|
-
pageViewContext.page,
|
|
1510
|
-
context.showSuggestedEditsDiff,
|
|
1511
|
-
context.showSuggestedEdits,
|
|
1512
|
-
context.remoteCarets,
|
|
1513
|
-
]);
|
|
1514
|
-
useEffect(() => {
|
|
1515
|
-
if (!context || compareView)
|
|
1516
|
-
return;
|
|
1517
|
-
const iframeDoc = getAccessibleIframeDocument(pageViewContext.editorIframe);
|
|
1518
|
-
if (!iframeDoc)
|
|
1519
|
-
return;
|
|
1520
|
-
// Get current page language and version to filter elements - use currentItemDescriptor as fallback during transitions
|
|
1521
|
-
const resetEffectPageLanguage = context.page?.item?.language ?? context.currentItemDescriptor?.language;
|
|
1522
|
-
const resetEffectPageVersion = context.page?.item?.version ?? context.currentItemDescriptor?.version;
|
|
1523
|
-
// Query all field elements
|
|
1524
|
-
const allFieldEls = Array.from(iframeDoc.querySelectorAll("[data-fieldid][data-itemid][data-language][data-version]"));
|
|
1525
|
-
allFieldEls.forEach(async (el) => {
|
|
1526
|
-
// don't stomp on the one that's live‑editing (only in non-preview modes)
|
|
1527
|
-
if (context.mode !== "preview" && isSameFieldKey(el, activeFieldKey))
|
|
1528
|
-
return;
|
|
1529
|
-
if (context.mode !== "preview" &&
|
|
1530
|
-
isElementActiveOrSelectedInIframe(iframeDoc, el)) {
|
|
1531
|
-
return;
|
|
1532
|
-
}
|
|
1533
|
-
const fieldId = el.getAttribute("data-fieldid");
|
|
1534
|
-
const itemId = el.getAttribute("data-itemid");
|
|
1535
|
-
const language = el.getAttribute("data-language");
|
|
1536
|
-
const version = parseInt(el.getAttribute("data-version"), 10);
|
|
1537
|
-
// CRITICAL FIX: Skip elements from other languages or versions during transitions
|
|
1538
|
-
// to prevent cross-language/version contamination during rapid switching
|
|
1539
|
-
if (resetEffectPageLanguage && language !== resetEffectPageLanguage) {
|
|
1540
|
-
return;
|
|
1541
|
-
}
|
|
1542
|
-
if (resetEffectPageVersion && version !== resetEffectPageVersion) {
|
|
1543
|
-
return;
|
|
1544
|
-
}
|
|
1545
|
-
// In suggestions mode (but not preview), only reset fields that don't have any suggested edits
|
|
1546
|
-
if (context.mode === "suggestions" ||
|
|
1547
|
-
(context.mode !== "preview" && context.showSuggestedEdits)) {
|
|
1548
|
-
const hasActiveSuggestions = context.suggestedEdits.some((edit) => edit.status === "pending" &&
|
|
1549
|
-
edit.fieldId === fieldId &&
|
|
1550
|
-
edit.itemId === itemId &&
|
|
1551
|
-
edit.mainItemLanguage === language &&
|
|
1552
|
-
(resetEffectPageVersion != null
|
|
1553
|
-
? edit.mainItemVersion === resetEffectPageVersion ||
|
|
1554
|
-
edit.mainItemVersion === 0
|
|
1555
|
-
: edit.mainItemVersion === version));
|
|
1556
|
-
// If field has active suggestions, don't reset it (let the other hook handle it)
|
|
1557
|
-
if (hasActiveSuggestions)
|
|
1558
|
-
return;
|
|
1559
|
-
}
|
|
1560
|
-
const realField = await context.itemsRepository.getItem({
|
|
1561
|
-
id: fieldId,
|
|
1562
|
-
language: "en",
|
|
1563
|
-
version: 0,
|
|
1564
|
-
});
|
|
1565
|
-
const fieldType = realField?.fields.find((f) => f.name === "Type")?.value;
|
|
1566
|
-
if (!fieldType || !isTextFieldType(fieldType)) {
|
|
1567
|
-
return;
|
|
1568
|
-
}
|
|
1569
|
-
// lookup baseline
|
|
1570
|
-
const loaded = await context.itemsRepository.getItem({
|
|
1571
|
-
id: itemId,
|
|
1572
|
-
language,
|
|
1573
|
-
version,
|
|
1574
|
-
});
|
|
1575
|
-
const repoF = loaded?.fields.find((f) => f.id === fieldId);
|
|
1576
|
-
let value = repoF?.rawValue ?? "";
|
|
1577
|
-
// override with any local edit (only in non-preview modes)
|
|
1578
|
-
// CRITICAL FIX: Only apply modified fields that match the current page language and version
|
|
1579
|
-
// to prevent cross-language/version contamination during rapid switching
|
|
1580
|
-
if (context.mode !== "preview" &&
|
|
1581
|
-
resetEffectPageLanguage &&
|
|
1582
|
-
language === resetEffectPageLanguage &&
|
|
1583
|
-
resetEffectPageVersion &&
|
|
1584
|
-
version === resetEffectPageVersion) {
|
|
1585
|
-
const mod = modifiedFieldsContext?.modifiedFields.find((m) => m.fieldId === fieldId &&
|
|
1586
|
-
m.item.id === itemId &&
|
|
1587
|
-
m.item.language === language &&
|
|
1588
|
-
m.item.version === version);
|
|
1589
|
-
if (mod)
|
|
1590
|
-
value = mod.value;
|
|
1591
|
-
}
|
|
1592
|
-
// write it in
|
|
1593
|
-
if (el.innerHTML !== value) {
|
|
1594
|
-
setInnerHTMLWithStackTrace(el, value, {
|
|
1595
|
-
source: "resetAllFieldsToBaselineOrLocal",
|
|
1596
|
-
fieldId,
|
|
1597
|
-
itemId,
|
|
1598
|
-
language,
|
|
1599
|
-
version,
|
|
1600
|
-
});
|
|
1601
|
-
}
|
|
1602
|
-
});
|
|
1603
|
-
}, [
|
|
1604
|
-
context.mode,
|
|
1605
|
-
context.showSuggestedEdits,
|
|
1606
|
-
context.suggestedEdits,
|
|
1607
|
-
context.remoteCarets,
|
|
1608
|
-
]);
|
|
1609
|
-
useEffect(() => {
|
|
1610
|
-
if (compareView || context.mode === "preview") {
|
|
1611
|
-
hideInlineFormatOverlay();
|
|
1612
|
-
return;
|
|
1613
|
-
}
|
|
1614
|
-
const iframe = pageViewContext.editorIframe;
|
|
1615
|
-
if (!iframe) {
|
|
1616
|
-
hideInlineFormatOverlay();
|
|
1617
|
-
return;
|
|
1618
|
-
}
|
|
1619
|
-
let detachDocumentListeners = null;
|
|
1620
|
-
const attachToCurrentIframeDocument = () => {
|
|
1621
|
-
detachDocumentListeners?.();
|
|
1622
|
-
const iframeDocument = getAccessibleIframeDocument(iframe);
|
|
1623
|
-
if (!iframeDocument) {
|
|
1624
|
-
detachDocumentListeners = null;
|
|
1625
|
-
hideInlineFormatOverlay();
|
|
1626
|
-
return;
|
|
1627
|
-
}
|
|
1628
|
-
let animationFrame = 0;
|
|
1629
|
-
const scheduleUpdate = () => {
|
|
1630
|
-
window.cancelAnimationFrame(animationFrame);
|
|
1631
|
-
animationFrame = window.requestAnimationFrame(updateInlineFormatOverlay);
|
|
1632
|
-
};
|
|
1633
|
-
const handlePointerDown = () => {
|
|
1634
|
-
inlineFormatPointerDownRef.current = true;
|
|
1635
|
-
hideInlineFormatOverlay();
|
|
1636
|
-
};
|
|
1637
|
-
const handlePointerUp = () => {
|
|
1638
|
-
inlineFormatPointerDownRef.current = false;
|
|
1639
|
-
scheduleUpdate();
|
|
1640
|
-
};
|
|
1641
|
-
iframeDocument.addEventListener("selectionchange", scheduleUpdate);
|
|
1642
|
-
iframeDocument.addEventListener("pointerdown", handlePointerDown);
|
|
1643
|
-
iframeDocument.addEventListener("pointerup", handlePointerUp);
|
|
1644
|
-
iframeDocument.addEventListener("mouseup", scheduleUpdate);
|
|
1645
|
-
iframeDocument.addEventListener("keyup", scheduleUpdate);
|
|
1646
|
-
iframe.contentWindow?.addEventListener("scroll", scheduleUpdate, true);
|
|
1647
|
-
window.addEventListener("scroll", scheduleUpdate, true);
|
|
1648
|
-
window.addEventListener("resize", scheduleUpdate);
|
|
1649
|
-
scheduleUpdate();
|
|
1650
|
-
detachDocumentListeners = () => {
|
|
1651
|
-
window.cancelAnimationFrame(animationFrame);
|
|
1652
|
-
iframeDocument.removeEventListener("selectionchange", scheduleUpdate);
|
|
1653
|
-
iframeDocument.removeEventListener("pointerdown", handlePointerDown);
|
|
1654
|
-
iframeDocument.removeEventListener("pointerup", handlePointerUp);
|
|
1655
|
-
iframeDocument.removeEventListener("mouseup", scheduleUpdate);
|
|
1656
|
-
iframeDocument.removeEventListener("keyup", scheduleUpdate);
|
|
1657
|
-
iframe.contentWindow?.removeEventListener("scroll", scheduleUpdate, true);
|
|
1658
|
-
window.removeEventListener("scroll", scheduleUpdate, true);
|
|
1659
|
-
window.removeEventListener("resize", scheduleUpdate);
|
|
1660
|
-
};
|
|
1661
|
-
};
|
|
1662
|
-
attachToCurrentIframeDocument();
|
|
1663
|
-
iframe.addEventListener("load", attachToCurrentIframeDocument);
|
|
1664
|
-
return () => {
|
|
1665
|
-
iframe.removeEventListener("load", attachToCurrentIframeDocument);
|
|
1666
|
-
detachDocumentListeners?.();
|
|
1667
|
-
};
|
|
1668
|
-
}, [compareView, context.mode, pageViewContext.editorIframe]);
|
|
1669
|
-
useEffect(() => {
|
|
1670
|
-
const animationFrame = window.requestAnimationFrame(updateInlineFormatOverlay);
|
|
1671
|
-
return () => window.cancelAnimationFrame(animationFrame);
|
|
1672
|
-
}, [
|
|
1673
|
-
context.selectedRange?.fieldId,
|
|
1674
|
-
context.selectedRange?.itemId,
|
|
1675
|
-
context.selectedRange?.language,
|
|
1676
|
-
context.selectedRange?.version,
|
|
1677
|
-
context.selectedRange?.startOffset,
|
|
1678
|
-
context.selectedRange?.endOffset,
|
|
1679
|
-
context.selectedRange?.text,
|
|
1680
|
-
]);
|
|
1681
|
-
useEffect(() => {
|
|
1682
|
-
if (!inlineFormatOverlay.visible)
|
|
1683
|
-
return;
|
|
1684
|
-
const handlePointerDown = (event) => {
|
|
1685
|
-
const target = event.target;
|
|
1686
|
-
if (target &&
|
|
1687
|
-
(inlineFormatOverlayRef.current?.contains(target) ||
|
|
1688
|
-
target === pageViewContext.editorIframe)) {
|
|
1689
|
-
return;
|
|
1690
|
-
}
|
|
1691
|
-
hideInlineFormatOverlay();
|
|
1692
|
-
};
|
|
1693
|
-
window.addEventListener("pointerdown", handlePointerDown, true);
|
|
1694
|
-
return () => {
|
|
1695
|
-
window.removeEventListener("pointerdown", handlePointerDown, true);
|
|
1696
|
-
};
|
|
1697
|
-
}, [inlineFormatOverlay.visible, pageViewContext.editorIframe]);
|
|
1698
|
-
const visibleInlineFormatCommands = inlineFormatSelectionRef.current
|
|
1699
|
-
? (inlineFormatCommandsCacheRef.current.get(getInlineFieldKey(inlineFormatSelectionRef.current.fieldElement)) ?? [])
|
|
1700
|
-
: [];
|
|
1701
|
-
if (!inlineFormatOverlay.visible ||
|
|
1702
|
-
visibleInlineFormatCommands.length === 0) {
|
|
1703
|
-
return null;
|
|
1704
|
-
}
|
|
1705
|
-
const overlay = (_jsx("div", { ref: inlineFormatOverlayRef, className: "border-border-default bg-neutral-grey-5 fixed z-[1000] flex h-8 items-center gap-0.5 rounded-md border px-0.5 py-px shadow-lg", style: {
|
|
1706
|
-
left: inlineFormatOverlay.left,
|
|
1707
|
-
top: inlineFormatOverlay.top,
|
|
1708
|
-
}, onMouseDown: (event) => {
|
|
1709
|
-
event.preventDefault();
|
|
1710
|
-
event.stopPropagation();
|
|
1711
|
-
}, children: visibleInlineFormatCommands.map((item) => {
|
|
1712
|
-
const isActive = inlineFormatOverlay.activeCommandIds.includes(item.id);
|
|
1713
|
-
return (_jsx("button", { type: "button", className: `inline-flex h-7 w-7 items-center justify-center rounded-sm p-0 text-[13px] leading-none font-semibold focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-blue-600 ${isActive
|
|
1714
|
-
? "text-neutral-grey-100 bg-white"
|
|
1715
|
-
: "bg-neutral-grey-5 text-neutral-grey-100 hover:bg-neutral-grey-10"}`, title: item.label, "aria-label": item.label, "aria-pressed": isActive, onMouseDown: (event) => void applyInlineFormatCommand(item, event), children: _jsx("span", { "aria-hidden": "true", className: "inline-flex translate-y-px items-center justify-center leading-none", children: item.icon ?? item.label }) }, item.id));
|
|
1716
|
-
}) }));
|
|
1717
|
-
return createPortal(overlay, document.body);
|
|
1718
|
-
}
|
|
1719
|
-
//# sourceMappingURL=InlineEditor.js.map
|