@parhelia/core 0.1.12570 → 0.1.12585
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents-view/AgentCard.d.ts +6 -4
- package/dist/agents-view/AgentCard.js +143 -24
- package/dist/agents-view/AgentCard.js.map +1 -1
- package/dist/agents-view/AgentsInbox.d.ts +1 -1
- package/dist/agents-view/AgentsInbox.js +7 -92
- package/dist/agents-view/AgentsInbox.js.map +1 -1
- package/dist/agents-view/AgentsTitlebar.js +3 -2
- package/dist/agents-view/AgentsTitlebar.js.map +1 -1
- package/dist/agents-view/AgentsView.d.ts +6 -7
- package/dist/agents-view/AgentsView.js +191 -99
- package/dist/agents-view/AgentsView.js.map +1 -1
- package/dist/agents-view/AgentsWorkspaceView.d.ts +2 -6
- package/dist/agents-view/AgentsWorkspaceView.js +266 -113
- package/dist/agents-view/AgentsWorkspaceView.js.map +1 -1
- package/dist/agents-view/ProfileAgentsGroup.d.ts +2 -1
- package/dist/agents-view/ProfileAgentsGroup.js +4 -3
- package/dist/agents-view/ProfileAgentsGroup.js.map +1 -1
- package/dist/components/ActionButton.d.ts +1 -1
- package/dist/components/ActionButton.js.map +1 -1
- package/dist/components/FilterInput.d.ts +1 -1
- package/dist/components/FilterInput.js +1 -1
- package/dist/components/FilterInput.js.map +1 -1
- package/dist/components/ui/LanguageSelector.js +2 -4
- package/dist/components/ui/LanguageSelector.js.map +1 -1
- package/dist/components/ui/PlaceholderInput.js +3 -3
- package/dist/components/ui/PlaceholderInput.js.map +1 -1
- package/dist/components/ui/PlaceholderInputTypes.js +1 -1
- package/dist/components/ui/PlaceholderInputTypes.js.map +1 -1
- package/dist/components/ui/alert-dialog.d.ts +1 -1
- package/dist/components/ui/alert-dialog.js +6 -10
- package/dist/components/ui/alert-dialog.js.map +1 -1
- package/dist/components/ui/button.d.ts +4 -4
- package/dist/components/ui/button.js +4 -1
- package/dist/components/ui/button.js.map +1 -1
- package/dist/components/ui/context-menu.d.ts +1 -1
- package/dist/components/ui/context-menu.js +12 -4
- package/dist/components/ui/context-menu.js.map +1 -1
- package/dist/components/ui/copy-button.d.ts +2 -1
- package/dist/components/ui/copy-button.js +2 -2
- package/dist/components/ui/copy-button.js.map +1 -1
- package/dist/components/ui/dialog.d.ts +1 -1
- package/dist/components/ui/dialog.js +21 -126
- package/dist/components/ui/dialog.js.map +1 -1
- package/dist/components/ui/input.d.ts +1 -1
- package/dist/components/ui/input.js +5 -3
- package/dist/components/ui/input.js.map +1 -1
- package/dist/components/ui/paste-button.d.ts +2 -1
- package/dist/components/ui/paste-button.js +2 -2
- package/dist/components/ui/paste-button.js.map +1 -1
- package/dist/components/ui/popover.js +1 -9
- package/dist/components/ui/popover.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/styled-dialog-title.js +1 -1
- package/dist/components/ui/styled-dialog-title.js.map +1 -1
- package/dist/components/ui/tabs.d.ts +1 -1
- package/dist/components/ui/tabs.js +4 -11
- package/dist/components/ui/tabs.js.map +1 -1
- package/dist/config/config.d.ts +4 -2
- package/dist/config/config.js +250 -70
- package/dist/config/config.js.map +1 -1
- package/dist/config/types/workspace.d.ts +6 -0
- package/dist/config/types.d.ts +63 -12
- package/dist/config/types.js.map +1 -1
- package/dist/editor/ConfirmationDialog.js +20 -4
- package/dist/editor/ConfirmationDialog.js.map +1 -1
- package/dist/editor/ContentTree.d.ts +2 -1
- package/dist/editor/ContentTree.js +93 -32
- package/dist/editor/ContentTree.js.map +1 -1
- package/dist/editor/Editor.js +87 -22
- package/dist/editor/Editor.js.map +1 -1
- package/dist/editor/FieldHistory.js +84 -36
- package/dist/editor/FieldHistory.js.map +1 -1
- package/dist/editor/FieldListField.js +21 -9
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/FieldListFieldWithFallbacks.js +23 -2
- package/dist/editor/FieldListFieldWithFallbacks.js.map +1 -1
- package/dist/editor/GlobalMenuBar.js +29 -2
- package/dist/editor/GlobalMenuBar.js.map +1 -1
- package/dist/editor/ImageEditor.js +5 -2
- package/dist/editor/ImageEditor.js.map +1 -1
- package/dist/editor/ItemInfo.js +36 -1
- package/dist/editor/ItemInfo.js.map +1 -1
- package/dist/editor/LinkEditorDialog.js +3 -0
- package/dist/editor/LinkEditorDialog.js.map +1 -1
- package/dist/editor/MainLayout.d.ts +0 -2
- package/dist/editor/MainLayout.js +65 -8
- package/dist/editor/MainLayout.js.map +1 -1
- package/dist/editor/MigrationsView.js +29 -5
- package/dist/editor/MigrationsView.js.map +1 -1
- package/dist/editor/MobileLayout.js +37 -12
- package/dist/editor/MobileLayout.js.map +1 -1
- package/dist/editor/PictureCropper.js +54 -45
- package/dist/editor/PictureCropper.js.map +1 -1
- package/dist/editor/PictureEditor.js +17 -15
- package/dist/editor/PictureEditor.js.map +1 -1
- package/dist/editor/QuickItemSwitcher.js +37 -63
- package/dist/editor/QuickItemSwitcher.js.map +1 -1
- package/dist/editor/SetupWizard.js +52 -12
- package/dist/editor/SetupWizard.js.map +1 -1
- package/dist/editor/Titlebar.js +7 -2
- package/dist/editor/Titlebar.js.map +1 -1
- package/dist/editor/ai/AgentCostDisplay.d.ts +1 -0
- package/dist/editor/ai/AgentCostDisplay.js +1 -1
- package/dist/editor/ai/AgentCostDisplay.js.map +1 -1
- package/dist/editor/ai/AgentDocumentList.js +32 -14
- package/dist/editor/ai/AgentDocumentList.js.map +1 -1
- package/dist/editor/ai/AgentGreeting.js +6 -5
- package/dist/editor/ai/AgentGreeting.js.map +1 -1
- package/dist/editor/ai/AgentProfileSelector.js +2 -1
- package/dist/editor/ai/AgentProfileSelector.js.map +1 -1
- package/dist/editor/ai/AgentStatusBadge.d.ts +0 -5
- package/dist/editor/ai/AgentStatusBadge.js +67 -65
- package/dist/editor/ai/AgentStatusBadge.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.d.ts +14 -2
- package/dist/editor/ai/AgentTerminal.js +2515 -579
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/AgentTerminalStatusBar.d.ts +9 -4
- package/dist/editor/ai/AgentTerminalStatusBar.js +481 -56
- package/dist/editor/ai/AgentTerminalStatusBar.js.map +1 -1
- package/dist/editor/ai/Agents.js +161 -113
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.d.ts +10 -1
- package/dist/editor/ai/AiResponseMessage.js +267 -26
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/ContextInfoBar.d.ts +2 -3
- package/dist/editor/ai/ContextInfoBar.js +64 -7
- package/dist/editor/ai/ContextInfoBar.js.map +1 -1
- package/dist/editor/ai/EditOperationsPanel.d.ts +3 -2
- package/dist/editor/ai/EditOperationsPanel.js +21 -78
- package/dist/editor/ai/EditOperationsPanel.js.map +1 -1
- package/dist/editor/ai/GuidanceOverlay.js +17 -11
- package/dist/editor/ai/GuidanceOverlay.js.map +1 -1
- package/dist/editor/ai/InlineAiDialog.d.ts +1 -1
- package/dist/editor/ai/InlineAiDialog.js +514 -192
- package/dist/editor/ai/InlineAiDialog.js.map +1 -1
- package/dist/editor/ai/InlineAiTrigger.js +115 -12
- package/dist/editor/ai/InlineAiTrigger.js.map +1 -1
- package/dist/editor/ai/MediaImage.js +40 -8
- package/dist/editor/ai/MediaImage.js.map +1 -1
- package/dist/editor/ai/SpawnedAgentsPanel.js +10 -12
- package/dist/editor/ai/SpawnedAgentsPanel.js.map +1 -1
- package/dist/editor/ai/ToolCallDisplay.d.ts +22 -2
- package/dist/editor/ai/ToolCallDisplay.js +614 -202
- package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
- package/dist/editor/ai/dialogs/AgentDialogHandler.d.ts +1 -8
- package/dist/editor/ai/dialogs/AgentDialogHandler.js +379 -42
- package/dist/editor/ai/dialogs/AgentDialogHandler.js.map +1 -1
- package/dist/editor/ai/dialogs/QuestionnaireInline.d.ts +5 -1
- package/dist/editor/ai/dialogs/QuestionnaireInline.js +628 -60
- package/dist/editor/ai/dialogs/QuestionnaireInline.js.map +1 -1
- package/dist/editor/ai/dialogs/agentDialogTypes.d.ts +117 -0
- package/dist/editor/ai/dialogs/agentDialogTypes.js +2 -0
- package/dist/editor/ai/dialogs/agentDialogTypes.js.map +1 -1
- package/dist/editor/ai/types.d.ts +3 -1
- package/dist/editor/ai/useAgentStatus.d.ts +2 -1
- package/dist/editor/ai/useAgentStatus.js +90 -100
- package/dist/editor/ai/useAgentStatus.js.map +1 -1
- package/dist/editor/ai/useInlineAiPosition.js +45 -5
- package/dist/editor/ai/useInlineAiPosition.js.map +1 -1
- package/dist/editor/client/AboutDialog.js +4 -2
- package/dist/editor/client/AboutDialog.js.map +1 -1
- package/dist/editor/client/EditorShell.d.ts +4 -1
- package/dist/editor/client/EditorShell.js +853 -258
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +33 -19
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/client/helpers.js +6 -0
- package/dist/editor/client/helpers.js.map +1 -1
- package/dist/editor/client/hooks/useEditorUrlSync.js +1 -2
- package/dist/editor/client/hooks/useEditorUrlSync.js.map +1 -1
- package/dist/editor/client/hooks/useEditorWebSocket.d.ts +10 -0
- package/dist/editor/client/hooks/useEditorWebSocket.js +209 -14
- package/dist/editor/client/hooks/useEditorWebSocket.js.map +1 -1
- package/dist/editor/client/hooks/useQuota.d.ts +8 -0
- package/dist/editor/client/hooks/useQuota.js.map +1 -1
- package/dist/editor/client/hooks/useSocketMessageHandler.js +73 -15
- package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
- package/dist/editor/client/itemsRepository.js +10 -6
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/client/operations.d.ts +6 -3
- package/dist/editor/client/operations.js +208 -30
- package/dist/editor/client/operations.js.map +1 -1
- package/dist/editor/client/pageModelBuilder.js +4 -31
- package/dist/editor/client/pageModelBuilder.js.map +1 -1
- package/dist/editor/client/ui/DevModeIndicator.js +2 -2
- package/dist/editor/client/ui/DevModeIndicator.js.map +1 -1
- package/dist/editor/client/ui/EditorChrome.d.ts +0 -6
- package/dist/editor/client/ui/EditorChrome.js +55 -72
- package/dist/editor/client/ui/EditorChrome.js.map +1 -1
- package/dist/editor/client/ui/FullscreenControls.js +5 -3
- package/dist/editor/client/ui/FullscreenControls.js.map +1 -1
- package/dist/editor/commands/commands.d.ts +11 -1
- package/dist/editor/commands/commands.js +12 -1
- package/dist/editor/commands/commands.js.map +1 -1
- package/dist/editor/commands/componentCommands.js +109 -55
- package/dist/editor/commands/componentCommands.js.map +1 -1
- package/dist/editor/commands/customCommandConverter.d.ts +8 -1
- package/dist/editor/commands/customCommandConverter.js +35 -5
- package/dist/editor/commands/customCommandConverter.js.map +1 -1
- package/dist/editor/commands/handlers/agentHandler.js +2 -1
- package/dist/editor/commands/handlers/agentHandler.js.map +1 -1
- package/dist/editor/commands/itemCommands.d.ts +3 -0
- package/dist/editor/commands/itemCommands.js +93 -10
- package/dist/editor/commands/itemCommands.js.map +1 -1
- package/dist/editor/commands/undo.d.ts +9 -15
- package/dist/editor/commands/undo.js +24 -0
- package/dist/editor/commands/undo.js.map +1 -1
- package/dist/editor/context-menu/InsertMenu.js +83 -39
- package/dist/editor/context-menu/InsertMenu.js.map +1 -1
- package/dist/editor/field-types/MultiLineText.js +1 -1
- package/dist/editor/field-types/MultiLineText.js.map +1 -1
- package/dist/editor/field-types/RawEditor.js +1 -1
- package/dist/editor/field-types/RichTextEditor.js +13 -5
- package/dist/editor/field-types/RichTextEditor.js.map +1 -1
- package/dist/editor/field-types/RichTextEditorComponent.js +37 -3
- package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
- package/dist/editor/field-types/SingleLineText.js +1 -1
- package/dist/editor/field-types/TreeListEditor.js +3 -2
- package/dist/editor/field-types/TreeListEditor.js.map +1 -1
- package/dist/editor/field-types/richtext/components/ReactSlate.css +23 -5
- package/dist/editor/field-types/richtext/components/ReactSlate.d.ts +2 -0
- package/dist/editor/field-types/richtext/components/ReactSlate.js +28 -4
- package/dist/editor/field-types/richtext/components/ReactSlate.js.map +1 -1
- package/dist/editor/field-types/richtext/components/ToolbarButton.js +4 -2
- package/dist/editor/field-types/richtext/components/ToolbarButton.js.map +1 -1
- package/dist/editor/field-types/richtext/contextMenuFactory.d.ts +13 -0
- package/dist/editor/field-types/richtext/contextMenuFactory.js +181 -24
- package/dist/editor/field-types/richtext/contextMenuFactory.js.map +1 -1
- 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/plugins.js +4 -0
- package/dist/editor/field-types/richtext/utils/plugins.js.map +1 -1
- package/dist/editor/field-types/textContextMenuFactory.js +3 -2
- package/dist/editor/field-types/textContextMenuFactory.js.map +1 -1
- package/dist/editor/media-selector/AiImageSearchPrompt.js +4 -2
- package/dist/editor/media-selector/AiImageSearchPrompt.js.map +1 -1
- package/dist/editor/media-selector/MediaFolderBrowser.js +1 -1
- package/dist/editor/media-selector/MediaFolderBrowser.js.map +1 -1
- package/dist/editor/media-selector/MediaSelector.js +7 -1
- package/dist/editor/media-selector/MediaSelector.js.map +1 -1
- package/dist/editor/media-selector/TreeSelector.js +40 -35
- package/dist/editor/media-selector/TreeSelector.js.map +1 -1
- package/dist/editor/menubar/ActiveUsers.js +1 -1
- package/dist/editor/menubar/ActiveUsers.js.map +1 -1
- package/dist/editor/menubar/GenericToolbar.js +4 -2
- package/dist/editor/menubar/GenericToolbar.js.map +1 -1
- package/dist/editor/menubar/ItemLanguageVersion.js +2 -2
- package/dist/editor/menubar/ItemLanguageVersion.js.map +1 -1
- package/dist/editor/menubar/PageSelector.js +26 -147
- package/dist/editor/menubar/PageSelector.js.map +1 -1
- package/dist/editor/menubar/Separator.js +1 -1
- package/dist/editor/menubar/VersionSelector.js +2 -4
- package/dist/editor/menubar/VersionSelector.js.map +1 -1
- package/dist/editor/menubar/WorkflowButton.js +39 -12
- package/dist/editor/menubar/WorkflowButton.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/CustomCommandsToolbar.js +16 -38
- package/dist/editor/menubar/toolbar-sections/CustomCommandsToolbar.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/EditControls.js +3 -3
- package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/HelpButton.js +1 -0
- package/dist/editor/menubar/toolbar-sections/HelpButton.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/ManualBrowser.d.ts +6 -10
- package/dist/editor/menubar/toolbar-sections/ManualBrowser.js +597 -220
- package/dist/editor/menubar/toolbar-sections/ManualBrowser.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js +13 -2
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
- package/dist/editor/page-editor-chrome/CommentHighlighting.js +42 -1
- package/dist/editor/page-editor-chrome/CommentHighlighting.js.map +1 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js +1 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
- package/dist/editor/page-editor-chrome/InlineEditor.js +97 -48
- package/dist/editor/page-editor-chrome/InlineEditor.js.map +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +38 -17
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js.map +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.js +17 -11
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.js.map +1 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js +301 -301
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
- package/dist/editor/page-viewer/DeviceToolbar.js +1 -1
- package/dist/editor/page-viewer/DeviceToolbar.js.map +1 -1
- package/dist/editor/page-viewer/EditorForm.js +69 -11
- package/dist/editor/page-viewer/EditorForm.js.map +1 -1
- package/dist/editor/page-viewer/MiniMap.d.ts +2 -4
- package/dist/editor/page-viewer/MiniMap.js +91 -28
- package/dist/editor/page-viewer/MiniMap.js.map +1 -1
- package/dist/editor/page-viewer/PageViewer.d.ts +3 -1
- package/dist/editor/page-viewer/PageViewer.js +92 -19
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.d.ts +2 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +348 -115
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.js +114 -49
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.js.map +1 -1
- package/dist/editor/page-viewer/pageViewContext.d.ts +1 -0
- package/dist/editor/page-viewer/pageViewContext.js +51 -14
- package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
- package/dist/editor/pageModel.d.ts +14 -1
- package/dist/editor/reviews/Comment.d.ts +2 -1
- package/dist/editor/reviews/Comment.js +92 -15
- package/dist/editor/reviews/Comment.js.map +1 -1
- package/dist/editor/reviews/CommentDisplayPopover.js +70 -5
- package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -1
- package/dist/editor/reviews/CommentView.d.ts +3 -1
- package/dist/editor/reviews/CommentView.js +26 -6
- package/dist/editor/reviews/CommentView.js.map +1 -1
- package/dist/editor/reviews/Comments.js +140 -75
- package/dist/editor/reviews/Comments.js.map +1 -1
- package/dist/editor/reviews/CreateReviewDialog.js +281 -177
- package/dist/editor/reviews/CreateReviewDialog.js.map +1 -1
- package/dist/editor/reviews/DecisionsMatrix.js +96 -25
- package/dist/editor/reviews/DecisionsMatrix.js.map +1 -1
- package/dist/editor/reviews/DiffView.js +7 -14
- package/dist/editor/reviews/DiffView.js.map +1 -1
- package/dist/editor/reviews/EditReviewSettingsDialog.js +6 -4
- package/dist/editor/reviews/EditReviewSettingsDialog.js.map +1 -1
- package/dist/editor/reviews/MultiReviewManager.js +25 -3
- package/dist/editor/reviews/MultiReviewManager.js.map +1 -1
- package/dist/editor/reviews/PagesPanel.js +31 -15
- package/dist/editor/reviews/PagesPanel.js.map +1 -1
- package/dist/editor/reviews/PreviewInfo.js +1 -4
- package/dist/editor/reviews/PreviewInfo.js.map +1 -1
- package/dist/editor/reviews/ReviewCard.js +13 -7
- package/dist/editor/reviews/ReviewCard.js.map +1 -1
- package/dist/editor/reviews/ReviewDetail.js +3 -2
- package/dist/editor/reviews/ReviewDetail.js.map +1 -1
- package/dist/editor/reviews/ReviewsList.js +7 -3
- package/dist/editor/reviews/ReviewsList.js.map +1 -1
- package/dist/editor/reviews/SuggestedEdit.js +34 -3
- package/dist/editor/reviews/SuggestedEdit.js.map +1 -1
- package/dist/editor/reviews/SuggestionDisplayPopover.js +31 -5
- package/dist/editor/reviews/SuggestionDisplayPopover.js.map +1 -1
- package/dist/editor/reviews/commentAi.js +25 -6
- package/dist/editor/reviews/commentAi.js.map +1 -1
- package/dist/editor/reviews/reviewCommands.js +4 -1
- package/dist/editor/reviews/reviewCommands.js.map +1 -1
- package/dist/editor/reviews/useMultiReview.js +2 -2
- package/dist/editor/reviews/useMultiReview.js.map +1 -1
- package/dist/editor/reviews/useReviews.d.ts +4 -3
- package/dist/editor/reviews/useReviews.js +21 -32
- package/dist/editor/reviews/useReviews.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +240 -5
- package/dist/editor/services/agentService.js +299 -39
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +57 -1
- package/dist/editor/services/aiService.js +79 -6
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/services/contentService.d.ts +6 -3
- package/dist/editor/services/contentService.js +13 -12
- package/dist/editor/services/contentService.js.map +1 -1
- package/dist/editor/services/editService.d.ts +52 -1
- package/dist/editor/services/editService.js +94 -2
- package/dist/editor/services/editService.js.map +1 -1
- package/dist/editor/services/indexService.js +1 -1
- package/dist/editor/services/indexService.js.map +1 -1
- package/dist/editor/services/reviewsService.d.ts +3 -6
- package/dist/editor/services/reviewsService.js +2 -11
- package/dist/editor/services/reviewsService.js.map +1 -1
- package/dist/editor/services/serviceHelper.d.ts +2 -1
- package/dist/editor/services/serviceHelper.js +112 -20
- package/dist/editor/services/serviceHelper.js.map +1 -1
- package/dist/editor/services/systemService.d.ts +2 -1
- package/dist/editor/services/systemService.js +3 -0
- package/dist/editor/services/systemService.js.map +1 -1
- package/dist/editor/services-server/api.d.ts +1 -2
- package/dist/editor/services-server/api.js +11 -6
- package/dist/editor/services-server/api.js.map +1 -1
- package/dist/editor/settings/About.js +317 -3
- package/dist/editor/settings/About.js.map +1 -1
- package/dist/editor/settings/IndexOverview.js +3 -1
- package/dist/editor/settings/IndexOverview.js.map +1 -1
- package/dist/editor/settings/QuotaInfo.js +210 -4
- package/dist/editor/settings/QuotaInfo.js.map +1 -1
- package/dist/editor/settings/SettingsView.js +25 -23
- package/dist/editor/settings/SettingsView.js.map +1 -1
- package/dist/editor/settings/Status.js +7 -6
- package/dist/editor/settings/Status.js.map +1 -1
- package/dist/editor/settings/index/CollectionWarningsDisplay.d.ts +10 -0
- package/dist/editor/settings/index/CollectionWarningsDisplay.js +16 -0
- package/dist/editor/settings/index/CollectionWarningsDisplay.js.map +1 -0
- package/dist/editor/settings/index/useIndexStatus.js +23 -22
- package/dist/editor/settings/index/useIndexStatus.js.map +1 -1
- package/dist/editor/settings/panels/AgentsPanel.d.ts +0 -4
- package/dist/editor/settings/panels/AgentsPanel.js +95 -121
- package/dist/editor/settings/panels/AgentsPanel.js.map +1 -1
- package/dist/editor/settings/panels/ModelConfigPanel.js +1 -1
- package/dist/editor/settings/panels/ModelConfigPanel.js.map +1 -1
- package/dist/editor/settings/panels/ModelsPanel.js +324 -108
- package/dist/editor/settings/panels/ModelsPanel.js.map +1 -1
- package/dist/editor/settings/panels/ProvidersPanel.d.ts +1 -1
- package/dist/editor/settings/panels/ProvidersPanel.js +86 -59
- package/dist/editor/settings/panels/ProvidersPanel.js.map +1 -1
- package/dist/editor/settings/panels/SearchConfigPanel.js +67 -6
- package/dist/editor/settings/panels/SearchConfigPanel.js.map +1 -1
- package/dist/editor/settings/panels/StatusPanel.js +7 -2
- package/dist/editor/settings/panels/StatusPanel.js.map +1 -1
- package/dist/editor/settings/panels/index.d.ts +3 -2
- package/dist/editor/settings/panels/index.js +3 -2
- package/dist/editor/settings/panels/index.js.map +1 -1
- package/dist/editor/settings/status/coreStatusChecks.js +124 -19
- package/dist/editor/settings/status/coreStatusChecks.js.map +1 -1
- package/dist/editor/settings/status/useStartupChecks.d.ts +3 -1
- package/dist/editor/settings/status/useStartupChecks.js +9 -5
- package/dist/editor/settings/status/useStartupChecks.js.map +1 -1
- package/dist/editor/setup-wizard/steps/CompleteStep.d.ts +2 -1
- package/dist/editor/setup-wizard/steps/CompleteStep.js +2 -1
- package/dist/editor/setup-wizard/steps/CompleteStep.js.map +1 -1
- package/dist/editor/sidebar/ComponentPalette.js +2 -1
- package/dist/editor/sidebar/ComponentPalette.js.map +1 -1
- package/dist/editor/sidebar/ComponentTree.d.ts +8 -1
- package/dist/editor/sidebar/ComponentTree.js +216 -69
- package/dist/editor/sidebar/ComponentTree.js.map +1 -1
- package/dist/editor/sidebar/EditHistory.js +22 -46
- package/dist/editor/sidebar/EditHistory.js.map +1 -1
- package/dist/editor/sidebar/Favorites.js +4 -8
- package/dist/editor/sidebar/Favorites.js.map +1 -1
- package/dist/editor/sidebar/MainContentTree.js +4 -3
- package/dist/editor/sidebar/MainContentTree.js.map +1 -1
- package/dist/editor/sidebar/OperationItem.js +21 -7
- package/dist/editor/sidebar/OperationItem.js.map +1 -1
- package/dist/editor/sidebar/SidebarPanel.d.ts +3 -1
- package/dist/editor/sidebar/SidebarPanel.js +44 -12
- package/dist/editor/sidebar/SidebarPanel.js.map +1 -1
- package/dist/editor/sidebar/SidebarStack.d.ts +2 -1
- package/dist/editor/sidebar/SidebarStack.js +4 -3
- package/dist/editor/sidebar/SidebarStack.js.map +1 -1
- package/dist/editor/sidebar/Validation.js +24 -12
- package/dist/editor/sidebar/Validation.js.map +1 -1
- package/dist/editor/sidebar/Workbox.js +53 -3
- package/dist/editor/sidebar/Workbox.js.map +1 -1
- package/dist/editor/sidebar/WorkspaceRail.d.ts +0 -1
- package/dist/editor/sidebar/WorkspaceRail.js +56 -167
- package/dist/editor/sidebar/WorkspaceRail.js.map +1 -1
- package/dist/editor/template-wizard/TemplateStructureInlineEditor.js +3 -2
- package/dist/editor/template-wizard/TemplateStructureInlineEditor.js.map +1 -1
- package/dist/editor/tree-indicators/GutterColumns.d.ts +3 -1
- package/dist/editor/tree-indicators/GutterColumns.js +26 -5
- package/dist/editor/tree-indicators/GutterColumns.js.map +1 -1
- package/dist/editor/tree-indicators/GutterContext.d.ts +4 -0
- package/dist/editor/tree-indicators/GutterContext.js +23 -0
- package/dist/editor/tree-indicators/GutterContext.js.map +1 -1
- package/dist/editor/tree-indicators/index.d.ts +0 -1
- package/dist/editor/tree-indicators/index.js +0 -1
- package/dist/editor/tree-indicators/index.js.map +1 -1
- package/dist/editor/tree-indicators/types.d.ts +12 -1
- package/dist/editor/ui/CopyMoveTargetSelectorDialog.js +1 -1
- package/dist/editor/ui/CopyMoveTargetSelectorDialog.js.map +1 -1
- package/dist/editor/ui/Icons.js +1 -1
- package/dist/editor/ui/Icons.js.map +1 -1
- package/dist/editor/ui/ItemNameDialogNew.d.ts +2 -0
- package/dist/editor/ui/ItemNameDialogNew.js +33 -17
- package/dist/editor/ui/ItemNameDialogNew.js.map +1 -1
- package/dist/editor/ui/ItemSearch.js +7 -11
- package/dist/editor/ui/ItemSearch.js.map +1 -1
- package/dist/editor/ui/SimpleIconButton.js +1 -1
- package/dist/editor/ui/SimpleIconButton.js.map +1 -1
- package/dist/editor/ui/SimpleTabs.d.ts +1 -0
- package/dist/editor/ui/SimpleTabs.js +45 -25
- package/dist/editor/ui/SimpleTabs.js.map +1 -1
- package/dist/editor/ui/Splitter.d.ts +1 -0
- package/dist/editor/ui/Splitter.js +102 -86
- package/dist/editor/ui/Splitter.js.map +1 -1
- package/dist/editor/ui/TemplateSelectorDialog.js +4 -4
- package/dist/editor/ui/TemplateSelectorDialog.js.map +1 -1
- package/dist/editor/ui/TreeListSelector.d.ts +6 -1
- package/dist/editor/ui/TreeListSelector.js +2 -2
- package/dist/editor/ui/TreeListSelector.js.map +1 -1
- package/dist/editor/utils/keyboardNavigation.d.ts +6 -20
- package/dist/editor/utils/keyboardNavigation.js +48 -140
- package/dist/editor/utils/keyboardNavigation.js.map +1 -1
- package/dist/editor/utils.js +19 -9
- package/dist/editor/utils.js.map +1 -1
- package/dist/editor/views/CompareView.d.ts +3 -1
- package/dist/editor/views/CompareView.js +7 -5
- package/dist/editor/views/CompareView.js.map +1 -1
- package/dist/editor/views/EditView.js +1 -1
- package/dist/editor/views/EditView.js.map +1 -1
- package/dist/editor/views/EditorSlot.js +27 -34
- package/dist/editor/views/EditorSlot.js.map +1 -1
- package/dist/editor/views/ItemEditor.js +7 -3
- package/dist/editor/views/ItemEditor.js.map +1 -1
- package/dist/editor/views/MediaFolderEditView.js +1 -1
- package/dist/editor/views/MediaFolderEditView.js.map +1 -1
- package/dist/editor/views/ParheliaView.js +5 -6
- package/dist/editor/views/ParheliaView.js.map +1 -1
- package/dist/editor/views/SingleEditView.d.ts +2 -1
- package/dist/editor/views/SingleEditView.js +10 -8
- package/dist/editor/views/SingleEditView.js.map +1 -1
- package/dist/editor/views/editorSlotContext.js +35 -6
- package/dist/editor/views/editorSlotContext.js.map +1 -1
- package/dist/index.d.ts +16 -2
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/setup/services/setupWizardService.d.ts +48 -14
- package/dist/setup/services/setupWizardService.js +52 -17
- package/dist/setup/services/setupWizardService.js.map +1 -1
- package/dist/setup/wizard/steps/AddModelDialog.js +12 -3
- package/dist/setup/wizard/steps/AddModelDialog.js.map +1 -1
- package/dist/setup/wizard/steps/ImportModelDialog.js +46 -22
- package/dist/setup/wizard/steps/ImportModelDialog.js.map +1 -1
- package/dist/splash-screen/ModernSplashScreen.js +112 -32
- package/dist/splash-screen/ModernSplashScreen.js.map +1 -1
- package/dist/splash-screen/NewPage.js +33 -50
- package/dist/splash-screen/NewPage.js.map +1 -1
- package/dist/splash-screen/OpenPage.js +2 -6
- package/dist/splash-screen/OpenPage.js.map +1 -1
- package/dist/splash-screen/ParheliaAssistantChat.js +12 -29
- package/dist/splash-screen/ParheliaAssistantChat.js.map +1 -1
- package/dist/splash-screen/ParheliaLogo.js +87 -37
- package/dist/splash-screen/ParheliaLogo.js.map +1 -1
- package/dist/splash-screen/RecentPages.js +3 -3
- package/dist/splash-screen/RecentPages.js.map +1 -1
- package/dist/tour/Tour.d.ts +2 -1
- package/dist/tour/Tour.js +256 -75
- package/dist/tour/Tour.js.map +1 -1
- package/dist/tour/default-tour.js +222 -96
- package/dist/tour/default-tour.js.map +1 -1
- package/dist/types.d.ts +70 -29
- package/package.json +19 -15
- package/styles.css +39 -10
- package/dist/editor/ComponentInfo.d.ts +0 -4
- package/dist/editor/ComponentInfo.js +0 -41
- package/dist/editor/ComponentInfo.js.map +0 -1
- package/dist/editor/ai/HelpTerminal.d.ts +0 -5
- package/dist/editor/ai/HelpTerminal.js +0 -166
- package/dist/editor/ai/HelpTerminal.js.map +0 -1
- package/dist/editor/field-types/ReactQuill.d.ts +0 -125
- package/dist/editor/field-types/ReactQuill.js +0 -385
- package/dist/editor/field-types/ReactQuill.js.map +0 -1
- package/dist/editor/services-server/graphQL.d.ts +0 -29
- package/dist/editor/services-server/graphQL.js +0 -53
- package/dist/editor/services-server/graphQL.js.map +0 -1
- package/dist/editor/settings/AllAgentsPanel.d.ts +0 -5
- package/dist/editor/settings/AllAgentsPanel.js +0 -139
- package/dist/editor/settings/AllAgentsPanel.js.map +0 -1
- package/dist/editor/settings/LatestFeedback.d.ts +0 -1
- package/dist/editor/settings/LatestFeedback.js +0 -136
- package/dist/editor/settings/LatestFeedback.js.map +0 -1
- package/dist/editor/settings/Setup.d.ts +0 -1
- package/dist/editor/settings/Setup.js +0 -211
- package/dist/editor/settings/Setup.js.map +0 -1
- package/dist/editor/settings/panels/DatabasePanel.d.ts +0 -6
- package/dist/editor/settings/panels/DatabasePanel.js +0 -50
- package/dist/editor/settings/panels/DatabasePanel.js.map +0 -1
- package/dist/editor/settings/setup-steps/AiSetupStep/EmbeddingsModelSection.d.ts +0 -2
- package/dist/editor/settings/setup-steps/AiSetupStep/EmbeddingsModelSection.js +0 -195
- package/dist/editor/settings/setup-steps/AiSetupStep/EmbeddingsModelSection.js.map +0 -1
- package/dist/editor/settings/setup-steps/AiSetupStep/index.d.ts +0 -2
- package/dist/editor/settings/setup-steps/AiSetupStep/index.js +0 -21
- package/dist/editor/settings/setup-steps/AiSetupStep/index.js.map +0 -1
- package/dist/editor/settings/setup-steps/AiSetupStep/provider/ProviderSection.d.ts +0 -1
- package/dist/editor/settings/setup-steps/AiSetupStep/provider/ProviderSection.js +0 -233
- package/dist/editor/settings/setup-steps/AiSetupStep/provider/ProviderSection.js.map +0 -1
- package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersList.d.ts +0 -15
- package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersList.js +0 -14
- package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersList.js.map +0 -1
- package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.d.ts +0 -1
- package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.js +0 -94
- package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.js.map +0 -1
- package/dist/editor/settings/setup-steps/AiSetupStep/types.d.ts +0 -1
- package/dist/editor/settings/setup-steps/AiSetupStep/types.js +0 -2
- package/dist/editor/settings/setup-steps/AiSetupStep/types.js.map +0 -1
- package/dist/editor/settings/setup-steps/AiSetupStep/utils.d.ts +0 -5
- package/dist/editor/settings/setup-steps/AiSetupStep/utils.js +0 -44
- package/dist/editor/settings/setup-steps/AiSetupStep/utils.js.map +0 -1
- package/dist/editor/settings/setup-steps/IndexSetupStep.d.ts +0 -2
- package/dist/editor/settings/setup-steps/IndexSetupStep.js +0 -36
- package/dist/editor/settings/setup-steps/IndexSetupStep.js.map +0 -1
- package/dist/editor/settings/setup-steps/SettingsSetupStep.d.ts +0 -2
- package/dist/editor/settings/setup-steps/SettingsSetupStep.js +0 -111
- package/dist/editor/settings/setup-steps/SettingsSetupStep.js.map +0 -1
- package/dist/editor/settings/setup-steps/SetupOverview.d.ts +0 -14
- package/dist/editor/settings/setup-steps/SetupOverview.js +0 -38
- package/dist/editor/settings/setup-steps/SetupOverview.js.map +0 -1
- package/dist/editor/sidebar/Debug.d.ts +0 -1
- package/dist/editor/sidebar/Debug.js +0 -70
- package/dist/editor/sidebar/Debug.js.map +0 -1
- package/dist/editor/sidebar/GraphQL.d.ts +0 -2
- package/dist/editor/sidebar/GraphQL.js +0 -234
- package/dist/editor/sidebar/GraphQL.js.map +0 -1
- package/dist/editor/sidebar/LeftToolbar.d.ts +0 -1
- package/dist/editor/sidebar/LeftToolbar.js +0 -12
- package/dist/editor/sidebar/LeftToolbar.js.map +0 -1
- package/dist/editor/sidebar/NavigationSidebar.d.ts +0 -4
- package/dist/editor/sidebar/NavigationSidebar.js +0 -254
- package/dist/editor/sidebar/NavigationSidebar.js.map +0 -1
- package/dist/editor/tree-indicators/GutterSelector.d.ts +0 -5
- package/dist/editor/tree-indicators/GutterSelector.js +0 -91
- package/dist/editor/tree-indicators/GutterSelector.js.map +0 -1
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx,
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React, { useState, useEffect, useRef, useCallback, useSyncExternalStore, useMemo, startTransition, } from "react";
|
|
4
4
|
import { toast } from "sonner";
|
|
5
|
-
import { EditContextProvider, FieldsEditContextProvider, OperationsContextProvider, } from "./editContext";
|
|
5
|
+
import { EditContextProvider, FieldsEditContextProvider, OperationsContextProvider, useEditContext, } from "./editContext";
|
|
6
6
|
import { fieldModificationStore } from "./fieldModificationStore";
|
|
7
|
-
import { useRouter, useSearchParams, usePathname } from "
|
|
7
|
+
import { useRouter, useSearchParams, usePathname } from "./navigation";
|
|
8
8
|
import { findComponent, getComponentById } from "../componentTreeHelper";
|
|
9
9
|
import { getOperationsContext } from "./operations";
|
|
10
10
|
import { handleErrorResult } from "./helpers";
|
|
11
|
-
import { executeFieldAction as executeFieldServerAction, connectSocket, getEditHistory, getRunningOperations, releaseFieldLocks, validateItems, } from "../services/editService";
|
|
11
|
+
import { executeFieldAction as executeFieldServerAction, connectSocket, getEditHistory, getRunningOperations, reconnectSession, releaseFieldLocks, validateItems, } from "../services/editService";
|
|
12
12
|
import { useEditorWebSocket } from "./hooks/useEditorWebSocket";
|
|
13
|
+
import { createEditorSocketDiagnostics, } from "./socketDiagnostics";
|
|
14
|
+
import { localStorageService } from "../services/localStorageService";
|
|
13
15
|
import { useSocketMessageHandler } from "./hooks/useSocketMessageHandler";
|
|
14
16
|
import "react-json-view-lite/dist/index.css";
|
|
15
17
|
import { MediaSelector, } from "../media-selector/MediaSelector";
|
|
@@ -20,6 +22,7 @@ import { getItemDescriptor } from "../utils";
|
|
|
20
22
|
import { EditContextMenu } from "../ContextMenu";
|
|
21
23
|
import { InlineAiTrigger } from "../ai/InlineAiTrigger";
|
|
22
24
|
import { FieldEditorPopup } from "../FieldEditorPopup";
|
|
25
|
+
import { ConcurrentUserLimitDialog } from "../ConcurrentUserLimitDialog";
|
|
23
26
|
import { post } from "../services/serviceHelper";
|
|
24
27
|
import { PageViewerFrame } from "../page-viewer/PageViewerFrame";
|
|
25
28
|
import { useItemsRepository } from "./itemsRepository";
|
|
@@ -33,6 +36,7 @@ import { GuidanceOverlay } from "../ai/GuidanceOverlay";
|
|
|
33
36
|
import { AgentDialogHandler } from "../ai/dialogs";
|
|
34
37
|
import { usePageViewContext, } from "../page-viewer/pageViewContext";
|
|
35
38
|
import { QuickItemSwitcher } from "../QuickItemSwitcher";
|
|
39
|
+
import { useEditorSlotContext, } from "../views/editorSlotContext";
|
|
36
40
|
import { getComments, getAvailableCommentTags, } from "../services/reviewsService";
|
|
37
41
|
import { useReviews } from "../reviews/useReviews";
|
|
38
42
|
import uuid from "react-uuid";
|
|
@@ -49,7 +53,33 @@ import { useWorkbox } from "./hooks/useWorkbox";
|
|
|
49
53
|
import { useMediaSelector } from "./hooks/useMediaSelector";
|
|
50
54
|
import { useGlobalEditorKeyDown } from "./hooks/useGlobalEditorKeyDown";
|
|
51
55
|
import { useStartupChecks } from "../settings/status/index";
|
|
52
|
-
|
|
56
|
+
import { FeatureGate, LicenseFeatures, LicenseProvider, LicenseOverlay, } from "../../licensing";
|
|
57
|
+
function AgentsSlotContextBridge({ slot }) {
|
|
58
|
+
const editContext = useEditContext();
|
|
59
|
+
const slotContext = useEditorSlotContext({
|
|
60
|
+
slotId: slot.slotId,
|
|
61
|
+
itemDescriptor: slot.itemDescriptor,
|
|
62
|
+
refreshToken: slot.refreshToken,
|
|
63
|
+
});
|
|
64
|
+
if (!editContext || !slotContext)
|
|
65
|
+
return null;
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
editContext.registerSlotContext(slot.slotId, slotContext);
|
|
68
|
+
return () => {
|
|
69
|
+
editContext.unregisterSlotContext(slot.slotId);
|
|
70
|
+
};
|
|
71
|
+
}, [
|
|
72
|
+
slot.slotId,
|
|
73
|
+
slotContext,
|
|
74
|
+
editContext.registerSlotContext,
|
|
75
|
+
editContext.unregisterSlotContext,
|
|
76
|
+
]);
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
function AgentsSlotContextBridgeHost({ slots }) {
|
|
80
|
+
return (_jsx(_Fragment, { children: slots.map((slot) => (_jsx(AgentsSlotContextBridge, { slot: slot }, `agents-slot-bridge-${slot.slotId}`))) }));
|
|
81
|
+
}
|
|
82
|
+
export function EditorShell({ configuration, className, item: loadItemDescriptor, sessionId, userInfo, userPreferences, initialLicenseStatus, initialLicenseStatusLoaded, parheliaSettings, setUserPreferences, children, }) {
|
|
53
83
|
const router = useRouter();
|
|
54
84
|
const pathname = usePathname();
|
|
55
85
|
const searchParams = useSearchParams();
|
|
@@ -97,6 +127,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
97
127
|
const [historyMode, setHistoryMode] = useState("global");
|
|
98
128
|
const [showOnlyMyChanges, setShowOnlyMyChanges] = useState(true);
|
|
99
129
|
const [filterByCurrentLanguage, setFilterByCurrentLanguage] = useState(true);
|
|
130
|
+
const [historySearchQuery, setHistorySearchQuery] = useState("");
|
|
100
131
|
const [recentEdits, setRecentEdits] = useState([]);
|
|
101
132
|
const addRecentEdit = useCallback((edit) => {
|
|
102
133
|
setRecentEdits((prevEditedFields) => {
|
|
@@ -124,14 +155,14 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
124
155
|
if (typeof window !== "undefined")
|
|
125
156
|
sessionStorage?.setItem("sessionId", sessionId);
|
|
126
157
|
// Workspace state
|
|
127
|
-
// Note: "reviews" is a sidebar, not a workspace. If
|
|
158
|
+
// Note: "reviews" is a sidebar, not a workspace. If workspace=reviews, we should
|
|
128
159
|
// set workspace to "editor" and open the reviews sidebar instead.
|
|
129
160
|
// Memoize searchParams reads to avoid triggering Router state updates during render
|
|
130
161
|
// (Next.js App Router uses startTransition internally for URL changes)
|
|
131
|
-
const rawWorkspace = useMemo(() => searchParams.get("workspace")
|
|
162
|
+
const rawWorkspace = useMemo(() => searchParams.get("workspace"), [searchParams]);
|
|
132
163
|
const isReviewsSidebarRequest = rawWorkspace === "reviews";
|
|
133
164
|
const [workspaceId, setWorkspaceId] = useState(
|
|
134
|
-
// If
|
|
165
|
+
// If workspace=reviews, use "editor" workspace (reviews is a sidebar, not a workspace)
|
|
135
166
|
isReviewsSidebarRequest
|
|
136
167
|
? "editor"
|
|
137
168
|
: (rawWorkspace ??
|
|
@@ -153,7 +184,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
153
184
|
else {
|
|
154
185
|
sidebars = [...(configuration.editor.defaultOpenSidebars ?? [])];
|
|
155
186
|
}
|
|
156
|
-
// If
|
|
187
|
+
// If workspace=reviews was requested, ensure reviews sidebar is open
|
|
157
188
|
if (isReviewsSidebarRequest && !sidebars.includes("reviews")) {
|
|
158
189
|
sidebars.push("reviews");
|
|
159
190
|
}
|
|
@@ -161,17 +192,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
161
192
|
});
|
|
162
193
|
// Toolbar pinned sidebars - icons always visible in toolbar (defined early so callbacks can access)
|
|
163
194
|
const [pinnedSidebars, setPinnedSidebars] = useState(userInfo.preferences?.pinnedSidebars || []);
|
|
195
|
+
const pinnedSidebarsRef = useRef(pinnedSidebars);
|
|
164
196
|
// Locked sidebars - open panels that stay visible when selecting another sidebar
|
|
165
197
|
const [lockedSidebars, setLockedSidebars] = useState(() => {
|
|
166
|
-
if (typeof window === "undefined") {
|
|
167
|
-
return [];
|
|
168
|
-
}
|
|
169
198
|
try {
|
|
170
|
-
const
|
|
171
|
-
if (!stored) {
|
|
172
|
-
return [];
|
|
173
|
-
}
|
|
174
|
-
const parsed = JSON.parse(stored);
|
|
199
|
+
const parsed = localStorageService.getOrSetItem("editor.lockedSidebars", []);
|
|
175
200
|
if (!Array.isArray(parsed) ||
|
|
176
201
|
!parsed.every((id) => typeof id === "string")) {
|
|
177
202
|
return [];
|
|
@@ -187,6 +212,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
187
212
|
useEffect(() => {
|
|
188
213
|
lockedSidebarsRef.current = lockedSidebars;
|
|
189
214
|
}, [lockedSidebars]);
|
|
215
|
+
useEffect(() => {
|
|
216
|
+
pinnedSidebarsRef.current = pinnedSidebars;
|
|
217
|
+
}, [pinnedSidebars]);
|
|
190
218
|
// Filter locked sidebars to only include currently open sidebars
|
|
191
219
|
useEffect(() => {
|
|
192
220
|
const openSet = new Set(openSidebars);
|
|
@@ -248,15 +276,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
248
276
|
// Group open sidebars into vertical stacks (each stack shares one left column).
|
|
249
277
|
const [sidebarStacks, setSidebarStacks] = useState(() => {
|
|
250
278
|
const defaultStacks = openSidebars.map((id) => [id]);
|
|
251
|
-
if (typeof window === "undefined") {
|
|
252
|
-
return normalizeSidebarStacks(openSidebars, defaultStacks);
|
|
253
|
-
}
|
|
254
279
|
try {
|
|
255
|
-
const
|
|
256
|
-
if (!
|
|
280
|
+
const parsed = localStorageService.getItem("editor.sidebarStacks");
|
|
281
|
+
if (!parsed) {
|
|
257
282
|
return normalizeSidebarStacks(openSidebars, defaultStacks);
|
|
258
283
|
}
|
|
259
|
-
const parsed = JSON.parse(stored);
|
|
260
284
|
if (!Array.isArray(parsed) ||
|
|
261
285
|
!parsed.every((x) => Array.isArray(x) && x.every((id) => typeof id === "string"))) {
|
|
262
286
|
return normalizeSidebarStacks(openSidebars, defaultStacks);
|
|
@@ -278,28 +302,19 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
278
302
|
}, [openSidebars, normalizeSidebarStacks]);
|
|
279
303
|
// Persist stacks independently of the URL (user layout preference).
|
|
280
304
|
useEffect(() => {
|
|
281
|
-
if (typeof window === "undefined")
|
|
282
|
-
return;
|
|
283
305
|
try {
|
|
284
|
-
|
|
306
|
+
localStorageService.setItem("editor.sidebarStacks", sidebarStacks);
|
|
285
307
|
}
|
|
286
308
|
catch { }
|
|
287
309
|
}, [sidebarStacks]);
|
|
288
310
|
// Persist locked sidebars to localStorage.
|
|
289
311
|
useEffect(() => {
|
|
290
|
-
if (typeof window === "undefined")
|
|
291
|
-
return;
|
|
292
312
|
try {
|
|
293
|
-
|
|
313
|
+
localStorageService.setItem("editor.lockedSidebars", lockedSidebars);
|
|
294
314
|
}
|
|
295
315
|
catch { }
|
|
296
316
|
}, [lockedSidebars]);
|
|
297
|
-
// Legacy aliases for backwards compatibility
|
|
298
317
|
const viewName = workspaceId;
|
|
299
|
-
const setViewName = setWorkspaceId;
|
|
300
|
-
const viewNameRef = workspaceIdRef;
|
|
301
|
-
const previousViewName = previousWorkspaceId;
|
|
302
|
-
const setPreviousViewName = setPreviousWorkspaceId;
|
|
303
318
|
const [compareMode, setCompareModeState] = useState(false);
|
|
304
319
|
const [componentDesignerComponent, setComponentDesignerComponent] = useState();
|
|
305
320
|
const [componentDesignerRendering, setComponentDesignerRendering] = useState();
|
|
@@ -307,6 +322,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
307
322
|
const [ignoreBlur, setIgnoreBlur] = useState(false);
|
|
308
323
|
const [currentItemDescriptor, setCurrentItemDescriptor] = useState();
|
|
309
324
|
const currentItemDescriptorRef = useRef(undefined);
|
|
325
|
+
const latestGlobalLoadKeyRef = useRef(null);
|
|
310
326
|
const [editorSlots, setEditorSlots] = useState(() => {
|
|
311
327
|
// Always start with empty slots - don't persist the number of slots
|
|
312
328
|
return [];
|
|
@@ -326,6 +342,29 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
326
342
|
activeSlotIdRef.current = activeSlotId;
|
|
327
343
|
}, [activeSlotId]);
|
|
328
344
|
const [slotContexts, setSlotContexts] = useState(() => new Map());
|
|
345
|
+
const promptSessionReconnect = useCallback((reason) => {
|
|
346
|
+
const description = reason && reason !== "session-revoked"
|
|
347
|
+
? `${reason}. Click reconnect to continue in this browser.`
|
|
348
|
+
: "You were disconnected because this account is active in another browser.";
|
|
349
|
+
toast.error("Session disconnected", {
|
|
350
|
+
id: "session-revoked",
|
|
351
|
+
description,
|
|
352
|
+
action: {
|
|
353
|
+
label: "Reconnect",
|
|
354
|
+
onClick: async () => {
|
|
355
|
+
const result = await reconnectSession(sessionId);
|
|
356
|
+
if (result.type === "success") {
|
|
357
|
+
window.location.reload();
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
toast.error("Reconnect failed", {
|
|
361
|
+
description: "Could not claim this browser session. Try again.",
|
|
362
|
+
});
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
duration: Infinity,
|
|
366
|
+
});
|
|
367
|
+
}, [sessionId]);
|
|
329
368
|
// Track previous item ID to detect actual navigation between pages
|
|
330
369
|
const previousItemIdRef = useRef(undefined);
|
|
331
370
|
useEffect(() => {
|
|
@@ -354,10 +393,18 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
354
393
|
// Ref to track when we're handling a popstate event (browser back/forward)
|
|
355
394
|
// This prevents the URL sync effect from pushing new history entries during back navigation
|
|
356
395
|
const isHandlingPopStateRef = useRef(false);
|
|
357
|
-
//
|
|
358
|
-
//
|
|
359
|
-
|
|
360
|
-
|
|
396
|
+
// When switchWorkspace already pushed a URL entry, the follow-up URL sync
|
|
397
|
+
// effect should only *replace* that entry (not push a second one).
|
|
398
|
+
const switchWorkspacePushedRef = useRef(false);
|
|
399
|
+
// Ref to track the last known URL for the popstate handler.
|
|
400
|
+
// Uses pathname+search (not full href) so comparisons are consistent with the
|
|
401
|
+
// relative URLs produced by the URL sync effect and updateUrl.
|
|
402
|
+
const lastUrlRef = useRef(typeof window !== "undefined"
|
|
403
|
+
? `${window.location.pathname}${window.location.search}`
|
|
404
|
+
: "");
|
|
405
|
+
// The very first URL sync after initial load should replaceState (not pushState)
|
|
406
|
+
// so the initial bare URL isn't left as a dead-end history entry.
|
|
407
|
+
const isFirstUrlSyncRef = useRef(true);
|
|
361
408
|
const [inlineEditingFieldElement, setInlineEditingFieldElement] = useState();
|
|
362
409
|
const [lockedField, setLockedField] = useState();
|
|
363
410
|
const [itemLanguages, setItemLanguages] = useState([]);
|
|
@@ -370,26 +417,16 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
370
417
|
const [showSuggestedEditsDiff, setShowSuggestedEditsDiff] = useState(false);
|
|
371
418
|
const [availableCommentTags, setAvailableCommentTags] = useState([]);
|
|
372
419
|
const [showComments, setShowComments] = useState(() => {
|
|
373
|
-
|
|
374
|
-
? localStorage.getItem("editor.showComments")
|
|
375
|
-
: null;
|
|
376
|
-
return savedShowComments ? JSON.parse(savedShowComments) : true;
|
|
420
|
+
return localStorageService.getOrSetItem("editor.showComments", true);
|
|
377
421
|
});
|
|
378
422
|
useEffect(() => {
|
|
379
|
-
|
|
380
|
-
localStorage.setItem("editor.showComments", JSON.stringify(showComments));
|
|
381
|
-
}
|
|
423
|
+
localStorageService.setItem("editor.showComments", showComments);
|
|
382
424
|
}, [showComments]);
|
|
383
425
|
const [showResolvedComments, setShowResolvedComments] = useState(() => {
|
|
384
|
-
|
|
385
|
-
? localStorage.getItem("editor.showResolvedComments")
|
|
386
|
-
: null;
|
|
387
|
-
return saved ? JSON.parse(saved) : false;
|
|
426
|
+
return localStorageService.getOrSetItem("editor.showResolvedComments", false);
|
|
388
427
|
});
|
|
389
428
|
useEffect(() => {
|
|
390
|
-
|
|
391
|
-
localStorage.setItem("editor.showResolvedComments", JSON.stringify(showResolvedComments));
|
|
392
|
-
}
|
|
429
|
+
localStorageService.setItem("editor.showResolvedComments", showResolvedComments);
|
|
393
430
|
}, [showResolvedComments]);
|
|
394
431
|
const [selectedComment, setSelectedComment] = useState();
|
|
395
432
|
const [browseHistory, setBrowseHistory] = useState(() => {
|
|
@@ -408,13 +445,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
408
445
|
visitedAt: entry.visitedAt,
|
|
409
446
|
}));
|
|
410
447
|
});
|
|
411
|
-
// Navigation history for browser history (
|
|
448
|
+
// Navigation history for browser history (workspace + item combinations)
|
|
412
449
|
const [navigationHistory, setNavigationHistory] = useState(() => {
|
|
413
|
-
// Initialize from browse history with current
|
|
450
|
+
// Initialize from browse history with the current workspace
|
|
414
451
|
if (!userInfo.browseHistory)
|
|
415
452
|
return [];
|
|
416
453
|
const defaultWorkspaceId = searchParams.get("workspace") ??
|
|
417
|
-
searchParams.get("view") ??
|
|
418
454
|
configuration.editor.defaultWorkspace ??
|
|
419
455
|
configuration.editor.workspaces?.[0]?.id ??
|
|
420
456
|
"editor";
|
|
@@ -446,20 +482,43 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
446
482
|
const [statusMessage, setStatusMessage] = useState("");
|
|
447
483
|
const [focusFieldComponentId, setFocusFieldComponentId] = useState();
|
|
448
484
|
const [enableCompletions, setEnableCompletions] = useState(false);
|
|
449
|
-
const [
|
|
485
|
+
const [showComponentNavigatorDefault, setShowComponentNavigatorDefault] = useState(userPreferences.showComponentNavigator ?? false);
|
|
486
|
+
const [slotComponentNavigatorVisibility, setSlotComponentNavigatorVisibility] = useState({});
|
|
487
|
+
useEffect(() => {
|
|
488
|
+
setSlotComponentNavigatorVisibility((prev) => {
|
|
489
|
+
const next = {};
|
|
490
|
+
let changed = false;
|
|
491
|
+
for (const slot of editorSlots) {
|
|
492
|
+
if (Object.prototype.hasOwnProperty.call(prev, slot.slotId)) {
|
|
493
|
+
next[slot.slotId] = prev[slot.slotId];
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
next[slot.slotId] = showComponentNavigatorDefault;
|
|
497
|
+
changed = true;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if (Object.keys(prev).length !== editorSlots.length) {
|
|
501
|
+
changed = true;
|
|
502
|
+
}
|
|
503
|
+
return changed ? next : prev;
|
|
504
|
+
});
|
|
505
|
+
}, [editorSlots, showComponentNavigatorDefault]);
|
|
450
506
|
const [showAgentsPanel, setShowAgentsPanel] = useState(userPreferences.showAgentsPanel ?? false);
|
|
451
507
|
const [showMinimap, setShowMinimap] = useState(userPreferences.showMinimap ?? true);
|
|
452
508
|
const [showHelpTerminal, setShowHelpTerminal] = useState(false);
|
|
453
509
|
const [helpTerminalInitialPrompt, setHelpTerminalInitialPrompt] = useState(undefined);
|
|
454
510
|
const [helpTerminalProfileName, setHelpTerminalProfileName] = useState(undefined);
|
|
455
511
|
const [helpTerminalActiveTab, setHelpTerminalActiveTab] = useState(undefined);
|
|
456
|
-
const [
|
|
512
|
+
const [selectedHelpSectionId, setSelectedHelpSectionId] = useState(null);
|
|
513
|
+
const [showAgentsWorkspaceEditor, setShowAgentsWorkspaceEditor] = useState(false);
|
|
514
|
+
const [selectedAgentsWorkspaceAgentId, setSelectedAgentsWorkspaceAgentId] = useState(null);
|
|
457
515
|
const [activeEditorTab, setActiveEditorTab] = useState(null);
|
|
458
516
|
const [showLayoutComponents, setShowLayoutComponents] = useState(userPreferences.showLayoutComponents ?? false);
|
|
459
517
|
const { quotaInfo, setQuotaInfo, isQuotaExceeded, getQuotaWarningMessage } = useQuota({
|
|
460
518
|
showError: ({ summary, details }) => showErrorToast({ summary, details }),
|
|
461
519
|
});
|
|
462
520
|
const [webSocketMessages, setWebSocketMessages] = useState([]);
|
|
521
|
+
const [socketDiagnostics, setSocketDiagnostics] = useState(() => createEditorSocketDiagnostics(sessionId));
|
|
463
522
|
const [favorites, setFavorites] = useState([]);
|
|
464
523
|
// Quick item switcher state
|
|
465
524
|
const [quickSwitcherVisible, setQuickSwitcherVisible] = useState(false);
|
|
@@ -477,13 +536,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
477
536
|
if (!startupChecks.hasBlockingIssues)
|
|
478
537
|
return;
|
|
479
538
|
// Don't redirect if already in settings workspace - let user navigate freely within settings
|
|
480
|
-
const currentWorkspace = searchParams.get("workspace")
|
|
539
|
+
const currentWorkspace = searchParams.get("workspace");
|
|
481
540
|
if (currentWorkspace === "settings")
|
|
482
541
|
return;
|
|
483
542
|
// Redirect to the status panel (where user can see all issues and navigate to fixes)
|
|
484
543
|
const url = new URL(window.location.href);
|
|
485
544
|
url.searchParams.set("workspace", "settings");
|
|
486
|
-
url.searchParams.delete("view"); // Remove legacy param
|
|
487
545
|
url.searchParams.set("ccpanel", "status");
|
|
488
546
|
router.push(url.toString(), { scroll: false });
|
|
489
547
|
}, [
|
|
@@ -501,6 +559,16 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
501
559
|
setMode(queryMode);
|
|
502
560
|
}
|
|
503
561
|
}, [searchParams, isInitialLoad]);
|
|
562
|
+
useEffect(() => {
|
|
563
|
+
if (!isInitialLoad)
|
|
564
|
+
return;
|
|
565
|
+
const helpParam = searchParams.get("help");
|
|
566
|
+
if (helpParam) {
|
|
567
|
+
setHelpTerminalActiveTab("manual");
|
|
568
|
+
setShowHelpTerminal(true);
|
|
569
|
+
setSelectedHelpSectionId(helpParam === "contents" || helpParam === "true" ? null : helpParam);
|
|
570
|
+
}
|
|
571
|
+
}, [searchParams, isInitialLoad]);
|
|
504
572
|
useEffect(() => {
|
|
505
573
|
if (mode === "suggestions") {
|
|
506
574
|
// Ensure we're in the editor workspace and open the feedback sidebar
|
|
@@ -547,12 +615,19 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
547
615
|
setSlotContexts((prev) => {
|
|
548
616
|
if (!prev.has(slotId))
|
|
549
617
|
return prev;
|
|
618
|
+
if (slotId === activeSlotIdRef.current) {
|
|
619
|
+
const activeCtx = prev.get(slotId);
|
|
620
|
+
if (activeCtx) {
|
|
621
|
+
lastActiveSlotContextRef.current = activeCtx;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
550
624
|
const next = new Map(prev);
|
|
551
625
|
next.delete(slotId);
|
|
552
626
|
return next;
|
|
553
627
|
});
|
|
554
628
|
}, []);
|
|
555
629
|
const slotContextsRef = useRef(slotContexts);
|
|
630
|
+
const lastActiveSlotContextRef = useRef(null);
|
|
556
631
|
useEffect(() => {
|
|
557
632
|
slotContextsRef.current = slotContexts;
|
|
558
633
|
}, [slotContexts]);
|
|
@@ -578,6 +653,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
578
653
|
const activeSlotContext = activeSlotId
|
|
579
654
|
? slotContexts.get(activeSlotId)
|
|
580
655
|
: undefined;
|
|
656
|
+
const isComponentNavigatorOpenForSlot = useCallback((slotId) => {
|
|
657
|
+
if (!slotId)
|
|
658
|
+
return showComponentNavigatorDefault;
|
|
659
|
+
return (slotComponentNavigatorVisibility[slotId] ?? showComponentNavigatorDefault);
|
|
660
|
+
}, [slotComponentNavigatorVisibility, showComponentNavigatorDefault]);
|
|
661
|
+
const showComponentNavigator = isComponentNavigatorOpenForSlot(activeSlotId);
|
|
581
662
|
// Sync global compareMode from the active slot's compareMode for URL syncing
|
|
582
663
|
useEffect(() => {
|
|
583
664
|
if (activeSlotContext && activeSlotContext.compareMode !== compareMode) {
|
|
@@ -625,6 +706,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
625
706
|
const isItemUsedInCurrentPage = useCallback((item) => pageItemsSetRef.current.has(makeItemKey(item)), [makeItemKey]);
|
|
626
707
|
const socketMessageListeners = useRef(new Set());
|
|
627
708
|
const [socketConnectionVersion, setSocketConnectionVersion] = useState(0);
|
|
709
|
+
useEffect(() => {
|
|
710
|
+
setSocketDiagnostics(createEditorSocketDiagnostics(sessionId));
|
|
711
|
+
}, [sessionId]);
|
|
628
712
|
const addSocketMessageListener = useCallback((callback) => {
|
|
629
713
|
socketMessageListeners.current.add(callback);
|
|
630
714
|
return () => socketMessageListeners.current.delete(callback);
|
|
@@ -670,7 +754,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
670
754
|
catch { }
|
|
671
755
|
};
|
|
672
756
|
}, [addSocketMessageListener]);
|
|
757
|
+
const shouldLoadReviews = openSidebars.includes("reviews") ||
|
|
758
|
+
workspaceId === "reviews" ||
|
|
759
|
+
workspaceId === "comments";
|
|
673
760
|
const reviews = useReviews({
|
|
761
|
+
enabled: shouldLoadReviews,
|
|
674
762
|
currentItemDescriptor,
|
|
675
763
|
addSocketMessageListener: addSocketMessageListener,
|
|
676
764
|
});
|
|
@@ -709,8 +797,13 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
709
797
|
console.error(`No workspace found for id: ${workspaceId}`);
|
|
710
798
|
return null;
|
|
711
799
|
}
|
|
712
|
-
|
|
713
|
-
|
|
800
|
+
const activeKeyboardCommands = useMemo(() => {
|
|
801
|
+
const activeCommandIds = new Set(currentWorkspace.keyboardCommandIds ?? []);
|
|
802
|
+
return (configuration.commands.keyboardCommands ?? []).filter((command) => activeCommandIds.has(command.id));
|
|
803
|
+
}, [
|
|
804
|
+
currentWorkspace.keyboardCommandIds,
|
|
805
|
+
configuration.commands.keyboardCommands,
|
|
806
|
+
]);
|
|
714
807
|
useEffect(() => {
|
|
715
808
|
if (currentWorkspace?.component) {
|
|
716
809
|
setCenterPanelView(currentWorkspace.component);
|
|
@@ -738,6 +831,45 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
738
831
|
const sendClientInfo = useCallback(() => {
|
|
739
832
|
debouncedSendClientInfo();
|
|
740
833
|
}, [debouncedSendClientInfo]);
|
|
834
|
+
const getCurrentHistoryState = useCallback(() => {
|
|
835
|
+
if (typeof window === "undefined") {
|
|
836
|
+
return null;
|
|
837
|
+
}
|
|
838
|
+
return window.history.state;
|
|
839
|
+
}, []);
|
|
840
|
+
useEffect(() => {
|
|
841
|
+
if (isInitialLoad) {
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
const itemid = searchParams.get("itemid");
|
|
845
|
+
const language = searchParams.get("lang") ?? searchParams.get("language");
|
|
846
|
+
const versionParam = searchParams.get("version");
|
|
847
|
+
const version = versionParam ? parseInt(versionParam, 10) : 0;
|
|
848
|
+
const itemId = cleanId(itemid ?? undefined);
|
|
849
|
+
if (!itemId || !language) {
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
const currentDescriptor = currentItemDescriptorRef.current;
|
|
853
|
+
const matchesCurrentDescriptor = currentDescriptor?.id === itemId &&
|
|
854
|
+
currentDescriptor?.language === language &&
|
|
855
|
+
(!version || currentDescriptor?.version === version);
|
|
856
|
+
if (matchesCurrentDescriptor) {
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
isHandlingPopStateRef.current = true;
|
|
860
|
+
loadItemRef
|
|
861
|
+
.current({
|
|
862
|
+
id: itemId,
|
|
863
|
+
language,
|
|
864
|
+
version,
|
|
865
|
+
}, {
|
|
866
|
+
addToBrowseHistory: false,
|
|
867
|
+
skipViewChange: true,
|
|
868
|
+
})
|
|
869
|
+
.finally(() => {
|
|
870
|
+
isHandlingPopStateRef.current = false;
|
|
871
|
+
});
|
|
872
|
+
}, [isInitialLoad, searchParams]);
|
|
741
873
|
const startTour = useCallback(() => {
|
|
742
874
|
setIsTourActive(true);
|
|
743
875
|
// Persist tour state to URL so it survives navigation/remounts
|
|
@@ -745,32 +877,61 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
745
877
|
const params = new URLSearchParams(window.location.search);
|
|
746
878
|
params.set("tour", "active");
|
|
747
879
|
const newUrl = `${window.location.pathname}?${params.toString()}`;
|
|
748
|
-
window.history.replaceState(
|
|
880
|
+
window.history.replaceState(getCurrentHistoryState(), "", newUrl);
|
|
749
881
|
}, [setIsTourActive]);
|
|
750
|
-
const isMobile = useMediaQuery("(max-width:
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
882
|
+
const isMobile = useMediaQuery("(max-width: 767px)");
|
|
883
|
+
// On mobile, clear all locked sidebars and keep only the last-opened panel.
|
|
884
|
+
// Handles viewport resize: desktop -> mobile unlocks everything and trims to one panel.
|
|
885
|
+
useEffect(() => {
|
|
886
|
+
if (isMobile) {
|
|
887
|
+
setLockedSidebars([]);
|
|
888
|
+
const current = openSidebarsRef.current;
|
|
889
|
+
const lastSidebar = current[current.length - 1];
|
|
890
|
+
if (current.length > 1 && lastSidebar) {
|
|
891
|
+
const trimmed = [lastSidebar];
|
|
892
|
+
openSidebarsRef.current = trimmed;
|
|
893
|
+
setOpenSidebars(trimmed);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}, [isMobile]);
|
|
897
|
+
const setComponentNavigatorOpenForSlot = useCallback((slotId, value) => {
|
|
898
|
+
const previousValue = isComponentNavigatorOpenForSlot(slotId);
|
|
899
|
+
const newValue = typeof value === "function" ? value(previousValue) : value;
|
|
900
|
+
if (slotId) {
|
|
901
|
+
setSlotComponentNavigatorVisibility((prev) => {
|
|
902
|
+
const currentValue = prev[slotId] ?? showComponentNavigatorDefault;
|
|
903
|
+
if (currentValue === newValue && prev[slotId] !== undefined) {
|
|
904
|
+
return prev;
|
|
905
|
+
}
|
|
906
|
+
return {
|
|
907
|
+
...prev,
|
|
908
|
+
[slotId]: newValue,
|
|
909
|
+
};
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
setShowComponentNavigatorDefault(newValue);
|
|
754
913
|
setUserPreferences({ showComponentNavigator: newValue });
|
|
755
|
-
// On mobile, close Agents Panel when opening Component Navigator
|
|
756
914
|
if (isMobile && newValue) {
|
|
757
915
|
setShowAgentsPanel(false);
|
|
758
916
|
setUserPreferences({ showAgentsPanel: false });
|
|
759
917
|
}
|
|
760
918
|
}, [
|
|
761
|
-
|
|
762
|
-
|
|
919
|
+
isComponentNavigatorOpenForSlot,
|
|
920
|
+
showComponentNavigatorDefault,
|
|
763
921
|
setUserPreferences,
|
|
764
922
|
isMobile,
|
|
765
923
|
setShowAgentsPanel,
|
|
766
924
|
]);
|
|
925
|
+
const handleSetShowComponentNavigator = useCallback((value) => {
|
|
926
|
+
setComponentNavigatorOpenForSlot(activeSlotIdRef.current, value);
|
|
927
|
+
}, [setComponentNavigatorOpenForSlot]);
|
|
767
928
|
const handleSetShowAgentsPanel = useCallback((value) => {
|
|
768
929
|
const newValue = typeof value === "function" ? value(showAgentsPanel) : value;
|
|
769
930
|
setShowAgentsPanel(newValue);
|
|
770
931
|
setUserPreferences({ showAgentsPanel: newValue });
|
|
771
932
|
// On mobile, close Component Navigator when opening Agents Panel
|
|
772
933
|
if (isMobile && newValue) {
|
|
773
|
-
|
|
934
|
+
setComponentNavigatorOpenForSlot(activeSlotIdRef.current, false);
|
|
774
935
|
setUserPreferences({ showComponentNavigator: false });
|
|
775
936
|
}
|
|
776
937
|
}, [
|
|
@@ -778,8 +939,40 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
778
939
|
setShowAgentsPanel,
|
|
779
940
|
setUserPreferences,
|
|
780
941
|
isMobile,
|
|
781
|
-
|
|
942
|
+
setComponentNavigatorOpenForSlot,
|
|
782
943
|
]);
|
|
944
|
+
// Mobile editor panel state (EditorForm shown in bottom panel on mobile)
|
|
945
|
+
const [mobileEditorPanelOpen, setMobileEditorPanelOpenRaw] = useState(false);
|
|
946
|
+
const [dismissedMobilePanelToken, setDismissedMobilePanelToken] = useState(null);
|
|
947
|
+
const previousActiveSlotIdRef = useRef(null);
|
|
948
|
+
const mobilePanelDismissToken = useMemo(() => {
|
|
949
|
+
const selectionKey = selection.join(",");
|
|
950
|
+
return [
|
|
951
|
+
activeSlotId || "no-slot",
|
|
952
|
+
selectionKey,
|
|
953
|
+
insertMode ? "insert" : "browse",
|
|
954
|
+
].join("|");
|
|
955
|
+
}, [activeSlotId, insertMode, selection]);
|
|
956
|
+
useEffect(() => {
|
|
957
|
+
const previousActiveSlotId = previousActiveSlotIdRef.current;
|
|
958
|
+
if (previousActiveSlotId !== activeSlotId) {
|
|
959
|
+
setDismissedMobilePanelToken(null);
|
|
960
|
+
}
|
|
961
|
+
previousActiveSlotIdRef.current = activeSlotId;
|
|
962
|
+
}, [activeSlotId]);
|
|
963
|
+
const handleSetMobileEditorPanelOpen = useCallback((open) => {
|
|
964
|
+
setMobileEditorPanelOpenRaw(open);
|
|
965
|
+
if (!open) {
|
|
966
|
+
setDismissedMobilePanelToken(mobilePanelDismissToken);
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
setDismissedMobilePanelToken(null);
|
|
970
|
+
if (open && isMobile) {
|
|
971
|
+
// Close all sidebars when opening the editor panel on mobile
|
|
972
|
+
openSidebarsRef.current = [];
|
|
973
|
+
setOpenSidebars([]);
|
|
974
|
+
}
|
|
975
|
+
}, [isMobile, mobilePanelDismissToken]);
|
|
783
976
|
const handleSetShowMinimap = useCallback((value) => {
|
|
784
977
|
const newValue = typeof value === "function" ? value(showMinimap) : value;
|
|
785
978
|
setShowMinimap(newValue);
|
|
@@ -793,6 +986,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
793
986
|
setHelpTerminalInitialPrompt(undefined);
|
|
794
987
|
setHelpTerminalProfileName(undefined);
|
|
795
988
|
setHelpTerminalActiveTab(undefined);
|
|
989
|
+
setSelectedHelpSectionId(null);
|
|
796
990
|
}
|
|
797
991
|
}, [showHelpTerminal]);
|
|
798
992
|
const toggleHelpTerminal = useCallback((options) => {
|
|
@@ -835,13 +1029,27 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
835
1029
|
setOpenSidebars(newOrder);
|
|
836
1030
|
setSidebarStacks((prev) => normalizeSidebarStacks(newOrder, prev));
|
|
837
1031
|
}, []);
|
|
1032
|
+
const ensureSidebarPinned = useCallback((sidebarId) => {
|
|
1033
|
+
const currentPinnedSidebars = pinnedSidebarsRef.current;
|
|
1034
|
+
if (currentPinnedSidebars.includes(sidebarId)) {
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
const newPinnedSidebars = [...currentPinnedSidebars, sidebarId];
|
|
1038
|
+
pinnedSidebarsRef.current = newPinnedSidebars;
|
|
1039
|
+
setPinnedSidebars(newPinnedSidebars);
|
|
1040
|
+
setUserPreferences({ pinnedSidebars: newPinnedSidebars });
|
|
1041
|
+
}, [setUserPreferences]);
|
|
838
1042
|
// messageHandler is defined after loadItem/loadHistory declarations to avoid temporal dead zones
|
|
839
1043
|
const user = activeSessions.find((x) => x.sessionId === sessionId)?.user ||
|
|
840
1044
|
userInfo.user;
|
|
841
|
-
// Self-heal if our session disappears (e.g., after HMR)
|
|
1045
|
+
// Self-heal if our session disappears (e.g., after HMR). Skip when concurrent user limit
|
|
1046
|
+
// is showing so we don't spam recovery attempts when the connection was rejected.
|
|
842
1047
|
const missingSessionRecoveryTimerRef = useRef(null);
|
|
843
1048
|
const missingSessionRecoveryAttemptsRef = useRef(0);
|
|
1049
|
+
const concurrentUserLimitErrorRef = useRef(null);
|
|
844
1050
|
useEffect(() => {
|
|
1051
|
+
if (concurrentUserLimitErrorRef.current !== null)
|
|
1052
|
+
return;
|
|
845
1053
|
const hasMySession = activeSessions.some((s) => s.sessionId === sessionId);
|
|
846
1054
|
if (hasMySession) {
|
|
847
1055
|
// Reset recovery state when we see ourselves again
|
|
@@ -857,7 +1065,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
857
1065
|
return;
|
|
858
1066
|
const attempt = missingSessionRecoveryAttemptsRef.current + 1;
|
|
859
1067
|
const delay = Math.min(250 * Math.pow(2, attempt - 1), 2000);
|
|
860
|
-
console.warn(`⚠️ Current session not present in active sessions. Recovery attempt ${attempt} in ${delay}ms...`);
|
|
861
1068
|
missingSessionRecoveryTimerRef.current = setTimeout(() => {
|
|
862
1069
|
missingSessionRecoveryTimerRef.current = null;
|
|
863
1070
|
missingSessionRecoveryAttemptsRef.current = attempt;
|
|
@@ -866,6 +1073,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
866
1073
|
}
|
|
867
1074
|
else {
|
|
868
1075
|
// Force a reconnect to refresh presence after several failed nudges
|
|
1076
|
+
console.warn("Session presence did not recover after retries. Forcing reconnect.");
|
|
869
1077
|
try {
|
|
870
1078
|
globalThis.editorSocket?.close(4000, "recover-presence");
|
|
871
1079
|
}
|
|
@@ -885,9 +1093,15 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
885
1093
|
// Initialize lastUrlRef to current URL on mount
|
|
886
1094
|
lastUrlRef.current = window.location.href;
|
|
887
1095
|
const keepAliveUrl = "/parhelia/keepalive";
|
|
888
|
-
const
|
|
1096
|
+
const runSessionCheck = () => {
|
|
889
1097
|
fetch(keepAliveUrl + "?ts=" + Date.now())
|
|
890
1098
|
.then((response) => {
|
|
1099
|
+
if (response.headers.get("X-Parhelia-Session-Revoked") === "true") {
|
|
1100
|
+
window.dispatchEvent(new CustomEvent("parhelia:session-revoked", {
|
|
1101
|
+
detail: { reason: "session-revoked" },
|
|
1102
|
+
}));
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
891
1105
|
if (response.status === 401 || response.status === 403) {
|
|
892
1106
|
toast.error("Your session has expired", {
|
|
893
1107
|
description: "Please login again to continue editing.",
|
|
@@ -900,7 +1114,24 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
900
1114
|
}
|
|
901
1115
|
})
|
|
902
1116
|
.catch((error) => console.error("Keep Alive error:", error));
|
|
903
|
-
}
|
|
1117
|
+
};
|
|
1118
|
+
const keepaliveIntervalMs = (() => {
|
|
1119
|
+
const param = new URLSearchParams(window.location.search).get("keepaliveIntervalMs");
|
|
1120
|
+
if (!param)
|
|
1121
|
+
return 5 * 60 * 1000;
|
|
1122
|
+
const ms = parseInt(param, 10);
|
|
1123
|
+
return Number.isFinite(ms) && ms >= 2000 && ms <= 60000
|
|
1124
|
+
? ms
|
|
1125
|
+
: 5 * 60 * 1000;
|
|
1126
|
+
})();
|
|
1127
|
+
const interval = setInterval(() => {
|
|
1128
|
+
runSessionCheck();
|
|
1129
|
+
}, keepaliveIntervalMs);
|
|
1130
|
+
const handleVisibilityChange = () => {
|
|
1131
|
+
if (document.visibilityState === "visible") {
|
|
1132
|
+
runSessionCheck();
|
|
1133
|
+
}
|
|
1134
|
+
};
|
|
904
1135
|
const handleMessage = (event) => {
|
|
905
1136
|
if (event.data.type === "componentsSelected") {
|
|
906
1137
|
setSelection(event.data.componentIds);
|
|
@@ -908,35 +1139,44 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
908
1139
|
};
|
|
909
1140
|
// Listen for browser navigation events (back/forward buttons)
|
|
910
1141
|
const handlePopState = (event) => {
|
|
911
|
-
const newUrl = window.location.
|
|
1142
|
+
const newUrl = `${window.location.pathname}${window.location.search}`;
|
|
912
1143
|
if (newUrl !== lastUrlRef.current) {
|
|
913
1144
|
lastUrlRef.current = newUrl;
|
|
914
1145
|
// Mark that we're handling a popstate to prevent URL sync from pushing to history
|
|
915
1146
|
isHandlingPopStateRef.current = true;
|
|
916
1147
|
// Sync URL parameters back to component state for browser navigation
|
|
917
1148
|
const urlParams = new URLSearchParams(window.location.search);
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
if (urlWorkspace && urlWorkspace !== viewNameRef.current) {
|
|
1149
|
+
const urlWorkspace = urlParams.get("workspace");
|
|
1150
|
+
if (urlWorkspace && urlWorkspace !== workspaceIdRef.current) {
|
|
921
1151
|
setWorkspaceId(urlWorkspace);
|
|
922
1152
|
}
|
|
923
|
-
// Handle sidebar changes
|
|
1153
|
+
// Handle sidebar changes — clear when absent so state matches the URL
|
|
924
1154
|
const sidebarParam = urlParams.get("sidebar");
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
1155
|
+
const newSidebars = sidebarParam
|
|
1156
|
+
? sidebarParam.split(",").filter(Boolean)
|
|
1157
|
+
: [];
|
|
1158
|
+
setOpenSidebars(newSidebars);
|
|
929
1159
|
// Handle wizard ID changes
|
|
930
1160
|
const wizardId = urlParams.get("wizardid");
|
|
931
1161
|
setCurrentWizardId(wizardId);
|
|
932
|
-
// Handle compare mode changes
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
1162
|
+
// Handle compare mode changes — always set to avoid stale-closure mismatch
|
|
1163
|
+
// (React skips re-render when the value is unchanged)
|
|
1164
|
+
setCompareMode(urlParams.get("compare") === "true");
|
|
1165
|
+
// Handle help panel changes
|
|
1166
|
+
const helpParam = urlParams.get("help");
|
|
1167
|
+
if (helpParam) {
|
|
1168
|
+
setHelpTerminalActiveTab("manual");
|
|
1169
|
+
setShowHelpTerminal(true);
|
|
1170
|
+
setSelectedHelpSectionId(helpParam === "contents" || helpParam === "true"
|
|
1171
|
+
? null
|
|
1172
|
+
: helpParam);
|
|
1173
|
+
}
|
|
1174
|
+
else {
|
|
1175
|
+
handleSetShowHelpTerminal(false);
|
|
936
1176
|
}
|
|
937
|
-
// Handle mode changes
|
|
1177
|
+
// Handle mode changes — always set to avoid stale-closure mismatch
|
|
938
1178
|
const urlMode = urlParams.get("mode");
|
|
939
|
-
if (urlMode
|
|
1179
|
+
if (urlMode) {
|
|
940
1180
|
setMode(urlMode);
|
|
941
1181
|
}
|
|
942
1182
|
// Handle fullscreen changes (shell-level state)
|
|
@@ -989,9 +1229,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
989
1229
|
};
|
|
990
1230
|
window.addEventListener("message", handleMessage);
|
|
991
1231
|
window.addEventListener("popstate", handlePopState);
|
|
1232
|
+
window.addEventListener("visibilitychange", handleVisibilityChange);
|
|
992
1233
|
return () => {
|
|
993
1234
|
window.removeEventListener("message", handleMessage);
|
|
994
1235
|
window.removeEventListener("popstate", handlePopState);
|
|
1236
|
+
window.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
995
1237
|
clearInterval(interval);
|
|
996
1238
|
};
|
|
997
1239
|
}, []);
|
|
@@ -1001,14 +1243,34 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1001
1243
|
if (searchParams.get("noTour") !== null) {
|
|
1002
1244
|
return;
|
|
1003
1245
|
}
|
|
1246
|
+
// Don't start tour when there are setup errors (user is or will be on system status page)
|
|
1247
|
+
if (startupChecks.state === "complete" && startupChecks.hasBlockingIssues) {
|
|
1248
|
+
return;
|
|
1249
|
+
}
|
|
1250
|
+
// Don't start tour when already on settings system status page
|
|
1251
|
+
if (viewName === "settings" && searchParams.get("ccpanel") === "status") {
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
// Wait for startup checks so we know whether we'll redirect to status
|
|
1255
|
+
if (startupChecks.state !== "complete") {
|
|
1256
|
+
return;
|
|
1257
|
+
}
|
|
1004
1258
|
const tour = configuration.activeTour;
|
|
1005
1259
|
const key = tour === "default" ? "editor.tourShown" : "editor.tourShown." + tour;
|
|
1006
|
-
const tourShown =
|
|
1260
|
+
const tourShown = localStorageService.getString(key);
|
|
1007
1261
|
if (!tourShown) {
|
|
1008
1262
|
startTour();
|
|
1009
|
-
|
|
1263
|
+
localStorageService.setString(key, "true");
|
|
1010
1264
|
}
|
|
1011
|
-
}, [
|
|
1265
|
+
}, [
|
|
1266
|
+
user,
|
|
1267
|
+
startupChecks.state,
|
|
1268
|
+
startupChecks.hasBlockingIssues,
|
|
1269
|
+
viewName,
|
|
1270
|
+
searchParams,
|
|
1271
|
+
configuration.activeTour,
|
|
1272
|
+
startTour,
|
|
1273
|
+
]);
|
|
1012
1274
|
// WebSocket initialization is performed after messageHandler is defined
|
|
1013
1275
|
// Defer URL sync until loadItem is defined below
|
|
1014
1276
|
// Mark end of initial load phase
|
|
@@ -1140,7 +1402,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1140
1402
|
const loadComments = useCallback(async () => {
|
|
1141
1403
|
if (!currentItemDescriptor)
|
|
1142
1404
|
return;
|
|
1143
|
-
const
|
|
1405
|
+
const reviewId = searchParams.get("reviewId");
|
|
1406
|
+
const result = await getComments(currentItemDescriptor.id, currentItemDescriptor.language, currentItemDescriptor.version, reviewId ?? undefined);
|
|
1144
1407
|
if (handleErrorResult(result, ui, state))
|
|
1145
1408
|
return;
|
|
1146
1409
|
setComments((x) => {
|
|
@@ -1153,7 +1416,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1153
1416
|
allComments.sort((a, b) => a.position - b.position);
|
|
1154
1417
|
return allComments;
|
|
1155
1418
|
});
|
|
1156
|
-
}, [currentItemDescriptor]);
|
|
1419
|
+
}, [currentItemDescriptor, searchParams]);
|
|
1157
1420
|
// Assuming currentItemDescriptor, ui, state, handleErrorResult, and setSuggestedEdits
|
|
1158
1421
|
// are available in your component context.
|
|
1159
1422
|
const loadSuggestedEdits = useCallback(async () => {
|
|
@@ -1162,7 +1425,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1162
1425
|
const result = await getSuggestedEdits(item.descriptor.id, item.descriptor.language, item.descriptor.version);
|
|
1163
1426
|
if (handleErrorResult(result, ui, state))
|
|
1164
1427
|
return;
|
|
1165
|
-
|
|
1428
|
+
const edits = result.data || [];
|
|
1429
|
+
setSuggestedEdits(edits);
|
|
1166
1430
|
}, [item]);
|
|
1167
1431
|
const loadFavorites = useCallback(async () => {
|
|
1168
1432
|
try {
|
|
@@ -1236,6 +1500,25 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1236
1500
|
return idB.localeCompare(idA);
|
|
1237
1501
|
});
|
|
1238
1502
|
}, []);
|
|
1503
|
+
const normalizeEditHistoryPayload = useCallback((value) => {
|
|
1504
|
+
if (Array.isArray(value)) {
|
|
1505
|
+
return value;
|
|
1506
|
+
}
|
|
1507
|
+
if (value && typeof value === "object") {
|
|
1508
|
+
const payload = value;
|
|
1509
|
+
const candidates = [
|
|
1510
|
+
payload.operations,
|
|
1511
|
+
payload.history,
|
|
1512
|
+
payload.items,
|
|
1513
|
+
payload.data,
|
|
1514
|
+
];
|
|
1515
|
+
const firstArray = candidates.find((candidate) => Array.isArray(candidate));
|
|
1516
|
+
if (Array.isArray(firstArray)) {
|
|
1517
|
+
return firstArray;
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
return [];
|
|
1521
|
+
}, []);
|
|
1239
1522
|
useEffect(() => {
|
|
1240
1523
|
// Read fresh page from the mutable slot context ref chain.
|
|
1241
1524
|
// The slot context uses a stable ref that is mutated in-place (see editorSlotContext.ts),
|
|
@@ -1258,7 +1541,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1258
1541
|
if (handleErrorResult(result, ui, state))
|
|
1259
1542
|
return;
|
|
1260
1543
|
setEditHistory((prev) => {
|
|
1261
|
-
const next = result.data
|
|
1544
|
+
const next = normalizeEditHistoryPayload(result.data);
|
|
1262
1545
|
if (!prev.length)
|
|
1263
1546
|
return sortEditHistoryByDateDesc(next);
|
|
1264
1547
|
if (!next.length)
|
|
@@ -1317,6 +1600,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1317
1600
|
const shouldFilterByLanguage = filterByLanguage !== undefined
|
|
1318
1601
|
? filterByLanguage
|
|
1319
1602
|
: filterByCurrentLanguage;
|
|
1603
|
+
const trimmedHistoryQuery = historySearchQuery.trim();
|
|
1604
|
+
const historyQuery = trimmedHistoryQuery.length > 0 ? trimmedHistoryQuery : undefined;
|
|
1320
1605
|
if (currentMode === "global") {
|
|
1321
1606
|
// Global mode: optionally filter by session and/or language
|
|
1322
1607
|
const currentLanguage = item?.descriptor?.language || currentItemDescriptor?.language;
|
|
@@ -1326,13 +1611,14 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1326
1611
|
language: shouldFilterByLanguage && currentLanguage
|
|
1327
1612
|
? currentLanguage
|
|
1328
1613
|
: undefined,
|
|
1614
|
+
query: historyQuery,
|
|
1329
1615
|
});
|
|
1330
1616
|
if (handleErrorResult(result, ui, state)) {
|
|
1331
1617
|
console.error("[EditorShell] Failed to load history:", result);
|
|
1332
1618
|
return;
|
|
1333
1619
|
}
|
|
1334
1620
|
setEditHistory((prev) => {
|
|
1335
|
-
const next = result.data
|
|
1621
|
+
const next = normalizeEditHistoryPayload(result.data);
|
|
1336
1622
|
const scope = {
|
|
1337
1623
|
mode: currentMode,
|
|
1338
1624
|
filterBySession: shouldFilterBySession,
|
|
@@ -1343,8 +1629,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1343
1629
|
};
|
|
1344
1630
|
if (!prev.length)
|
|
1345
1631
|
return sortEditHistoryByDateDesc(next.filter((op) => matchesHistoryScope(op, scope)));
|
|
1346
|
-
if (!next.length)
|
|
1632
|
+
if (!next.length) {
|
|
1633
|
+
// When searching, respect the empty result — don't fall back to previous items
|
|
1634
|
+
if (historyQuery)
|
|
1635
|
+
return [];
|
|
1347
1636
|
return sortEditHistoryByDateDesc(prev.filter((op) => matchesHistoryScope(op, scope)));
|
|
1637
|
+
}
|
|
1348
1638
|
const prevById = new Map(prev.map((x) => [x.id, x]));
|
|
1349
1639
|
const nextById = new Set(next.map((x) => x.id));
|
|
1350
1640
|
const merged = next
|
|
@@ -1380,9 +1670,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1380
1670
|
return mergedOp;
|
|
1381
1671
|
});
|
|
1382
1672
|
// Preserve operations that arrived via WebSocket during the fetch window
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1673
|
+
// but not when filtering by search query — search results are authoritative
|
|
1674
|
+
if (!historyQuery) {
|
|
1675
|
+
for (const [id, priorOp] of prevById) {
|
|
1676
|
+
if (!nextById.has(id) && matchesHistoryScope(priorOp, scope)) {
|
|
1677
|
+
merged.push(priorOp);
|
|
1678
|
+
}
|
|
1386
1679
|
}
|
|
1387
1680
|
}
|
|
1388
1681
|
return sortEditHistoryByDateDesc(merged);
|
|
@@ -1405,12 +1698,13 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1405
1698
|
const result = await getEditHistory({
|
|
1406
1699
|
item: itemFilter,
|
|
1407
1700
|
sessionId: shouldFilterBySession ? sessionId : undefined,
|
|
1701
|
+
query: historyQuery,
|
|
1408
1702
|
});
|
|
1409
1703
|
if (handleErrorResult(result, ui, state)) {
|
|
1410
1704
|
console.error("[EditorShell] Failed to load item history:", result);
|
|
1411
1705
|
return;
|
|
1412
1706
|
}
|
|
1413
|
-
let operations = result.data
|
|
1707
|
+
let operations = normalizeEditHistoryPayload(result.data);
|
|
1414
1708
|
// Client-side version filtering for current-version mode
|
|
1415
1709
|
if (currentMode === "current-version") {
|
|
1416
1710
|
// Defensive filter: only include operations for the current version
|
|
@@ -1433,8 +1727,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1433
1727
|
};
|
|
1434
1728
|
if (!prev.length)
|
|
1435
1729
|
return sortEditHistoryByDateDesc(operations.filter((op) => matchesHistoryScope(op, scope)));
|
|
1436
|
-
if (!operations.length)
|
|
1730
|
+
if (!operations.length) {
|
|
1731
|
+
if (historyQuery)
|
|
1732
|
+
return [];
|
|
1437
1733
|
return sortEditHistoryByDateDesc(prev.filter((op) => matchesHistoryScope(op, scope)));
|
|
1734
|
+
}
|
|
1438
1735
|
const prevById = new Map(prev.map((x) => [x.id, x]));
|
|
1439
1736
|
const nextById = new Set(operations.map((x) => x.id));
|
|
1440
1737
|
const merged = operations
|
|
@@ -1470,9 +1767,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1470
1767
|
return mergedOp;
|
|
1471
1768
|
});
|
|
1472
1769
|
// Preserve operations that arrived via WebSocket during the fetch window
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1770
|
+
// but not when filtering by search query — search results are authoritative
|
|
1771
|
+
if (!historyQuery) {
|
|
1772
|
+
for (const [id, priorOp] of prevById) {
|
|
1773
|
+
if (!nextById.has(id) && matchesHistoryScope(priorOp, scope)) {
|
|
1774
|
+
merged.push(priorOp);
|
|
1775
|
+
}
|
|
1476
1776
|
}
|
|
1477
1777
|
}
|
|
1478
1778
|
return sortEditHistoryByDateDesc(merged);
|
|
@@ -1483,9 +1783,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1483
1783
|
historyMode,
|
|
1484
1784
|
showOnlyMyChanges,
|
|
1485
1785
|
filterByCurrentLanguage,
|
|
1786
|
+
historySearchQuery,
|
|
1486
1787
|
item,
|
|
1487
1788
|
currentItemDescriptor,
|
|
1488
1789
|
matchesHistoryScope,
|
|
1790
|
+
normalizeEditHistoryPayload,
|
|
1489
1791
|
sortEditHistoryByDateDesc,
|
|
1490
1792
|
]);
|
|
1491
1793
|
// Debounced history refresh to avoid hammering `/parhelia/editHistory` on rapid UI changes.
|
|
@@ -1551,13 +1853,14 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1551
1853
|
}
|
|
1552
1854
|
}
|
|
1553
1855
|
else if (historyMode !== "global" && currentItemDescriptor) {
|
|
1856
|
+
// Always load immediately for page-centric/current-version/timeline so we don't show
|
|
1857
|
+
// an empty history list for the debounce window (e.g. 600ms). The effect only runs on
|
|
1858
|
+
// mode or item id/lang/version change, not on every keystroke, so this avoids flaky
|
|
1859
|
+
// undo/redo tests and improves UX when switching to page-centric.
|
|
1554
1860
|
if (!historyInitialLoadDoneRef.current) {
|
|
1555
1861
|
historyInitialLoadDoneRef.current = true;
|
|
1556
|
-
refreshHistoryRef.current(historyMode);
|
|
1557
|
-
}
|
|
1558
|
-
else {
|
|
1559
|
-
debouncedRefreshHistoryRef.current(historyMode);
|
|
1560
1862
|
}
|
|
1863
|
+
refreshHistoryRef.current(historyMode);
|
|
1561
1864
|
}
|
|
1562
1865
|
else if (historyMode !== "global" && !currentItemDescriptor) {
|
|
1563
1866
|
// Clear history if no item loaded in page-centric modes
|
|
@@ -1570,6 +1873,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1570
1873
|
historyMode,
|
|
1571
1874
|
showOnlyMyChanges,
|
|
1572
1875
|
filterByCurrentLanguage,
|
|
1876
|
+
historySearchQuery,
|
|
1573
1877
|
currentItemDescriptor?.id,
|
|
1574
1878
|
currentItemDescriptor?.language,
|
|
1575
1879
|
currentItemDescriptor?.version,
|
|
@@ -1634,6 +1938,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1634
1938
|
if (isInitialLoad)
|
|
1635
1939
|
return;
|
|
1636
1940
|
const current = new URLSearchParams(window.location.search);
|
|
1941
|
+
const urlWorkspace = current.get("workspace");
|
|
1942
|
+
const urlView = current.get("view");
|
|
1943
|
+
const isWorkspaceTransitioning = !!urlWorkspace && urlWorkspace !== viewName;
|
|
1637
1944
|
// Sync item-related parameters only when an item is selected
|
|
1638
1945
|
if (currentItemDescriptor) {
|
|
1639
1946
|
if (current.get("itemid") !== currentItemDescriptor.id) {
|
|
@@ -1664,10 +1971,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1664
1971
|
// If reviewId or urlItemId exists, preserve itemid/lang/version from URL
|
|
1665
1972
|
}
|
|
1666
1973
|
// Always sync workspace-related parameters regardless of item selection
|
|
1667
|
-
if (current.get("workspace") !== viewName) {
|
|
1974
|
+
if (!isWorkspaceTransitioning && current.get("workspace") !== viewName) {
|
|
1668
1975
|
current.set("workspace", viewName);
|
|
1669
1976
|
}
|
|
1670
|
-
current.delete("view"); // Remove legacy view param
|
|
1671
1977
|
// Sync sidebar state
|
|
1672
1978
|
const currentSidebars = current.get("sidebar") ?? "";
|
|
1673
1979
|
const newSidebars = openSidebars.join(",");
|
|
@@ -1679,6 +1985,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1679
1985
|
current.delete("sidebar");
|
|
1680
1986
|
}
|
|
1681
1987
|
}
|
|
1988
|
+
if (showHelpTerminal) {
|
|
1989
|
+
current.set("help", selectedHelpSectionId ?? "contents");
|
|
1990
|
+
}
|
|
1991
|
+
else {
|
|
1992
|
+
current.delete("help");
|
|
1993
|
+
}
|
|
1682
1994
|
if (!compareMode) {
|
|
1683
1995
|
current.delete("compare");
|
|
1684
1996
|
current.delete("compareLanguage");
|
|
@@ -1703,15 +2015,15 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1703
2015
|
else {
|
|
1704
2016
|
current.delete("wizardid");
|
|
1705
2017
|
}
|
|
1706
|
-
//
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
}
|
|
1713
|
-
else {
|
|
2018
|
+
// Preserve settings-specific parameters while transitioning into Settings too.
|
|
2019
|
+
// Some callers update the URL first and switch the workspace state a moment later.
|
|
2020
|
+
const isSettingsNavigation = viewName === "settings" ||
|
|
2021
|
+
urlWorkspace === "settings" ||
|
|
2022
|
+
urlView === "settings";
|
|
2023
|
+
if (!isSettingsNavigation) {
|
|
1714
2024
|
current.delete("ccpanel");
|
|
2025
|
+
current.delete("providerId");
|
|
2026
|
+
current.delete("modelId");
|
|
1715
2027
|
}
|
|
1716
2028
|
// Preserve reviewId parameter if it exists (for review links)
|
|
1717
2029
|
// This ensures review links don't lose the reviewId when URL is synced
|
|
@@ -1724,17 +2036,42 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1724
2036
|
const browserPathname = typeof window !== "undefined" ? window.location.pathname : pathname;
|
|
1725
2037
|
const newUrl = `${browserPathname}?${current.toString()}`;
|
|
1726
2038
|
const oldUrl = `${browserPathname}${window.location.search}`;
|
|
2039
|
+
const isRestoringLastSyncedUrl = newUrl === lastUrlRef.current;
|
|
1727
2040
|
// Skip pushing to history if we're handling a popstate event (browser back/forward)
|
|
1728
2041
|
// This prevents the URL sync from overwriting the history during back navigation
|
|
1729
2042
|
if (isHandlingPopStateRef.current) {
|
|
1730
2043
|
return;
|
|
1731
2044
|
}
|
|
1732
2045
|
if (newUrl !== oldUrl) {
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
2046
|
+
if (isFirstUrlSyncRef.current ||
|
|
2047
|
+
switchWorkspacePushedRef.current ||
|
|
2048
|
+
isRestoringLastSyncedUrl) {
|
|
2049
|
+
window.history.replaceState(getCurrentHistoryState(), "", newUrl);
|
|
2050
|
+
isFirstUrlSyncRef.current = false;
|
|
2051
|
+
switchWorkspacePushedRef.current = false;
|
|
2052
|
+
}
|
|
2053
|
+
else {
|
|
2054
|
+
window.history.pushState(getCurrentHistoryState(), "", newUrl);
|
|
2055
|
+
}
|
|
1736
2056
|
lastUrlRef.current = newUrl;
|
|
1737
2057
|
}
|
|
2058
|
+
else {
|
|
2059
|
+
// Even when the first sync is a no-op (URL already matches state), consume
|
|
2060
|
+
// the first-sync flag. Otherwise the *next* real change (e.g. user selecting
|
|
2061
|
+
// a different item) would hit the replaceState branch and overwrite the
|
|
2062
|
+
// landing history entry — eating the "back" target and making browser back
|
|
2063
|
+
// skip past the initial page.
|
|
2064
|
+
if (isFirstUrlSyncRef.current) {
|
|
2065
|
+
isFirstUrlSyncRef.current = false;
|
|
2066
|
+
lastUrlRef.current = newUrl;
|
|
2067
|
+
}
|
|
2068
|
+
if (switchWorkspacePushedRef.current) {
|
|
2069
|
+
// A workspace change may already have pushed the exact target URL. If we leave
|
|
2070
|
+
// this flag set, the next unrelated item navigation will incorrectly replace
|
|
2071
|
+
// history instead of pushing a new entry, which breaks browser back/forward.
|
|
2072
|
+
switchWorkspacePushedRef.current = false;
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
1738
2075
|
}, [
|
|
1739
2076
|
currentItemDescriptor,
|
|
1740
2077
|
viewName,
|
|
@@ -1746,6 +2083,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1746
2083
|
fullscreen,
|
|
1747
2084
|
currentWizardId,
|
|
1748
2085
|
openSidebars,
|
|
2086
|
+
showHelpTerminal,
|
|
2087
|
+
selectedHelpSectionId,
|
|
1749
2088
|
]);
|
|
1750
2089
|
useEffect(() => {
|
|
1751
2090
|
async function load() {
|
|
@@ -1842,12 +2181,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1842
2181
|
"en",
|
|
1843
2182
|
version: 0,
|
|
1844
2183
|
};
|
|
1845
|
-
const loadedItem = await itemsRepository.getItem(itemToLoad);
|
|
1846
|
-
if (!loadedItem) {
|
|
1847
|
-
return undefined;
|
|
1848
|
-
}
|
|
1849
2184
|
// ensure this is object has no additional properties
|
|
1850
2185
|
itemToLoad = getItemDescriptor(itemToLoad);
|
|
2186
|
+
const requestedItemKey = makeItemKey(itemToLoad);
|
|
1851
2187
|
// Check if item is already open in any slot - if so, reuse that slot instead of creating a new one
|
|
1852
2188
|
const existingSlotForItem = editorSlots.find((s) => s.itemDescriptor.id === itemToLoad.id &&
|
|
1853
2189
|
s.itemDescriptor.language === itemToLoad.language &&
|
|
@@ -1858,6 +2194,17 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1858
2194
|
!options?.openInNewSlot &&
|
|
1859
2195
|
!options?.targetSlotId) {
|
|
1860
2196
|
const isExistingSlotActive = existingSlotForItem.slotId === activeSlotIdRef.current;
|
|
2197
|
+
if (isExistingSlotActive) {
|
|
2198
|
+
latestGlobalLoadKeyRef.current = requestedItemKey;
|
|
2199
|
+
}
|
|
2200
|
+
const loadedItem = await itemsRepository.getItem(itemToLoad);
|
|
2201
|
+
if (!loadedItem) {
|
|
2202
|
+
return undefined;
|
|
2203
|
+
}
|
|
2204
|
+
if (isExistingSlotActive &&
|
|
2205
|
+
latestGlobalLoadKeyRef.current !== requestedItemKey) {
|
|
2206
|
+
return loadedItem;
|
|
2207
|
+
}
|
|
1861
2208
|
// If forceRefresh is true, update the slot with a new refreshToken to trigger a reload
|
|
1862
2209
|
if (options?.forceRefresh) {
|
|
1863
2210
|
setEditorSlots((prev) => {
|
|
@@ -1928,12 +2275,30 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1928
2275
|
return next;
|
|
1929
2276
|
});
|
|
1930
2277
|
// Decide whether this load should update the global "current item" state.
|
|
1931
|
-
// - If we're loading into an inactive slot
|
|
1932
|
-
// - If the slot is active
|
|
2278
|
+
// - If we're loading into an inactive slot we won't normally touch, do NOT update URL/tree/controls.
|
|
2279
|
+
// - If the slot is active, becoming active as the first slot, or is a brand-new
|
|
2280
|
+
// slot the caller explicitly opened, do update.
|
|
1933
2281
|
const isFirstSlot = editorSlots.length === 0;
|
|
1934
|
-
const
|
|
1935
|
-
|
|
1936
|
-
|
|
2282
|
+
const isExplicitNewSlot = options?.openInNewSlot === true && !options?.targetSlotId;
|
|
2283
|
+
const shouldUpdateGlobalCurrentItem = isFirstSlot ||
|
|
2284
|
+
isExplicitNewSlot ||
|
|
2285
|
+
targetSlotId === activeSlotIdRef.current;
|
|
2286
|
+
if (shouldUpdateGlobalCurrentItem) {
|
|
2287
|
+
latestGlobalLoadKeyRef.current = requestedItemKey;
|
|
2288
|
+
}
|
|
2289
|
+
const loadedItem = await itemsRepository.getItem(itemToLoad);
|
|
2290
|
+
if (!loadedItem) {
|
|
2291
|
+
return undefined;
|
|
2292
|
+
}
|
|
2293
|
+
if (shouldUpdateGlobalCurrentItem &&
|
|
2294
|
+
latestGlobalLoadKeyRef.current !== requestedItemKey) {
|
|
2295
|
+
return loadedItem;
|
|
2296
|
+
}
|
|
2297
|
+
// Activate the new slot when this is either the initial first slot, or the
|
|
2298
|
+
// caller explicitly opened the item in a new slot — focus follows the action so
|
|
2299
|
+
// the user immediately sees what they just opened. Beyond that, slot focus is
|
|
2300
|
+
// driven by mouse hover/click.
|
|
2301
|
+
if (isFirstSlot || isExplicitNewSlot) {
|
|
1937
2302
|
activeSlotIdRef.current = targetSlotId;
|
|
1938
2303
|
setActiveSlotId(targetSlotId);
|
|
1939
2304
|
}
|
|
@@ -2039,9 +2404,10 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2039
2404
|
useEffect(() => {
|
|
2040
2405
|
if (fullscreen &&
|
|
2041
2406
|
!searchParams.get("fullscreen") &&
|
|
2042
|
-
!configuration.forceFullscreen
|
|
2407
|
+
!configuration.forceFullscreen &&
|
|
2408
|
+
!isMobile)
|
|
2043
2409
|
setShowFullscreenHint(true);
|
|
2044
|
-
}, [fullscreen, configuration.forceFullscreen, searchParams]);
|
|
2410
|
+
}, [fullscreen, configuration.forceFullscreen, searchParams, isMobile]);
|
|
2045
2411
|
const state = {
|
|
2046
2412
|
page,
|
|
2047
2413
|
configuration,
|
|
@@ -2140,20 +2506,35 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2140
2506
|
? existingOp.progress
|
|
2141
2507
|
: op.progress;
|
|
2142
2508
|
// IMPORTANT: Once canUndo becomes true, never downgrade it to false,
|
|
2143
|
-
// UNLESS this update indicates the operation was undone.
|
|
2144
|
-
//
|
|
2145
|
-
//
|
|
2146
|
-
|
|
2509
|
+
// UNLESS this update indicates the operation was undone.
|
|
2510
|
+
// Runtime logs show long-running undo completion can arrive as:
|
|
2511
|
+
// - existing: canUndo=true, executionStatus=executing
|
|
2512
|
+
// - incoming: canUndo=false, executionStatus=completed
|
|
2513
|
+
// without explicit undone/canRedo flags yet.
|
|
2514
|
+
const isExplicitUndoUpdate = op.undone === true || op.canRedo === true;
|
|
2515
|
+
const isInferredUndoCompletion = existingOp.executionStatus === "executing" &&
|
|
2516
|
+
op.executionStatus === "completed" &&
|
|
2517
|
+
existingOp.canUndo === true &&
|
|
2518
|
+
op.canUndo === false;
|
|
2519
|
+
const isUndoUpdate = isExplicitUndoUpdate || isInferredUndoCompletion;
|
|
2147
2520
|
const mergedCanUndo = isUndoUpdate
|
|
2148
2521
|
? false
|
|
2149
2522
|
: existingOp.canUndo === true
|
|
2150
2523
|
? true
|
|
2151
2524
|
: op.canUndo;
|
|
2525
|
+
const mergedCanRedo = isUndoUpdate
|
|
2526
|
+
? true
|
|
2527
|
+
: op.canRedo ?? existingOp.canRedo;
|
|
2528
|
+
const mergedUndone = isUndoUpdate
|
|
2529
|
+
? true
|
|
2530
|
+
: op.undone ?? existingOp.undone;
|
|
2152
2531
|
const mergedOp = {
|
|
2153
2532
|
...existingOp,
|
|
2154
2533
|
...op,
|
|
2155
2534
|
progress: mergedProgress,
|
|
2156
2535
|
canUndo: mergedCanUndo,
|
|
2536
|
+
canRedo: mergedCanRedo,
|
|
2537
|
+
undone: mergedUndone,
|
|
2157
2538
|
};
|
|
2158
2539
|
// Ensure undone operations always have canRedo: true.
|
|
2159
2540
|
if (mergedOp.undone && !mergedOp.canRedo) {
|
|
@@ -2207,10 +2588,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2207
2588
|
});
|
|
2208
2589
|
// Ref for markOperationComplete callback (needed because operationsContext is created later)
|
|
2209
2590
|
const markOperationCompleteRef = useRef(null);
|
|
2210
|
-
// When the websocket is reconnecting, we can briefly lose the ability to send messages.
|
|
2211
|
-
// Agent dialog responses (e.g. questionnaire cancel/submit) must not be dropped, otherwise
|
|
2212
|
-
// the backend tool call can remain pending and tests/users will hang.
|
|
2213
|
-
const pendingAgentDialogResponsesRef = useRef([]);
|
|
2214
2591
|
// WebSocket message handler and connection
|
|
2215
2592
|
const messageHandler = useSocketMessageHandler({
|
|
2216
2593
|
sessionId,
|
|
@@ -2236,28 +2613,31 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2236
2613
|
markOperationCompleteRef.current?.(operationId);
|
|
2237
2614
|
},
|
|
2238
2615
|
});
|
|
2616
|
+
// Concurrent user limit error state
|
|
2617
|
+
const [concurrentUserLimitError, setConcurrentUserLimitError] = useState(null);
|
|
2618
|
+
concurrentUserLimitErrorRef.current = concurrentUserLimitError;
|
|
2619
|
+
const handleRetryConnection = useCallback(() => {
|
|
2620
|
+
setConcurrentUserLimitError(null);
|
|
2621
|
+
// Force reconnection by triggering a new connection attempt
|
|
2622
|
+
// The useEditorWebSocket hook will check availability again
|
|
2623
|
+
const socket = globalThis.editorSocket;
|
|
2624
|
+
if (socket) {
|
|
2625
|
+
socket.close();
|
|
2626
|
+
delete globalThis.editorSocket;
|
|
2627
|
+
}
|
|
2628
|
+
// The hook will automatically attempt to reconnect
|
|
2629
|
+
}, []);
|
|
2239
2630
|
const { socketRef: socketInstanceRef } = useEditorWebSocket({
|
|
2240
2631
|
sessionId,
|
|
2241
2632
|
onMessage: messageHandler,
|
|
2242
2633
|
onOpen: async () => {
|
|
2634
|
+
// Clear concurrent user limit error on successful connection
|
|
2635
|
+
setConcurrentUserLimitError(null);
|
|
2636
|
+
// Startup WebSocket probe may have failed with a blocking error while seats were full;
|
|
2637
|
+
// re-run status checks quietly so "1 error" does not stick after a successful connect.
|
|
2638
|
+
void startupChecks.recheckQuiet();
|
|
2243
2639
|
// Increment socket connection version to trigger re-subscriptions
|
|
2244
2640
|
setSocketConnectionVersion((v) => v + 1);
|
|
2245
|
-
// Flush any queued agent dialog responses now that the socket is open.
|
|
2246
|
-
// Keep this early so pending tool calls unblock ASAP.
|
|
2247
|
-
try {
|
|
2248
|
-
if (socketInstanceRef.current &&
|
|
2249
|
-
socketInstanceRef.current.readyState === WebSocket.OPEN &&
|
|
2250
|
-
pendingAgentDialogResponsesRef.current.length > 0) {
|
|
2251
|
-
// FIFO flush
|
|
2252
|
-
while (pendingAgentDialogResponsesRef.current.length > 0) {
|
|
2253
|
-
const queued = pendingAgentDialogResponsesRef.current.shift();
|
|
2254
|
-
socketInstanceRef.current.send(JSON.stringify(queued));
|
|
2255
|
-
}
|
|
2256
|
-
}
|
|
2257
|
-
}
|
|
2258
|
-
catch (e) {
|
|
2259
|
-
console.error("Failed to flush queued agent dialog responses:", e);
|
|
2260
|
-
}
|
|
2261
2641
|
// Fetch any running operations on (re)connect for auto-resume
|
|
2262
2642
|
// This ensures the UI shows operations that are still executing
|
|
2263
2643
|
try {
|
|
@@ -2273,24 +2653,37 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2273
2653
|
}
|
|
2274
2654
|
},
|
|
2275
2655
|
onError: (error) => console.error("WebSocket error:", error),
|
|
2656
|
+
onConcurrentUserLimit: (error) => {
|
|
2657
|
+
setConcurrentUserLimitError(error);
|
|
2658
|
+
},
|
|
2659
|
+
onSessionRevoked: () => promptSessionReconnect("session-revoked"),
|
|
2276
2660
|
connectSocket,
|
|
2277
2661
|
requestQuota,
|
|
2278
2662
|
sendClientInfo,
|
|
2663
|
+
setSocketDiagnostics,
|
|
2279
2664
|
});
|
|
2665
|
+
useEffect(() => {
|
|
2666
|
+
const hasMySession = activeSessions.some((s) => s.sessionId === sessionId);
|
|
2667
|
+
if (hasMySession &&
|
|
2668
|
+
socketInstanceRef.current?.readyState === WebSocket.OPEN) {
|
|
2669
|
+
toast.dismiss("session-revoked");
|
|
2670
|
+
}
|
|
2671
|
+
}, [activeSessions, sessionId, socketConnectionVersion, socketInstanceRef]);
|
|
2672
|
+
useEffect(() => {
|
|
2673
|
+
const handleRevoked = (event) => {
|
|
2674
|
+
const customEvent = event;
|
|
2675
|
+
promptSessionReconnect(customEvent?.detail?.reason);
|
|
2676
|
+
};
|
|
2677
|
+
window.addEventListener("parhelia:session-revoked", handleRevoked);
|
|
2678
|
+
return () => {
|
|
2679
|
+
window.removeEventListener("parhelia:session-revoked", handleRevoked);
|
|
2680
|
+
};
|
|
2681
|
+
}, [promptSessionReconnect]);
|
|
2280
2682
|
const sendSocketMessage = useCallback((message) => {
|
|
2281
2683
|
if (socketInstanceRef.current &&
|
|
2282
2684
|
socketInstanceRef.current.readyState === WebSocket.OPEN) {
|
|
2283
2685
|
socketInstanceRef.current.send(JSON.stringify(message));
|
|
2284
2686
|
}
|
|
2285
|
-
else if (message.type === "agent-dialog-response") {
|
|
2286
|
-
// Queue dialog responses to avoid losing them during reconnects.
|
|
2287
|
-
pendingAgentDialogResponsesRef.current.push(message);
|
|
2288
|
-
// Prevent unbounded growth in pathological scenarios.
|
|
2289
|
-
if (pendingAgentDialogResponsesRef.current.length > 50) {
|
|
2290
|
-
pendingAgentDialogResponsesRef.current =
|
|
2291
|
-
pendingAgentDialogResponsesRef.current.slice(-50);
|
|
2292
|
-
}
|
|
2293
|
-
}
|
|
2294
2687
|
}, [socketInstanceRef]);
|
|
2295
2688
|
// URL update helper - defined early so it can be used by workspace/sidebar functions
|
|
2296
2689
|
const updateUrl = useCallback((params) => {
|
|
@@ -2309,7 +2702,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2309
2702
|
? `${browserPathname}?${queryString}`
|
|
2310
2703
|
: browserPathname;
|
|
2311
2704
|
if (typeof window !== "undefined") {
|
|
2312
|
-
window.history.pushState(
|
|
2705
|
+
window.history.pushState(getCurrentHistoryState(), "", newUrl);
|
|
2706
|
+
lastUrlRef.current = newUrl;
|
|
2313
2707
|
}
|
|
2314
2708
|
else {
|
|
2315
2709
|
router.push(newUrl, { scroll: false });
|
|
@@ -2334,8 +2728,13 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2334
2728
|
if (!options?.skipNavigationHistory) {
|
|
2335
2729
|
addNavigationEntry(targetWorkspaceId, item);
|
|
2336
2730
|
}
|
|
2337
|
-
//
|
|
2338
|
-
|
|
2731
|
+
// Mark that we're pushing from switchWorkspace so the URL sync effect
|
|
2732
|
+
// will replaceState instead of pushing a second history entry.
|
|
2733
|
+
switchWorkspacePushedRef.current = true;
|
|
2734
|
+
updateUrl({
|
|
2735
|
+
workspace: targetWorkspaceId,
|
|
2736
|
+
...options?.urlParams,
|
|
2737
|
+
});
|
|
2339
2738
|
if (typeof document.startViewTransition === "function") {
|
|
2340
2739
|
document.startViewTransition(() => {
|
|
2341
2740
|
flushSync(() => {
|
|
@@ -2355,14 +2754,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2355
2754
|
updateUrl,
|
|
2356
2755
|
handleSetShowAgentsPanel,
|
|
2357
2756
|
]);
|
|
2358
|
-
// Legacy alias for backwards compatibility
|
|
2359
|
-
const switchView = useCallback((viewName, options) => {
|
|
2360
|
-
// Handle ccpanel for settings workspace
|
|
2361
|
-
if (options?.ccpanel) {
|
|
2362
|
-
updateUrl({ ccpanel: options.ccpanel, workspace: viewName });
|
|
2363
|
-
}
|
|
2364
|
-
switchWorkspace(viewName, options);
|
|
2365
|
-
}, [switchWorkspace, updateUrl]);
|
|
2366
2757
|
// Helper: get all sidebar IDs that should be preserved (locked sidebars + their stack mates)
|
|
2367
2758
|
const getPreservedSidebarIds = useCallback(() => {
|
|
2368
2759
|
const lockedSet = new Set(lockedSidebarsRef.current);
|
|
@@ -2400,6 +2791,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2400
2791
|
setOpenSidebars(newSidebars);
|
|
2401
2792
|
setLockedSidebars((locked) => locked.filter((id) => id !== sidebarId));
|
|
2402
2793
|
setSidebarStacks((prevStacks) => normalizeSidebarStacks(newSidebars, prevStacks));
|
|
2794
|
+
if (sidebarId === "agents-panel") {
|
|
2795
|
+
setUserPreferences({ showAgentsPanel: false });
|
|
2796
|
+
}
|
|
2403
2797
|
startTransition(() => {
|
|
2404
2798
|
updateUrl({ sidebar: newSidebars.join(",") || undefined });
|
|
2405
2799
|
});
|
|
@@ -2414,34 +2808,69 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2414
2808
|
];
|
|
2415
2809
|
openSidebarsRef.current = newSidebars;
|
|
2416
2810
|
setOpenSidebars(newSidebars);
|
|
2811
|
+
ensureSidebarPinned(sidebarId);
|
|
2417
2812
|
setSidebarStacks((prevStacks) => normalizeSidebarStacks(newSidebars, prevStacks));
|
|
2813
|
+
if (sidebarId === "agents-panel") {
|
|
2814
|
+
setUserPreferences({ showAgentsPanel: true });
|
|
2815
|
+
}
|
|
2816
|
+
// On mobile, close the editor form panel when opening a sidebar
|
|
2817
|
+
if (isMobile) {
|
|
2818
|
+
setMobileEditorPanelOpenRaw(false);
|
|
2819
|
+
}
|
|
2418
2820
|
startTransition(() => {
|
|
2419
2821
|
updateUrl({ sidebar: newSidebars.join(",") || undefined });
|
|
2420
2822
|
});
|
|
2421
|
-
}, [
|
|
2823
|
+
}, [
|
|
2824
|
+
updateUrl,
|
|
2825
|
+
getPreservedSidebarIds,
|
|
2826
|
+
normalizeSidebarStacks,
|
|
2827
|
+
ensureSidebarPinned,
|
|
2828
|
+
setUserPreferences,
|
|
2829
|
+
isMobile,
|
|
2830
|
+
]);
|
|
2422
2831
|
// Ensure a sidebar is open (without toggling it closed if already open)
|
|
2423
|
-
const openSidebar = useCallback((sidebarId) => {
|
|
2832
|
+
const openSidebar = useCallback((sidebarId, options) => {
|
|
2424
2833
|
const currentOpenSidebars = openSidebarsRef.current;
|
|
2425
2834
|
if (currentOpenSidebars.includes(sidebarId)) {
|
|
2426
2835
|
// Already open, nothing to do
|
|
2427
2836
|
return;
|
|
2428
2837
|
}
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2838
|
+
const preservedSet = options?.preserveOpenSidebars
|
|
2839
|
+
? undefined
|
|
2840
|
+
: getPreservedSidebarIds();
|
|
2841
|
+
const newSidebars = options?.preserveOpenSidebars
|
|
2842
|
+
? [...currentOpenSidebars, sidebarId]
|
|
2843
|
+
: [
|
|
2844
|
+
...currentOpenSidebars.filter((id) => preservedSet?.has(id)),
|
|
2845
|
+
sidebarId,
|
|
2846
|
+
];
|
|
2435
2847
|
openSidebarsRef.current = newSidebars;
|
|
2436
2848
|
setOpenSidebars(newSidebars);
|
|
2849
|
+
ensureSidebarPinned(sidebarId);
|
|
2437
2850
|
setSidebarStacks((prevStacks) => normalizeSidebarStacks(newSidebars, prevStacks));
|
|
2851
|
+
if (sidebarId === "agents-panel") {
|
|
2852
|
+
setUserPreferences({ showAgentsPanel: true });
|
|
2853
|
+
}
|
|
2854
|
+
// On mobile, close the editor form panel when opening a sidebar
|
|
2855
|
+
if (isMobile) {
|
|
2856
|
+
setMobileEditorPanelOpenRaw(false);
|
|
2857
|
+
}
|
|
2438
2858
|
startTransition(() => {
|
|
2439
2859
|
updateUrl({ sidebar: newSidebars.join(",") || undefined });
|
|
2440
2860
|
});
|
|
2441
|
-
}, [
|
|
2861
|
+
}, [
|
|
2862
|
+
updateUrl,
|
|
2863
|
+
getPreservedSidebarIds,
|
|
2864
|
+
normalizeSidebarStacks,
|
|
2865
|
+
ensureSidebarPinned,
|
|
2866
|
+
setUserPreferences,
|
|
2867
|
+
isMobile,
|
|
2868
|
+
]);
|
|
2442
2869
|
// Toggle lock state for a sidebar stack (keeps it visible when selecting another)
|
|
2443
2870
|
// When toggling any sidebar, we toggle the entire stack it belongs to
|
|
2444
2871
|
const toggleSidebarLock = useCallback((sidebarId) => {
|
|
2872
|
+
if (isMobile)
|
|
2873
|
+
return;
|
|
2445
2874
|
const currentStacks = sidebarStacksRef.current;
|
|
2446
2875
|
const stackContainingSidebar = currentStacks.find((stack) => stack.includes(sidebarId));
|
|
2447
2876
|
const sidebarIdsToToggle = stackContainingSidebar || [sidebarId];
|
|
@@ -2459,7 +2888,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2459
2888
|
];
|
|
2460
2889
|
}
|
|
2461
2890
|
});
|
|
2462
|
-
}, []);
|
|
2891
|
+
}, [isMobile]);
|
|
2463
2892
|
const stackSidebar = useCallback((sidebarId, targetSidebarId, position = "after") => {
|
|
2464
2893
|
if (!sidebarId || !targetSidebarId || sidebarId === targetSidebarId)
|
|
2465
2894
|
return;
|
|
@@ -2491,6 +2920,38 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2491
2920
|
return normalizeSidebarStacks(openIds, next);
|
|
2492
2921
|
});
|
|
2493
2922
|
}, [normalizeSidebarStacks]);
|
|
2923
|
+
const moveSidebarToColumn = useCallback((sidebarId, targetSidebarId, position = "after") => {
|
|
2924
|
+
if (!sidebarId || !targetSidebarId || sidebarId === targetSidebarId) {
|
|
2925
|
+
return;
|
|
2926
|
+
}
|
|
2927
|
+
const currentOpen = openSidebarsRef.current;
|
|
2928
|
+
const baseOpen = currentOpen.filter((id) => id !== sidebarId);
|
|
2929
|
+
const targetIndex = baseOpen.indexOf(targetSidebarId);
|
|
2930
|
+
if (targetIndex === -1) {
|
|
2931
|
+
return;
|
|
2932
|
+
}
|
|
2933
|
+
const insertIndex = position === "before" ? targetIndex : targetIndex + 1;
|
|
2934
|
+
const nextOpen = [...baseOpen];
|
|
2935
|
+
nextOpen.splice(insertIndex, 0, sidebarId);
|
|
2936
|
+
openSidebarsRef.current = nextOpen;
|
|
2937
|
+
setOpenSidebars(nextOpen);
|
|
2938
|
+
startTransition(() => {
|
|
2939
|
+
updateUrl({ sidebar: nextOpen.join(",") || undefined });
|
|
2940
|
+
});
|
|
2941
|
+
setSidebarStacks((prev) => {
|
|
2942
|
+
const normalized = normalizeSidebarStacks(nextOpen, prev);
|
|
2943
|
+
const next = normalized
|
|
2944
|
+
.map((stack) => stack.filter((id) => id !== sidebarId))
|
|
2945
|
+
.filter((stack) => stack.length > 0);
|
|
2946
|
+
const targetStackIndex = next.findIndex((stack) => stack.includes(targetSidebarId));
|
|
2947
|
+
if (targetStackIndex === -1) {
|
|
2948
|
+
next.push([sidebarId]);
|
|
2949
|
+
return normalizeSidebarStacks(nextOpen, next);
|
|
2950
|
+
}
|
|
2951
|
+
next.splice(position === "before" ? targetStackIndex : targetStackIndex + 1, 0, [sidebarId]);
|
|
2952
|
+
return normalizeSidebarStacks(nextOpen, next);
|
|
2953
|
+
});
|
|
2954
|
+
}, [normalizeSidebarStacks, updateUrl]);
|
|
2494
2955
|
const unstackSidebar = useCallback((sidebarId) => {
|
|
2495
2956
|
setSidebarStacks((prev) => {
|
|
2496
2957
|
const openIds = openSidebarsRef.current;
|
|
@@ -2536,8 +2997,34 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2536
2997
|
// Get resolved sidebar (with panels materialized)
|
|
2537
2998
|
// Note: This is defined as a function that will be called later when editContext is available
|
|
2538
2999
|
const sidebars = configuration.editor.sidebars ?? [];
|
|
3000
|
+
const taskboardSidebarIds = new Set([
|
|
3001
|
+
"taskboard-project-list",
|
|
3002
|
+
"taskboard-my-tasks",
|
|
3003
|
+
]);
|
|
3004
|
+
const getSidebarsForWorkspace = useCallback((targetWorkspaceId) => {
|
|
3005
|
+
const isTaskboardWorkspace = targetWorkspaceId === "taskboard";
|
|
3006
|
+
const workspaceAllowedSidebarIds = userInfo.workspaces?.find((w) => w.id === targetWorkspaceId)
|
|
3007
|
+
?.sidebars ?? [];
|
|
3008
|
+
return sidebars.filter((s) => {
|
|
3009
|
+
const isTaskboardSidebar = taskboardSidebarIds.has(s.id);
|
|
3010
|
+
if (isTaskboardWorkspace && !isTaskboardSidebar)
|
|
3011
|
+
return false;
|
|
3012
|
+
if (!isTaskboardWorkspace && isTaskboardSidebar)
|
|
3013
|
+
return false;
|
|
3014
|
+
// Always show agents-panel regardless of workspace settings.
|
|
3015
|
+
if (s.id === "agents-panel") {
|
|
3016
|
+
return true;
|
|
3017
|
+
}
|
|
3018
|
+
// If no workspace settings or no sidebars defined for current workspace, show all.
|
|
3019
|
+
if (workspaceAllowedSidebarIds.length === 0) {
|
|
3020
|
+
return true;
|
|
3021
|
+
}
|
|
3022
|
+
// Only show sidebars that are in the allowed list for the current workspace.
|
|
3023
|
+
return workspaceAllowedSidebarIds.includes(s.id);
|
|
3024
|
+
});
|
|
3025
|
+
}, [sidebars, userInfo.workspaces]);
|
|
2539
3026
|
const getResolvedSidebar = useCallback((sidebarId) => {
|
|
2540
|
-
const sidebar =
|
|
3027
|
+
const sidebar = getSidebarsForWorkspace(workspaceId).find((s) => s.id === sidebarId);
|
|
2541
3028
|
if (!sidebar)
|
|
2542
3029
|
return undefined;
|
|
2543
3030
|
// Resolve panel factories using editContextRef to avoid circular dependency
|
|
@@ -2551,7 +3038,35 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2551
3038
|
...sidebar,
|
|
2552
3039
|
panels: resolvedPanels,
|
|
2553
3040
|
};
|
|
2554
|
-
}, [
|
|
3041
|
+
}, [getSidebarsForWorkspace, workspaceId]);
|
|
3042
|
+
useEffect(() => {
|
|
3043
|
+
if (!currentWorkspace.supportsSidebars) {
|
|
3044
|
+
return;
|
|
3045
|
+
}
|
|
3046
|
+
const allowedIds = new Set(getSidebarsForWorkspace(workspaceId).map((sidebar) => sidebar.id));
|
|
3047
|
+
const currentOpen = openSidebarsRef.current;
|
|
3048
|
+
let nextOpen = currentOpen.filter((id) => allowedIds.has(id));
|
|
3049
|
+
if (nextOpen.length === 0 && currentWorkspace.defaultSidebars?.length) {
|
|
3050
|
+
nextOpen = currentWorkspace.defaultSidebars.filter((id) => allowedIds.has(id));
|
|
3051
|
+
}
|
|
3052
|
+
const unchanged = nextOpen.length === currentOpen.length &&
|
|
3053
|
+
nextOpen.every((id, index) => id === currentOpen[index]);
|
|
3054
|
+
if (unchanged) {
|
|
3055
|
+
return;
|
|
3056
|
+
}
|
|
3057
|
+
openSidebarsRef.current = nextOpen;
|
|
3058
|
+
setOpenSidebars(nextOpen);
|
|
3059
|
+
setSidebarStacks((prev) => normalizeSidebarStacks(nextOpen, prev));
|
|
3060
|
+
startTransition(() => {
|
|
3061
|
+
updateUrl({ sidebar: nextOpen.join(",") || undefined });
|
|
3062
|
+
});
|
|
3063
|
+
}, [
|
|
3064
|
+
currentWorkspace,
|
|
3065
|
+
getSidebarsForWorkspace,
|
|
3066
|
+
normalizeSidebarStacks,
|
|
3067
|
+
updateUrl,
|
|
3068
|
+
workspaceId,
|
|
3069
|
+
]);
|
|
2555
3070
|
// Listen for switch-workspace and open-sidebar commands from agents via websocket
|
|
2556
3071
|
useEffect(() => {
|
|
2557
3072
|
const handleAgentMessage = (message) => {
|
|
@@ -2765,9 +3280,45 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2765
3280
|
}
|
|
2766
3281
|
return true;
|
|
2767
3282
|
}, [operations, ignoreBlur, sessionId]);
|
|
3283
|
+
const quickSwitcherEntries = useMemo(() => {
|
|
3284
|
+
const entries = [];
|
|
3285
|
+
const seen = new Set();
|
|
3286
|
+
const pushEntry = (entry) => {
|
|
3287
|
+
const key = entry.item
|
|
3288
|
+
? `${entry.workspaceId}:${entry.item.id}:${entry.item.language}:${entry.item.version}`
|
|
3289
|
+
: `${entry.workspaceId}:no-item`;
|
|
3290
|
+
if (seen.has(key))
|
|
3291
|
+
return;
|
|
3292
|
+
seen.add(key);
|
|
3293
|
+
entries.push(entry);
|
|
3294
|
+
};
|
|
3295
|
+
for (const entry of navigationHistory) {
|
|
3296
|
+
pushEntry(entry);
|
|
3297
|
+
}
|
|
3298
|
+
for (const entry of browseHistory) {
|
|
3299
|
+
if (!entry.id || !entry.language)
|
|
3300
|
+
continue;
|
|
3301
|
+
pushEntry({
|
|
3302
|
+
workspaceId,
|
|
3303
|
+
item: {
|
|
3304
|
+
id: entry.id,
|
|
3305
|
+
language: entry.language,
|
|
3306
|
+
version: entry.version ?? 0,
|
|
3307
|
+
},
|
|
3308
|
+
timestamp: entry.visitedAt
|
|
3309
|
+
? new Date(entry.visitedAt).getTime()
|
|
3310
|
+
: Date.now(),
|
|
3311
|
+
displayName: entry.name || workspaceId,
|
|
3312
|
+
itemName: entry.name,
|
|
3313
|
+
itemPath: entry.path,
|
|
3314
|
+
itemIcon: entry.icon,
|
|
3315
|
+
});
|
|
3316
|
+
}
|
|
3317
|
+
return entries.slice(0, 25);
|
|
3318
|
+
}, [navigationHistory, browseHistory, workspaceId]);
|
|
2768
3319
|
// Quick switcher handlers
|
|
2769
3320
|
const showQuickSwitcher = useCallback((show) => {
|
|
2770
|
-
if (show &&
|
|
3321
|
+
if (show && quickSwitcherEntries.length > 1) {
|
|
2771
3322
|
setQuickSwitcherVisible(true);
|
|
2772
3323
|
// Start with index 1 (second entry - previous entry) for quick switching
|
|
2773
3324
|
setQuickSwitcherSelectedIndex(1);
|
|
@@ -2775,11 +3326,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2775
3326
|
else {
|
|
2776
3327
|
setQuickSwitcherVisible(false);
|
|
2777
3328
|
}
|
|
2778
|
-
}, [
|
|
3329
|
+
}, [quickSwitcherEntries]);
|
|
2779
3330
|
const cycleQuickSwitcher = useCallback((direction) => {
|
|
2780
3331
|
if (!quickSwitcherVisible)
|
|
2781
3332
|
return;
|
|
2782
|
-
const maxItems = Math.min(5,
|
|
3333
|
+
const maxItems = Math.min(5, quickSwitcherEntries.length);
|
|
2783
3334
|
setQuickSwitcherSelectedIndex((current) => {
|
|
2784
3335
|
let newIndex = current;
|
|
2785
3336
|
// Determine grid layout (responsive columns)
|
|
@@ -2824,9 +3375,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2824
3375
|
}
|
|
2825
3376
|
return newIndex;
|
|
2826
3377
|
});
|
|
2827
|
-
}, [quickSwitcherVisible,
|
|
3378
|
+
}, [quickSwitcherVisible, quickSwitcherEntries]);
|
|
2828
3379
|
const handleQuickSwitcherSelect = useCallback((index) => {
|
|
2829
|
-
const selectedEntry =
|
|
3380
|
+
const selectedEntry = quickSwitcherEntries[index];
|
|
2830
3381
|
if (selectedEntry) {
|
|
2831
3382
|
// Determine target workspace: entries with items should go to "editor" workspace
|
|
2832
3383
|
// (fixes issue where browse history entries initialized with wrong workspaceId)
|
|
@@ -2878,23 +3429,46 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2878
3429
|
}
|
|
2879
3430
|
setQuickSwitcherVisible(false);
|
|
2880
3431
|
}, [
|
|
2881
|
-
|
|
3432
|
+
quickSwitcherEntries,
|
|
2882
3433
|
loadItem,
|
|
2883
3434
|
switchWorkspace,
|
|
2884
3435
|
workspaceId,
|
|
2885
3436
|
item,
|
|
2886
3437
|
setNavigationHistory,
|
|
2887
3438
|
]);
|
|
3439
|
+
useEffect(() => {
|
|
3440
|
+
if (typeof window === "undefined" ||
|
|
3441
|
+
process.env.PARHELIA_DEV_MODE !== "true") {
|
|
3442
|
+
return;
|
|
3443
|
+
}
|
|
3444
|
+
window.__parheliaQuickSwitcherTestApi = {
|
|
3445
|
+
open: () => showQuickSwitcher(true),
|
|
3446
|
+
cycle: (direction = "next") => cycleQuickSwitcher(direction),
|
|
3447
|
+
closeWithoutSelection: () => setQuickSwitcherVisible(false),
|
|
3448
|
+
selectCurrent: () => handleQuickSwitcherSelect(quickSwitcherSelectedIndex),
|
|
3449
|
+
getState: () => ({
|
|
3450
|
+
visible: quickSwitcherVisible,
|
|
3451
|
+
selectedIndex: quickSwitcherSelectedIndex,
|
|
3452
|
+
entryCount: quickSwitcherEntries.length,
|
|
3453
|
+
entries: quickSwitcherEntries.map((entry) => ({
|
|
3454
|
+
workspaceId: entry.workspaceId,
|
|
3455
|
+
itemId: entry.item?.id,
|
|
3456
|
+
displayName: entry.displayName,
|
|
3457
|
+
})),
|
|
3458
|
+
}),
|
|
3459
|
+
};
|
|
3460
|
+
return () => {
|
|
3461
|
+
delete window.__parheliaQuickSwitcherTestApi;
|
|
3462
|
+
};
|
|
3463
|
+
}, [
|
|
3464
|
+
showQuickSwitcher,
|
|
3465
|
+
cycleQuickSwitcher,
|
|
3466
|
+
handleQuickSwitcherSelect,
|
|
3467
|
+
quickSwitcherSelectedIndex,
|
|
3468
|
+
]);
|
|
2888
3469
|
const { handleKeyDown } = useKeyboardNavigation({
|
|
2889
3470
|
editContextRef,
|
|
2890
|
-
|
|
2891
|
-
pageViewContext: activePageViewContext,
|
|
2892
|
-
configuration,
|
|
2893
|
-
item,
|
|
2894
|
-
browseHistory,
|
|
2895
|
-
loadItem,
|
|
2896
|
-
showInfoToast,
|
|
2897
|
-
showErrorToast,
|
|
3471
|
+
keyboardCommands: activeKeyboardCommands,
|
|
2898
3472
|
executeCommand,
|
|
2899
3473
|
showQuickSwitcher,
|
|
2900
3474
|
cycleQuickSwitcher,
|
|
@@ -3014,10 +3588,19 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3014
3588
|
}
|
|
3015
3589
|
return null;
|
|
3016
3590
|
};
|
|
3591
|
+
let modifierWasPressedOnMouseDown = false;
|
|
3592
|
+
const handleMouseDown = (event) => {
|
|
3593
|
+
modifierWasPressedOnMouseDown = event.ctrlKey || event.metaKey;
|
|
3594
|
+
};
|
|
3017
3595
|
const handleCtrlClick = async (event) => {
|
|
3018
|
-
// Only proceed if Ctrl
|
|
3596
|
+
// Only proceed if Ctrl/Cmd was already pressed when the mouse interaction started.
|
|
3597
|
+
// This avoids accidental navigation when users press Ctrl after selecting text to copy.
|
|
3598
|
+
if (!modifierWasPressedOnMouseDown)
|
|
3599
|
+
return;
|
|
3600
|
+
// Also require the modifier to still be held for the final click event.
|
|
3019
3601
|
if (!event.ctrlKey && !event.metaKey)
|
|
3020
3602
|
return;
|
|
3603
|
+
modifierWasPressedOnMouseDown = false;
|
|
3021
3604
|
const target = event.target;
|
|
3022
3605
|
const text = getTextFromElement(target);
|
|
3023
3606
|
if (text && isGuid(text)) {
|
|
@@ -3032,8 +3615,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3032
3615
|
skipViewChange: true,
|
|
3033
3616
|
});
|
|
3034
3617
|
if (item) {
|
|
3035
|
-
|
|
3036
|
-
switchView("editor", {
|
|
3618
|
+
switchWorkspace("editor", {
|
|
3037
3619
|
skipNavigationHistory: true,
|
|
3038
3620
|
});
|
|
3039
3621
|
showInfoToast({
|
|
@@ -3059,12 +3641,14 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3059
3641
|
}
|
|
3060
3642
|
};
|
|
3061
3643
|
if (typeof document !== "undefined") {
|
|
3644
|
+
document.addEventListener("mousedown", handleMouseDown, true);
|
|
3062
3645
|
document.addEventListener("click", handleCtrlClick, true);
|
|
3063
3646
|
return () => {
|
|
3647
|
+
document.removeEventListener("mousedown", handleMouseDown, true);
|
|
3064
3648
|
document.removeEventListener("click", handleCtrlClick, true);
|
|
3065
3649
|
};
|
|
3066
3650
|
}
|
|
3067
|
-
}, [loadItem,
|
|
3651
|
+
}, [loadItem, switchWorkspace, showInfoToast, showErrorToast]);
|
|
3068
3652
|
useEffect(() => {
|
|
3069
3653
|
const handleGlobalBlur = () => {
|
|
3070
3654
|
operations.onFieldBlur?.();
|
|
@@ -3102,21 +3686,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3102
3686
|
// Otherwise, only show workspaces that are in the settings
|
|
3103
3687
|
return workspaceIdsFromSettings.includes(w.id) && !w.visible;
|
|
3104
3688
|
});
|
|
3105
|
-
// Get sidebars allowed for the current workspace from settings
|
|
3106
|
-
const currentWorkspaceSettings = userInfo.workspaces?.find((w) => w.id === workspaceId);
|
|
3107
|
-
const allowedSidebarIds = currentWorkspaceSettings?.sidebars ?? [];
|
|
3108
|
-
// Legacy: Calculate visible views for backwards compatibility
|
|
3109
|
-
const allViews = (configuration.editor.views ?? [])
|
|
3110
|
-
.filter((x) => {
|
|
3111
|
-
return !x.visible && !x.hidden;
|
|
3112
|
-
})
|
|
3113
|
-
.filter((x) => !userInfo.views ||
|
|
3114
|
-
userInfo.views.map((view) => view.name).includes(x.name));
|
|
3115
|
-
const pinnedViews = userInfo.preferences?.pinnedViews ||
|
|
3116
|
-
configuration.editor.defaultPinnedViews ||
|
|
3117
|
-
[];
|
|
3118
|
-
// Legacy visibleViews for backwards compatibility
|
|
3119
|
-
const visibleViews = allViews.filter((view) => view.name === viewName || pinnedViews.includes(view.name));
|
|
3120
3689
|
// Handle initial mode setup from URL (only on initial load)
|
|
3121
3690
|
useEffect(() => {
|
|
3122
3691
|
if (!isInitialLoad)
|
|
@@ -3161,7 +3730,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3161
3730
|
// This is especially important when called from the tour, where the current workspace
|
|
3162
3731
|
// might be the editor and URL-only navigation would get "corrected" back.
|
|
3163
3732
|
try {
|
|
3164
|
-
|
|
3733
|
+
switchWorkspace("home", {
|
|
3165
3734
|
skipConfirmation: true,
|
|
3166
3735
|
skipNavigationHistory: true,
|
|
3167
3736
|
});
|
|
@@ -3208,7 +3777,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3208
3777
|
const current = new URLSearchParams(searchParams.toString());
|
|
3209
3778
|
current.delete("version");
|
|
3210
3779
|
current.delete("itemid");
|
|
3211
|
-
current.delete("view"); // Remove legacy param
|
|
3212
3780
|
current.delete("workspace"); // Clear workspace
|
|
3213
3781
|
current.set("create", "1");
|
|
3214
3782
|
const newUrl = `${pathname}?${current.toString()}`;
|
|
@@ -3488,6 +4056,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3488
4056
|
setShowOnlyMyChanges,
|
|
3489
4057
|
filterByCurrentLanguage,
|
|
3490
4058
|
setFilterByCurrentLanguage,
|
|
4059
|
+
historySearchQuery,
|
|
4060
|
+
setHistorySearchQuery,
|
|
3491
4061
|
refreshHistory,
|
|
3492
4062
|
isRefreshing,
|
|
3493
4063
|
activeSessions,
|
|
@@ -3525,19 +4095,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3525
4095
|
workspaceId,
|
|
3526
4096
|
previousWorkspaceId,
|
|
3527
4097
|
switchWorkspace,
|
|
3528
|
-
// Sidebar state
|
|
3529
|
-
availableSidebars: (
|
|
3530
|
-
// Always show agents-panel regardless of workspace settings
|
|
3531
|
-
if (s.id === "agents-panel") {
|
|
3532
|
-
return true;
|
|
3533
|
-
}
|
|
3534
|
-
// If no workspace settings or no sidebars defined for current workspace, show all
|
|
3535
|
-
if (!allowedSidebarIds || allowedSidebarIds.length === 0) {
|
|
3536
|
-
return true;
|
|
3537
|
-
}
|
|
3538
|
-
// Only show sidebars that are in the allowed list for the current workspace
|
|
3539
|
-
return allowedSidebarIds.includes(s.id);
|
|
3540
|
-
}),
|
|
4098
|
+
// Sidebar state
|
|
4099
|
+
availableSidebars: getSidebarsForWorkspace(workspaceId),
|
|
3541
4100
|
openSidebars,
|
|
3542
4101
|
pinnedSidebars,
|
|
3543
4102
|
lockedSidebars,
|
|
@@ -3547,17 +4106,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3547
4106
|
toggleSidebarPin,
|
|
3548
4107
|
toggleSidebarLock,
|
|
3549
4108
|
stackSidebar,
|
|
4109
|
+
moveSidebarToColumn,
|
|
3550
4110
|
unstackSidebar,
|
|
3551
4111
|
reorderSidebarInStack,
|
|
3552
4112
|
reorderPinnedSidebars,
|
|
3553
4113
|
reorderOpenSidebars,
|
|
3554
4114
|
getResolvedSidebar,
|
|
3555
|
-
// Legacy compatibility (deprecated)
|
|
3556
|
-
viewName,
|
|
3557
|
-
previousViewName,
|
|
3558
|
-
switchView,
|
|
3559
|
-
view: currentView,
|
|
3560
|
-
visibleViews,
|
|
3561
4115
|
compareMode,
|
|
3562
4116
|
setCompareMode,
|
|
3563
4117
|
fullscreen,
|
|
@@ -3597,6 +4151,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3597
4151
|
addSocketMessageListener,
|
|
3598
4152
|
sendSocketMessage,
|
|
3599
4153
|
socketConnectionVersion,
|
|
4154
|
+
socketDiagnostics,
|
|
3600
4155
|
currentItemDescriptor,
|
|
3601
4156
|
editorSlots,
|
|
3602
4157
|
activeSlotId,
|
|
@@ -3609,6 +4164,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3609
4164
|
setActiveSlot,
|
|
3610
4165
|
revision,
|
|
3611
4166
|
notifyPageModelReady,
|
|
4167
|
+
pageModelReadyToken,
|
|
3612
4168
|
selectedComment,
|
|
3613
4169
|
setSelectedComment,
|
|
3614
4170
|
comments,
|
|
@@ -3687,6 +4243,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3687
4243
|
setEnableCompletions,
|
|
3688
4244
|
showComponentNavigator,
|
|
3689
4245
|
setShowComponentNavigator: handleSetShowComponentNavigator,
|
|
4246
|
+
isComponentNavigatorOpenForSlot,
|
|
4247
|
+
setComponentNavigatorOpenForSlot,
|
|
3690
4248
|
showAgentsPanel,
|
|
3691
4249
|
setShowAgentsPanel: handleSetShowAgentsPanel,
|
|
3692
4250
|
showMinimap,
|
|
@@ -3698,8 +4256,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3698
4256
|
helpTerminalProfileName,
|
|
3699
4257
|
helpTerminalActiveTab,
|
|
3700
4258
|
setHelpTerminalActiveTab,
|
|
4259
|
+
selectedHelpSectionId,
|
|
4260
|
+
setSelectedHelpSectionId,
|
|
3701
4261
|
showAgentsWorkspaceEditor,
|
|
3702
4262
|
setShowAgentsWorkspaceEditor: handleSetShowAgentsWorkspaceEditor,
|
|
4263
|
+
selectedAgentsWorkspaceAgentId,
|
|
4264
|
+
setSelectedAgentsWorkspaceAgentId,
|
|
3703
4265
|
activeEditorTab,
|
|
3704
4266
|
setActiveEditorTab,
|
|
3705
4267
|
showLayoutComponents,
|
|
@@ -3708,6 +4270,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3708
4270
|
isQuotaExceeded: isQuotaExceeded(),
|
|
3709
4271
|
getQuotaWarningMessage,
|
|
3710
4272
|
isMobile,
|
|
4273
|
+
mobileEditorPanelOpen,
|
|
4274
|
+
setMobileEditorPanelOpen: handleSetMobileEditorPanelOpen,
|
|
3711
4275
|
openDialog,
|
|
3712
4276
|
webSocketMessages,
|
|
3713
4277
|
clearWebSocketMessages: () => setWebSocketMessages([]),
|
|
@@ -3746,7 +4310,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3746
4310
|
configuration,
|
|
3747
4311
|
updateUrl,
|
|
3748
4312
|
workspaceId,
|
|
3749
|
-
switchView,
|
|
3750
4313
|
pathname,
|
|
3751
4314
|
router,
|
|
3752
4315
|
item,
|
|
@@ -3776,7 +4339,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3776
4339
|
currentWorkspace,
|
|
3777
4340
|
previousWorkspaceId,
|
|
3778
4341
|
switchWorkspace,
|
|
3779
|
-
allowedSidebarIds,
|
|
3780
4342
|
openSidebars,
|
|
3781
4343
|
pinnedSidebars,
|
|
3782
4344
|
lockedSidebars,
|
|
@@ -3788,9 +4350,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3788
4350
|
reorderOpenSidebars,
|
|
3789
4351
|
getResolvedSidebar,
|
|
3790
4352
|
viewName,
|
|
3791
|
-
previousViewName,
|
|
3792
|
-
currentView,
|
|
3793
|
-
visibleViews,
|
|
3794
4353
|
compareMode,
|
|
3795
4354
|
// Important: in multi-slot mode the active PageViewContext can change
|
|
3796
4355
|
// without the base `pageViewContext` identity changing (e.g. switching slots).
|
|
@@ -3815,6 +4374,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3815
4374
|
currentItemDescriptor,
|
|
3816
4375
|
revision,
|
|
3817
4376
|
notifyPageModelReady,
|
|
4377
|
+
pageModelReadyToken,
|
|
3818
4378
|
selectedComment,
|
|
3819
4379
|
comments,
|
|
3820
4380
|
availableCommentTags,
|
|
@@ -3833,6 +4393,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3833
4393
|
quickSwitcherSelectedIndex,
|
|
3834
4394
|
handleQuickSwitcherSelect,
|
|
3835
4395
|
webSocketMessages,
|
|
4396
|
+
socketDiagnostics,
|
|
3836
4397
|
factoriesRef,
|
|
3837
4398
|
user,
|
|
3838
4399
|
statusMessage,
|
|
@@ -3841,7 +4402,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3841
4402
|
isQuotaExceeded,
|
|
3842
4403
|
getQuotaWarningMessage,
|
|
3843
4404
|
isMobile,
|
|
4405
|
+
mobileEditorPanelOpen,
|
|
4406
|
+
handleSetMobileEditorPanelOpen,
|
|
3844
4407
|
showComponentNavigator,
|
|
4408
|
+
isComponentNavigatorOpenForSlot,
|
|
4409
|
+
setComponentNavigatorOpenForSlot,
|
|
3845
4410
|
handleSetShowComponentNavigator,
|
|
3846
4411
|
showAgentsPanel,
|
|
3847
4412
|
handleSetShowAgentsPanel,
|
|
@@ -3853,7 +4418,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3853
4418
|
helpTerminalProfileName,
|
|
3854
4419
|
helpTerminalActiveTab,
|
|
3855
4420
|
setHelpTerminalActiveTab,
|
|
4421
|
+
selectedHelpSectionId,
|
|
3856
4422
|
showAgentsWorkspaceEditor,
|
|
4423
|
+
selectedAgentsWorkspaceAgentId,
|
|
3857
4424
|
activeEditorTab,
|
|
3858
4425
|
showLayoutComponents,
|
|
3859
4426
|
openDialog,
|
|
@@ -4098,18 +4665,40 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
4098
4665
|
// prevDependencies.current = currentDependencies;
|
|
4099
4666
|
// editContextRef.current = editContext;
|
|
4100
4667
|
// }, [editContext]);
|
|
4668
|
+
// Auto-open the mobile editor panel for new selection/intent changes, but
|
|
4669
|
+
// keep a manual close sticky for the exact same context.
|
|
4670
|
+
useEffect(() => {
|
|
4671
|
+
if (!isMobile || workspaceId !== "editor")
|
|
4672
|
+
return;
|
|
4673
|
+
if (activeEditorTab) {
|
|
4674
|
+
handleSetMobileEditorPanelOpen(true);
|
|
4675
|
+
return;
|
|
4676
|
+
}
|
|
4677
|
+
if (!(selection.length > 0 || insertMode))
|
|
4678
|
+
return;
|
|
4679
|
+
if (dismissedMobilePanelToken === mobilePanelDismissToken)
|
|
4680
|
+
return;
|
|
4681
|
+
handleSetMobileEditorPanelOpen(true);
|
|
4682
|
+
}, [
|
|
4683
|
+
activeEditorTab,
|
|
4684
|
+
dismissedMobilePanelToken,
|
|
4685
|
+
handleSetMobileEditorPanelOpen,
|
|
4686
|
+
insertMode,
|
|
4687
|
+
isMobile,
|
|
4688
|
+
mobilePanelDismissToken,
|
|
4689
|
+
selection,
|
|
4690
|
+
workspaceId,
|
|
4691
|
+
]);
|
|
4101
4692
|
useEffect(() => {
|
|
4102
4693
|
fieldsEditContext.clearModifiedFields();
|
|
4103
4694
|
}, [currentItemDescriptor]);
|
|
4104
|
-
if (!
|
|
4695
|
+
if (!currentWorkspace)
|
|
4105
4696
|
return null;
|
|
4106
|
-
const editorUi = fullscreen ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "fixed inset-0 flex", children: [_jsx(PageViewerFrame, { compareView: compareMode, pageViewContext: activePageViewContext }), _jsx(FullscreenControls, { device: activePageViewContext.device, setDevice: (d) => activePageViewContext.setDevice(d), canExit: !configuration.forceFullscreen, onExit: () => setFullscreen(false), firstMobileDeviceName: configuration.devices[0]?.name })] }), showFullscreenHint && !configuration.forceFullscreen && (_jsx("div", { className: "fixed inset-0", onMouseMoveCapture: () => {
|
|
4697
|
+
const editorUi = fullscreen ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "fixed inset-0 flex", children: [_jsx(PageViewerFrame, { compareView: compareMode, pageViewContext: activePageViewContext }), _jsx(FullscreenControls, { device: activePageViewContext.device, setDevice: (d) => activePageViewContext.setDevice(d), canExit: !configuration.forceFullscreen, onExit: () => setFullscreen(false), firstMobileDeviceName: configuration.devices[0]?.name })] }), showFullscreenHint && !configuration.forceFullscreen && !isMobile && (_jsx("div", { className: "fixed inset-0 z-10000", onMouseMoveCapture: () => {
|
|
4107
4698
|
setTimeout(() => {
|
|
4108
4699
|
setShowFullscreenHint(false);
|
|
4109
4700
|
}, 600);
|
|
4110
|
-
}, "data-testid": "fullscreen-hint-overlay", children: _jsxs("div", { className: "fixed top-6 left-1/2 -translate-x-1/2 transform rounded-full bg-black/60 px-6 py-2.5 text-sm font-medium text-white shadow-2xl backdrop-blur-md transition-all duration-500", children: ["Press", " ", _jsx("kbd", { className: "mx-1 rounded bg-white/20 px-1.5 py-0.5 text-xs font-semibold", children: "Ctrl + F11" }), " ", "to exit fullscreen"] }) }))] })) : (_jsxs(_Fragment, { children: [_jsx(EditorChrome, { className: className, currentWorkspace: currentWorkspace, centerPanelView: centerPanelView, editContext: editContext,
|
|
4111
|
-
// Legacy props for backwards compatibility
|
|
4112
|
-
currentView: currentView, viewName: viewName }), isTourActive && (_jsx(Tour, { tourStopCallback: () => {
|
|
4701
|
+
}, "data-testid": "fullscreen-hint-overlay", children: _jsxs("div", { className: "fixed top-6 left-1/2 -translate-x-1/2 transform rounded-full bg-black/60 px-6 py-2.5 text-sm font-medium text-white shadow-2xl backdrop-blur-md transition-all duration-500", children: ["Press", " ", _jsx("kbd", { className: "mx-1 rounded bg-white/20 px-1.5 py-0.5 text-xs font-semibold", children: "Ctrl + F11" }), " ", "to exit fullscreen"] }) }))] })) : (_jsxs(_Fragment, { children: [_jsx(EditorChrome, { className: className, currentWorkspace: currentWorkspace, centerPanelView: centerPanelView, editContext: editContext, showAgentsPanel: showAgentsPanel, handleSetShowAgentsPanel: handleSetShowAgentsPanel, workspaceId: workspaceId }), isTourActive && (_jsx(Tour, { tourStopCallback: () => {
|
|
4113
4702
|
setIsTourActive(false);
|
|
4114
4703
|
// Remove tour state from URL
|
|
4115
4704
|
// Use history.replaceState instead of router.replace to avoid triggering React navigation
|
|
@@ -4119,8 +4708,14 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
4119
4708
|
const newUrl = queryString
|
|
4120
4709
|
? `${window.location.pathname}?${queryString}`
|
|
4121
4710
|
: window.location.pathname;
|
|
4122
|
-
window.history.replaceState(
|
|
4123
|
-
}, configuration: configuration, restoredFromUrl: tourRestoredRef.current })), _jsx(GuidanceOverlay, {}), _jsx(
|
|
4124
|
-
return (_jsx("div", { className: `editor h-full w-full`, children: _jsx(OperationsContextProvider, { value: operationsContext.context, children: _jsx(FieldsEditContextProvider, { value: fieldsEditContext, children: _jsxs(EditContextProvider, { value: editContext, children: [_jsx(DevModeIndicator, {}), startupChecks.state === "loading" && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-white/70 backdrop-blur-[1px]", children: _jsx("div", { className: "flex items-center gap-3 rounded-md border border-gray-200 bg-white px-4 py-3 text-gray-700 shadow-sm", children: _jsx(Spinner, { size: "xl" }) }) })), editContext.isRefreshing && (_jsx("div", { className: "pointer-events-none fixed right-0 bottom-0 flex h-24 w-24 items-center justify-center text-gray-600 opacity-50 select-none", children: _jsx(Spinner, {}) })),
|
|
4711
|
+
window.history.replaceState(getCurrentHistoryState(), "", newUrl);
|
|
4712
|
+
}, configuration: configuration, restoredFromUrl: tourRestoredRef.current })), _jsx(FeatureGate, { feature: LicenseFeatures.AI, children: _jsx(GuidanceOverlay, {}) }), _jsx(FeatureGate, { feature: LicenseFeatures.AI, children: _jsx(AgentDialogHandler, {}) })] }));
|
|
4713
|
+
return (_jsx(LicenseProvider, { initialLicenseStatus: initialLicenseStatus, initialStatusLoaded: initialLicenseStatusLoaded, children: _jsx("div", { className: `editor h-full w-full`, children: _jsx(OperationsContextProvider, { value: operationsContext.context, children: _jsx(FieldsEditContextProvider, { value: fieldsEditContext, children: _jsxs(EditContextProvider, { value: editContext, children: [_jsx(DevModeIndicator, {}), startupChecks.state === "loading" && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-white/70 backdrop-blur-[1px]", children: _jsx("div", { className: "flex items-center gap-3 rounded-md border border-gray-200 bg-white px-4 py-3 text-gray-700 shadow-sm", children: _jsx(Spinner, { size: "xl" }) }) })), editContext.isRefreshing && (_jsx("div", { className: "pointer-events-none fixed right-0 bottom-0 flex h-24 w-24 items-center justify-center text-gray-600 opacity-50 select-none", children: _jsx(Spinner, {}) })), (currentWorkspace.id === "agents" ||
|
|
4714
|
+
currentWorkspace.id === "taskboard") &&
|
|
4715
|
+
showAgentsWorkspaceEditor && (_jsx(AgentsSlotContextBridgeHost, { slots: editorSlots })), startupChecks.state !== "loading" && (children || editorUi), startupChecks.state !== "loading" && dialog, _jsx(Toaster, { position: "top-center" }), " ", _jsx(ConfirmationDialog, { ref: confirmationDialogRef }), _jsx(ConcurrentUserLimitDialog, { open: concurrentUserLimitError !== null, onOpenChange: (open) => {
|
|
4716
|
+
if (!open) {
|
|
4717
|
+
setConcurrentUserLimitError(null);
|
|
4718
|
+
}
|
|
4719
|
+
}, sessionId: sessionId, currentUsers: concurrentUserLimitError?.currentUsers ?? 0, maxUsers: concurrentUserLimitError?.maxUsers ?? 0, message: concurrentUserLimitError?.message ?? "", onRetry: handleRetryConnection, isAdministrator: userInfo.user.isAdministrator === true }), _jsx(QuickItemSwitcher, { visible: quickSwitcherVisible, entries: quickSwitcherEntries.slice(0, 5), selectedIndex: quickSwitcherSelectedIndex, onSelect: handleQuickSwitcherSelect, onClose: () => setQuickSwitcherVisible(false) }), _jsx(EditContextMenu, { ref: contextMenuRef }), _jsx(FeatureGate, { feature: LicenseFeatures.AI, children: _jsx(InlineAiTrigger, {}) }), media.mediaSelectorVisible && (_jsx(MediaSelector, { language: editContext.currentItemDescriptor.language, visible: media.mediaSelectorVisible, onHide: media.handleHide, onMediaSelected: media.onMediaSelect, selectedIdPath: media.selectedMediaIdPath, mode: media.mediaSelectorMode, initialSearchTerm: media.initialSearchTerm })), _jsx(FieldEditorPopup, { ref: fieldEditorPopupRef }), _jsx(LicenseOverlay, {})] }) }) }) }) }));
|
|
4125
4720
|
}
|
|
4126
4721
|
//# sourceMappingURL=EditorShell.js.map
|