@parhelia/core 0.1.12570 → 0.1.12572
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 +21 -21
- 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 +3 -2
- 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 +2424 -496
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/AgentTerminalStatusBar.d.ts +8 -3
- 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/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 +617 -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 +115 -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 +837 -253
- 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.js +26 -12
- package/dist/editor/reviews/Comment.js.map +1 -1
- package/dist/editor/reviews/CommentDisplayPopover.js +7 -5
- package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -1
- package/dist/editor/reviews/CommentView.js +19 -4
- package/dist/editor/reviews/CommentView.js.map +1 -1
- package/dist/editor/reviews/Comments.js +89 -72
- 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 +2 -2
- package/dist/editor/reviews/useReviews.js +12 -30
- 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/ModelsPanel.js +329 -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 +49 -13
- 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 +39 -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 +69 -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);
|
|
@@ -709,8 +793,13 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
709
793
|
console.error(`No workspace found for id: ${workspaceId}`);
|
|
710
794
|
return null;
|
|
711
795
|
}
|
|
712
|
-
|
|
713
|
-
|
|
796
|
+
const activeKeyboardCommands = useMemo(() => {
|
|
797
|
+
const activeCommandIds = new Set(currentWorkspace.keyboardCommandIds ?? []);
|
|
798
|
+
return (configuration.commands.keyboardCommands ?? []).filter((command) => activeCommandIds.has(command.id));
|
|
799
|
+
}, [
|
|
800
|
+
currentWorkspace.keyboardCommandIds,
|
|
801
|
+
configuration.commands.keyboardCommands,
|
|
802
|
+
]);
|
|
714
803
|
useEffect(() => {
|
|
715
804
|
if (currentWorkspace?.component) {
|
|
716
805
|
setCenterPanelView(currentWorkspace.component);
|
|
@@ -738,6 +827,45 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
738
827
|
const sendClientInfo = useCallback(() => {
|
|
739
828
|
debouncedSendClientInfo();
|
|
740
829
|
}, [debouncedSendClientInfo]);
|
|
830
|
+
const getCurrentHistoryState = useCallback(() => {
|
|
831
|
+
if (typeof window === "undefined") {
|
|
832
|
+
return null;
|
|
833
|
+
}
|
|
834
|
+
return window.history.state;
|
|
835
|
+
}, []);
|
|
836
|
+
useEffect(() => {
|
|
837
|
+
if (isInitialLoad) {
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
const itemid = searchParams.get("itemid");
|
|
841
|
+
const language = searchParams.get("lang") ?? searchParams.get("language");
|
|
842
|
+
const versionParam = searchParams.get("version");
|
|
843
|
+
const version = versionParam ? parseInt(versionParam, 10) : 0;
|
|
844
|
+
const itemId = cleanId(itemid ?? undefined);
|
|
845
|
+
if (!itemId || !language) {
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
const currentDescriptor = currentItemDescriptorRef.current;
|
|
849
|
+
const matchesCurrentDescriptor = currentDescriptor?.id === itemId &&
|
|
850
|
+
currentDescriptor?.language === language &&
|
|
851
|
+
(!version || currentDescriptor?.version === version);
|
|
852
|
+
if (matchesCurrentDescriptor) {
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
isHandlingPopStateRef.current = true;
|
|
856
|
+
loadItemRef
|
|
857
|
+
.current({
|
|
858
|
+
id: itemId,
|
|
859
|
+
language,
|
|
860
|
+
version,
|
|
861
|
+
}, {
|
|
862
|
+
addToBrowseHistory: false,
|
|
863
|
+
skipViewChange: true,
|
|
864
|
+
})
|
|
865
|
+
.finally(() => {
|
|
866
|
+
isHandlingPopStateRef.current = false;
|
|
867
|
+
});
|
|
868
|
+
}, [isInitialLoad, searchParams]);
|
|
741
869
|
const startTour = useCallback(() => {
|
|
742
870
|
setIsTourActive(true);
|
|
743
871
|
// Persist tour state to URL so it survives navigation/remounts
|
|
@@ -745,32 +873,61 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
745
873
|
const params = new URLSearchParams(window.location.search);
|
|
746
874
|
params.set("tour", "active");
|
|
747
875
|
const newUrl = `${window.location.pathname}?${params.toString()}`;
|
|
748
|
-
window.history.replaceState(
|
|
876
|
+
window.history.replaceState(getCurrentHistoryState(), "", newUrl);
|
|
749
877
|
}, [setIsTourActive]);
|
|
750
|
-
const isMobile = useMediaQuery("(max-width:
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
878
|
+
const isMobile = useMediaQuery("(max-width: 767px)");
|
|
879
|
+
// On mobile, clear all locked sidebars and keep only the last-opened panel.
|
|
880
|
+
// Handles viewport resize: desktop -> mobile unlocks everything and trims to one panel.
|
|
881
|
+
useEffect(() => {
|
|
882
|
+
if (isMobile) {
|
|
883
|
+
setLockedSidebars([]);
|
|
884
|
+
const current = openSidebarsRef.current;
|
|
885
|
+
const lastSidebar = current[current.length - 1];
|
|
886
|
+
if (current.length > 1 && lastSidebar) {
|
|
887
|
+
const trimmed = [lastSidebar];
|
|
888
|
+
openSidebarsRef.current = trimmed;
|
|
889
|
+
setOpenSidebars(trimmed);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
}, [isMobile]);
|
|
893
|
+
const setComponentNavigatorOpenForSlot = useCallback((slotId, value) => {
|
|
894
|
+
const previousValue = isComponentNavigatorOpenForSlot(slotId);
|
|
895
|
+
const newValue = typeof value === "function" ? value(previousValue) : value;
|
|
896
|
+
if (slotId) {
|
|
897
|
+
setSlotComponentNavigatorVisibility((prev) => {
|
|
898
|
+
const currentValue = prev[slotId] ?? showComponentNavigatorDefault;
|
|
899
|
+
if (currentValue === newValue && prev[slotId] !== undefined) {
|
|
900
|
+
return prev;
|
|
901
|
+
}
|
|
902
|
+
return {
|
|
903
|
+
...prev,
|
|
904
|
+
[slotId]: newValue,
|
|
905
|
+
};
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
setShowComponentNavigatorDefault(newValue);
|
|
754
909
|
setUserPreferences({ showComponentNavigator: newValue });
|
|
755
|
-
// On mobile, close Agents Panel when opening Component Navigator
|
|
756
910
|
if (isMobile && newValue) {
|
|
757
911
|
setShowAgentsPanel(false);
|
|
758
912
|
setUserPreferences({ showAgentsPanel: false });
|
|
759
913
|
}
|
|
760
914
|
}, [
|
|
761
|
-
|
|
762
|
-
|
|
915
|
+
isComponentNavigatorOpenForSlot,
|
|
916
|
+
showComponentNavigatorDefault,
|
|
763
917
|
setUserPreferences,
|
|
764
918
|
isMobile,
|
|
765
919
|
setShowAgentsPanel,
|
|
766
920
|
]);
|
|
921
|
+
const handleSetShowComponentNavigator = useCallback((value) => {
|
|
922
|
+
setComponentNavigatorOpenForSlot(activeSlotIdRef.current, value);
|
|
923
|
+
}, [setComponentNavigatorOpenForSlot]);
|
|
767
924
|
const handleSetShowAgentsPanel = useCallback((value) => {
|
|
768
925
|
const newValue = typeof value === "function" ? value(showAgentsPanel) : value;
|
|
769
926
|
setShowAgentsPanel(newValue);
|
|
770
927
|
setUserPreferences({ showAgentsPanel: newValue });
|
|
771
928
|
// On mobile, close Component Navigator when opening Agents Panel
|
|
772
929
|
if (isMobile && newValue) {
|
|
773
|
-
|
|
930
|
+
setComponentNavigatorOpenForSlot(activeSlotIdRef.current, false);
|
|
774
931
|
setUserPreferences({ showComponentNavigator: false });
|
|
775
932
|
}
|
|
776
933
|
}, [
|
|
@@ -778,8 +935,40 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
778
935
|
setShowAgentsPanel,
|
|
779
936
|
setUserPreferences,
|
|
780
937
|
isMobile,
|
|
781
|
-
|
|
938
|
+
setComponentNavigatorOpenForSlot,
|
|
782
939
|
]);
|
|
940
|
+
// Mobile editor panel state (EditorForm shown in bottom panel on mobile)
|
|
941
|
+
const [mobileEditorPanelOpen, setMobileEditorPanelOpenRaw] = useState(false);
|
|
942
|
+
const [dismissedMobilePanelToken, setDismissedMobilePanelToken] = useState(null);
|
|
943
|
+
const previousActiveSlotIdRef = useRef(null);
|
|
944
|
+
const mobilePanelDismissToken = useMemo(() => {
|
|
945
|
+
const selectionKey = selection.join(",");
|
|
946
|
+
return [
|
|
947
|
+
activeSlotId || "no-slot",
|
|
948
|
+
selectionKey,
|
|
949
|
+
insertMode ? "insert" : "browse",
|
|
950
|
+
].join("|");
|
|
951
|
+
}, [activeSlotId, insertMode, selection]);
|
|
952
|
+
useEffect(() => {
|
|
953
|
+
const previousActiveSlotId = previousActiveSlotIdRef.current;
|
|
954
|
+
if (previousActiveSlotId !== activeSlotId) {
|
|
955
|
+
setDismissedMobilePanelToken(null);
|
|
956
|
+
}
|
|
957
|
+
previousActiveSlotIdRef.current = activeSlotId;
|
|
958
|
+
}, [activeSlotId]);
|
|
959
|
+
const handleSetMobileEditorPanelOpen = useCallback((open) => {
|
|
960
|
+
setMobileEditorPanelOpenRaw(open);
|
|
961
|
+
if (!open) {
|
|
962
|
+
setDismissedMobilePanelToken(mobilePanelDismissToken);
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
setDismissedMobilePanelToken(null);
|
|
966
|
+
if (open && isMobile) {
|
|
967
|
+
// Close all sidebars when opening the editor panel on mobile
|
|
968
|
+
openSidebarsRef.current = [];
|
|
969
|
+
setOpenSidebars([]);
|
|
970
|
+
}
|
|
971
|
+
}, [isMobile, mobilePanelDismissToken]);
|
|
783
972
|
const handleSetShowMinimap = useCallback((value) => {
|
|
784
973
|
const newValue = typeof value === "function" ? value(showMinimap) : value;
|
|
785
974
|
setShowMinimap(newValue);
|
|
@@ -793,6 +982,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
793
982
|
setHelpTerminalInitialPrompt(undefined);
|
|
794
983
|
setHelpTerminalProfileName(undefined);
|
|
795
984
|
setHelpTerminalActiveTab(undefined);
|
|
985
|
+
setSelectedHelpSectionId(null);
|
|
796
986
|
}
|
|
797
987
|
}, [showHelpTerminal]);
|
|
798
988
|
const toggleHelpTerminal = useCallback((options) => {
|
|
@@ -835,13 +1025,27 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
835
1025
|
setOpenSidebars(newOrder);
|
|
836
1026
|
setSidebarStacks((prev) => normalizeSidebarStacks(newOrder, prev));
|
|
837
1027
|
}, []);
|
|
1028
|
+
const ensureSidebarPinned = useCallback((sidebarId) => {
|
|
1029
|
+
const currentPinnedSidebars = pinnedSidebarsRef.current;
|
|
1030
|
+
if (currentPinnedSidebars.includes(sidebarId)) {
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
const newPinnedSidebars = [...currentPinnedSidebars, sidebarId];
|
|
1034
|
+
pinnedSidebarsRef.current = newPinnedSidebars;
|
|
1035
|
+
setPinnedSidebars(newPinnedSidebars);
|
|
1036
|
+
setUserPreferences({ pinnedSidebars: newPinnedSidebars });
|
|
1037
|
+
}, [setUserPreferences]);
|
|
838
1038
|
// messageHandler is defined after loadItem/loadHistory declarations to avoid temporal dead zones
|
|
839
1039
|
const user = activeSessions.find((x) => x.sessionId === sessionId)?.user ||
|
|
840
1040
|
userInfo.user;
|
|
841
|
-
// Self-heal if our session disappears (e.g., after HMR)
|
|
1041
|
+
// Self-heal if our session disappears (e.g., after HMR). Skip when concurrent user limit
|
|
1042
|
+
// is showing so we don't spam recovery attempts when the connection was rejected.
|
|
842
1043
|
const missingSessionRecoveryTimerRef = useRef(null);
|
|
843
1044
|
const missingSessionRecoveryAttemptsRef = useRef(0);
|
|
1045
|
+
const concurrentUserLimitErrorRef = useRef(null);
|
|
844
1046
|
useEffect(() => {
|
|
1047
|
+
if (concurrentUserLimitErrorRef.current !== null)
|
|
1048
|
+
return;
|
|
845
1049
|
const hasMySession = activeSessions.some((s) => s.sessionId === sessionId);
|
|
846
1050
|
if (hasMySession) {
|
|
847
1051
|
// Reset recovery state when we see ourselves again
|
|
@@ -857,7 +1061,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
857
1061
|
return;
|
|
858
1062
|
const attempt = missingSessionRecoveryAttemptsRef.current + 1;
|
|
859
1063
|
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
1064
|
missingSessionRecoveryTimerRef.current = setTimeout(() => {
|
|
862
1065
|
missingSessionRecoveryTimerRef.current = null;
|
|
863
1066
|
missingSessionRecoveryAttemptsRef.current = attempt;
|
|
@@ -866,6 +1069,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
866
1069
|
}
|
|
867
1070
|
else {
|
|
868
1071
|
// Force a reconnect to refresh presence after several failed nudges
|
|
1072
|
+
console.warn("Session presence did not recover after retries. Forcing reconnect.");
|
|
869
1073
|
try {
|
|
870
1074
|
globalThis.editorSocket?.close(4000, "recover-presence");
|
|
871
1075
|
}
|
|
@@ -885,9 +1089,15 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
885
1089
|
// Initialize lastUrlRef to current URL on mount
|
|
886
1090
|
lastUrlRef.current = window.location.href;
|
|
887
1091
|
const keepAliveUrl = "/parhelia/keepalive";
|
|
888
|
-
const
|
|
1092
|
+
const runSessionCheck = () => {
|
|
889
1093
|
fetch(keepAliveUrl + "?ts=" + Date.now())
|
|
890
1094
|
.then((response) => {
|
|
1095
|
+
if (response.headers.get("X-Parhelia-Session-Revoked") === "true") {
|
|
1096
|
+
window.dispatchEvent(new CustomEvent("parhelia:session-revoked", {
|
|
1097
|
+
detail: { reason: "session-revoked" },
|
|
1098
|
+
}));
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
891
1101
|
if (response.status === 401 || response.status === 403) {
|
|
892
1102
|
toast.error("Your session has expired", {
|
|
893
1103
|
description: "Please login again to continue editing.",
|
|
@@ -900,7 +1110,24 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
900
1110
|
}
|
|
901
1111
|
})
|
|
902
1112
|
.catch((error) => console.error("Keep Alive error:", error));
|
|
903
|
-
}
|
|
1113
|
+
};
|
|
1114
|
+
const keepaliveIntervalMs = (() => {
|
|
1115
|
+
const param = new URLSearchParams(window.location.search).get("keepaliveIntervalMs");
|
|
1116
|
+
if (!param)
|
|
1117
|
+
return 5 * 60 * 1000;
|
|
1118
|
+
const ms = parseInt(param, 10);
|
|
1119
|
+
return Number.isFinite(ms) && ms >= 2000 && ms <= 60000
|
|
1120
|
+
? ms
|
|
1121
|
+
: 5 * 60 * 1000;
|
|
1122
|
+
})();
|
|
1123
|
+
const interval = setInterval(() => {
|
|
1124
|
+
runSessionCheck();
|
|
1125
|
+
}, keepaliveIntervalMs);
|
|
1126
|
+
const handleVisibilityChange = () => {
|
|
1127
|
+
if (document.visibilityState === "visible") {
|
|
1128
|
+
runSessionCheck();
|
|
1129
|
+
}
|
|
1130
|
+
};
|
|
904
1131
|
const handleMessage = (event) => {
|
|
905
1132
|
if (event.data.type === "componentsSelected") {
|
|
906
1133
|
setSelection(event.data.componentIds);
|
|
@@ -908,35 +1135,44 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
908
1135
|
};
|
|
909
1136
|
// Listen for browser navigation events (back/forward buttons)
|
|
910
1137
|
const handlePopState = (event) => {
|
|
911
|
-
const newUrl = window.location.
|
|
1138
|
+
const newUrl = `${window.location.pathname}${window.location.search}`;
|
|
912
1139
|
if (newUrl !== lastUrlRef.current) {
|
|
913
1140
|
lastUrlRef.current = newUrl;
|
|
914
1141
|
// Mark that we're handling a popstate to prevent URL sync from pushing to history
|
|
915
1142
|
isHandlingPopStateRef.current = true;
|
|
916
1143
|
// Sync URL parameters back to component state for browser navigation
|
|
917
1144
|
const urlParams = new URLSearchParams(window.location.search);
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
if (urlWorkspace && urlWorkspace !== viewNameRef.current) {
|
|
1145
|
+
const urlWorkspace = urlParams.get("workspace");
|
|
1146
|
+
if (urlWorkspace && urlWorkspace !== workspaceIdRef.current) {
|
|
921
1147
|
setWorkspaceId(urlWorkspace);
|
|
922
1148
|
}
|
|
923
|
-
// Handle sidebar changes
|
|
1149
|
+
// Handle sidebar changes — clear when absent so state matches the URL
|
|
924
1150
|
const sidebarParam = urlParams.get("sidebar");
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
1151
|
+
const newSidebars = sidebarParam
|
|
1152
|
+
? sidebarParam.split(",").filter(Boolean)
|
|
1153
|
+
: [];
|
|
1154
|
+
setOpenSidebars(newSidebars);
|
|
929
1155
|
// Handle wizard ID changes
|
|
930
1156
|
const wizardId = urlParams.get("wizardid");
|
|
931
1157
|
setCurrentWizardId(wizardId);
|
|
932
|
-
// Handle compare mode changes
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
1158
|
+
// Handle compare mode changes — always set to avoid stale-closure mismatch
|
|
1159
|
+
// (React skips re-render when the value is unchanged)
|
|
1160
|
+
setCompareMode(urlParams.get("compare") === "true");
|
|
1161
|
+
// Handle help panel changes
|
|
1162
|
+
const helpParam = urlParams.get("help");
|
|
1163
|
+
if (helpParam) {
|
|
1164
|
+
setHelpTerminalActiveTab("manual");
|
|
1165
|
+
setShowHelpTerminal(true);
|
|
1166
|
+
setSelectedHelpSectionId(helpParam === "contents" || helpParam === "true"
|
|
1167
|
+
? null
|
|
1168
|
+
: helpParam);
|
|
1169
|
+
}
|
|
1170
|
+
else {
|
|
1171
|
+
handleSetShowHelpTerminal(false);
|
|
936
1172
|
}
|
|
937
|
-
// Handle mode changes
|
|
1173
|
+
// Handle mode changes — always set to avoid stale-closure mismatch
|
|
938
1174
|
const urlMode = urlParams.get("mode");
|
|
939
|
-
if (urlMode
|
|
1175
|
+
if (urlMode) {
|
|
940
1176
|
setMode(urlMode);
|
|
941
1177
|
}
|
|
942
1178
|
// Handle fullscreen changes (shell-level state)
|
|
@@ -989,9 +1225,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
989
1225
|
};
|
|
990
1226
|
window.addEventListener("message", handleMessage);
|
|
991
1227
|
window.addEventListener("popstate", handlePopState);
|
|
1228
|
+
window.addEventListener("visibilitychange", handleVisibilityChange);
|
|
992
1229
|
return () => {
|
|
993
1230
|
window.removeEventListener("message", handleMessage);
|
|
994
1231
|
window.removeEventListener("popstate", handlePopState);
|
|
1232
|
+
window.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
995
1233
|
clearInterval(interval);
|
|
996
1234
|
};
|
|
997
1235
|
}, []);
|
|
@@ -1001,14 +1239,34 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1001
1239
|
if (searchParams.get("noTour") !== null) {
|
|
1002
1240
|
return;
|
|
1003
1241
|
}
|
|
1242
|
+
// Don't start tour when there are setup errors (user is or will be on system status page)
|
|
1243
|
+
if (startupChecks.state === "complete" && startupChecks.hasBlockingIssues) {
|
|
1244
|
+
return;
|
|
1245
|
+
}
|
|
1246
|
+
// Don't start tour when already on settings system status page
|
|
1247
|
+
if (viewName === "settings" && searchParams.get("ccpanel") === "status") {
|
|
1248
|
+
return;
|
|
1249
|
+
}
|
|
1250
|
+
// Wait for startup checks so we know whether we'll redirect to status
|
|
1251
|
+
if (startupChecks.state !== "complete") {
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1004
1254
|
const tour = configuration.activeTour;
|
|
1005
1255
|
const key = tour === "default" ? "editor.tourShown" : "editor.tourShown." + tour;
|
|
1006
|
-
const tourShown =
|
|
1256
|
+
const tourShown = localStorageService.getString(key);
|
|
1007
1257
|
if (!tourShown) {
|
|
1008
1258
|
startTour();
|
|
1009
|
-
|
|
1259
|
+
localStorageService.setString(key, "true");
|
|
1010
1260
|
}
|
|
1011
|
-
}, [
|
|
1261
|
+
}, [
|
|
1262
|
+
user,
|
|
1263
|
+
startupChecks.state,
|
|
1264
|
+
startupChecks.hasBlockingIssues,
|
|
1265
|
+
viewName,
|
|
1266
|
+
searchParams,
|
|
1267
|
+
configuration.activeTour,
|
|
1268
|
+
startTour,
|
|
1269
|
+
]);
|
|
1012
1270
|
// WebSocket initialization is performed after messageHandler is defined
|
|
1013
1271
|
// Defer URL sync until loadItem is defined below
|
|
1014
1272
|
// Mark end of initial load phase
|
|
@@ -1140,7 +1398,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1140
1398
|
const loadComments = useCallback(async () => {
|
|
1141
1399
|
if (!currentItemDescriptor)
|
|
1142
1400
|
return;
|
|
1143
|
-
const
|
|
1401
|
+
const reviewId = searchParams.get("reviewId");
|
|
1402
|
+
const result = await getComments(currentItemDescriptor.id, currentItemDescriptor.language, currentItemDescriptor.version, reviewId ?? undefined);
|
|
1144
1403
|
if (handleErrorResult(result, ui, state))
|
|
1145
1404
|
return;
|
|
1146
1405
|
setComments((x) => {
|
|
@@ -1153,7 +1412,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1153
1412
|
allComments.sort((a, b) => a.position - b.position);
|
|
1154
1413
|
return allComments;
|
|
1155
1414
|
});
|
|
1156
|
-
}, [currentItemDescriptor]);
|
|
1415
|
+
}, [currentItemDescriptor, searchParams]);
|
|
1157
1416
|
// Assuming currentItemDescriptor, ui, state, handleErrorResult, and setSuggestedEdits
|
|
1158
1417
|
// are available in your component context.
|
|
1159
1418
|
const loadSuggestedEdits = useCallback(async () => {
|
|
@@ -1162,7 +1421,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1162
1421
|
const result = await getSuggestedEdits(item.descriptor.id, item.descriptor.language, item.descriptor.version);
|
|
1163
1422
|
if (handleErrorResult(result, ui, state))
|
|
1164
1423
|
return;
|
|
1165
|
-
|
|
1424
|
+
const edits = result.data || [];
|
|
1425
|
+
setSuggestedEdits(edits);
|
|
1166
1426
|
}, [item]);
|
|
1167
1427
|
const loadFavorites = useCallback(async () => {
|
|
1168
1428
|
try {
|
|
@@ -1236,6 +1496,25 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1236
1496
|
return idB.localeCompare(idA);
|
|
1237
1497
|
});
|
|
1238
1498
|
}, []);
|
|
1499
|
+
const normalizeEditHistoryPayload = useCallback((value) => {
|
|
1500
|
+
if (Array.isArray(value)) {
|
|
1501
|
+
return value;
|
|
1502
|
+
}
|
|
1503
|
+
if (value && typeof value === "object") {
|
|
1504
|
+
const payload = value;
|
|
1505
|
+
const candidates = [
|
|
1506
|
+
payload.operations,
|
|
1507
|
+
payload.history,
|
|
1508
|
+
payload.items,
|
|
1509
|
+
payload.data,
|
|
1510
|
+
];
|
|
1511
|
+
const firstArray = candidates.find((candidate) => Array.isArray(candidate));
|
|
1512
|
+
if (Array.isArray(firstArray)) {
|
|
1513
|
+
return firstArray;
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
return [];
|
|
1517
|
+
}, []);
|
|
1239
1518
|
useEffect(() => {
|
|
1240
1519
|
// Read fresh page from the mutable slot context ref chain.
|
|
1241
1520
|
// The slot context uses a stable ref that is mutated in-place (see editorSlotContext.ts),
|
|
@@ -1258,7 +1537,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1258
1537
|
if (handleErrorResult(result, ui, state))
|
|
1259
1538
|
return;
|
|
1260
1539
|
setEditHistory((prev) => {
|
|
1261
|
-
const next = result.data
|
|
1540
|
+
const next = normalizeEditHistoryPayload(result.data);
|
|
1262
1541
|
if (!prev.length)
|
|
1263
1542
|
return sortEditHistoryByDateDesc(next);
|
|
1264
1543
|
if (!next.length)
|
|
@@ -1317,6 +1596,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1317
1596
|
const shouldFilterByLanguage = filterByLanguage !== undefined
|
|
1318
1597
|
? filterByLanguage
|
|
1319
1598
|
: filterByCurrentLanguage;
|
|
1599
|
+
const trimmedHistoryQuery = historySearchQuery.trim();
|
|
1600
|
+
const historyQuery = trimmedHistoryQuery.length > 0 ? trimmedHistoryQuery : undefined;
|
|
1320
1601
|
if (currentMode === "global") {
|
|
1321
1602
|
// Global mode: optionally filter by session and/or language
|
|
1322
1603
|
const currentLanguage = item?.descriptor?.language || currentItemDescriptor?.language;
|
|
@@ -1326,13 +1607,14 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1326
1607
|
language: shouldFilterByLanguage && currentLanguage
|
|
1327
1608
|
? currentLanguage
|
|
1328
1609
|
: undefined,
|
|
1610
|
+
query: historyQuery,
|
|
1329
1611
|
});
|
|
1330
1612
|
if (handleErrorResult(result, ui, state)) {
|
|
1331
1613
|
console.error("[EditorShell] Failed to load history:", result);
|
|
1332
1614
|
return;
|
|
1333
1615
|
}
|
|
1334
1616
|
setEditHistory((prev) => {
|
|
1335
|
-
const next = result.data
|
|
1617
|
+
const next = normalizeEditHistoryPayload(result.data);
|
|
1336
1618
|
const scope = {
|
|
1337
1619
|
mode: currentMode,
|
|
1338
1620
|
filterBySession: shouldFilterBySession,
|
|
@@ -1343,8 +1625,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1343
1625
|
};
|
|
1344
1626
|
if (!prev.length)
|
|
1345
1627
|
return sortEditHistoryByDateDesc(next.filter((op) => matchesHistoryScope(op, scope)));
|
|
1346
|
-
if (!next.length)
|
|
1628
|
+
if (!next.length) {
|
|
1629
|
+
// When searching, respect the empty result — don't fall back to previous items
|
|
1630
|
+
if (historyQuery)
|
|
1631
|
+
return [];
|
|
1347
1632
|
return sortEditHistoryByDateDesc(prev.filter((op) => matchesHistoryScope(op, scope)));
|
|
1633
|
+
}
|
|
1348
1634
|
const prevById = new Map(prev.map((x) => [x.id, x]));
|
|
1349
1635
|
const nextById = new Set(next.map((x) => x.id));
|
|
1350
1636
|
const merged = next
|
|
@@ -1380,9 +1666,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1380
1666
|
return mergedOp;
|
|
1381
1667
|
});
|
|
1382
1668
|
// Preserve operations that arrived via WebSocket during the fetch window
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1669
|
+
// but not when filtering by search query — search results are authoritative
|
|
1670
|
+
if (!historyQuery) {
|
|
1671
|
+
for (const [id, priorOp] of prevById) {
|
|
1672
|
+
if (!nextById.has(id) && matchesHistoryScope(priorOp, scope)) {
|
|
1673
|
+
merged.push(priorOp);
|
|
1674
|
+
}
|
|
1386
1675
|
}
|
|
1387
1676
|
}
|
|
1388
1677
|
return sortEditHistoryByDateDesc(merged);
|
|
@@ -1405,12 +1694,13 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1405
1694
|
const result = await getEditHistory({
|
|
1406
1695
|
item: itemFilter,
|
|
1407
1696
|
sessionId: shouldFilterBySession ? sessionId : undefined,
|
|
1697
|
+
query: historyQuery,
|
|
1408
1698
|
});
|
|
1409
1699
|
if (handleErrorResult(result, ui, state)) {
|
|
1410
1700
|
console.error("[EditorShell] Failed to load item history:", result);
|
|
1411
1701
|
return;
|
|
1412
1702
|
}
|
|
1413
|
-
let operations = result.data
|
|
1703
|
+
let operations = normalizeEditHistoryPayload(result.data);
|
|
1414
1704
|
// Client-side version filtering for current-version mode
|
|
1415
1705
|
if (currentMode === "current-version") {
|
|
1416
1706
|
// Defensive filter: only include operations for the current version
|
|
@@ -1433,8 +1723,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1433
1723
|
};
|
|
1434
1724
|
if (!prev.length)
|
|
1435
1725
|
return sortEditHistoryByDateDesc(operations.filter((op) => matchesHistoryScope(op, scope)));
|
|
1436
|
-
if (!operations.length)
|
|
1726
|
+
if (!operations.length) {
|
|
1727
|
+
if (historyQuery)
|
|
1728
|
+
return [];
|
|
1437
1729
|
return sortEditHistoryByDateDesc(prev.filter((op) => matchesHistoryScope(op, scope)));
|
|
1730
|
+
}
|
|
1438
1731
|
const prevById = new Map(prev.map((x) => [x.id, x]));
|
|
1439
1732
|
const nextById = new Set(operations.map((x) => x.id));
|
|
1440
1733
|
const merged = operations
|
|
@@ -1470,9 +1763,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1470
1763
|
return mergedOp;
|
|
1471
1764
|
});
|
|
1472
1765
|
// Preserve operations that arrived via WebSocket during the fetch window
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1766
|
+
// but not when filtering by search query — search results are authoritative
|
|
1767
|
+
if (!historyQuery) {
|
|
1768
|
+
for (const [id, priorOp] of prevById) {
|
|
1769
|
+
if (!nextById.has(id) && matchesHistoryScope(priorOp, scope)) {
|
|
1770
|
+
merged.push(priorOp);
|
|
1771
|
+
}
|
|
1476
1772
|
}
|
|
1477
1773
|
}
|
|
1478
1774
|
return sortEditHistoryByDateDesc(merged);
|
|
@@ -1483,9 +1779,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1483
1779
|
historyMode,
|
|
1484
1780
|
showOnlyMyChanges,
|
|
1485
1781
|
filterByCurrentLanguage,
|
|
1782
|
+
historySearchQuery,
|
|
1486
1783
|
item,
|
|
1487
1784
|
currentItemDescriptor,
|
|
1488
1785
|
matchesHistoryScope,
|
|
1786
|
+
normalizeEditHistoryPayload,
|
|
1489
1787
|
sortEditHistoryByDateDesc,
|
|
1490
1788
|
]);
|
|
1491
1789
|
// Debounced history refresh to avoid hammering `/parhelia/editHistory` on rapid UI changes.
|
|
@@ -1551,13 +1849,14 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1551
1849
|
}
|
|
1552
1850
|
}
|
|
1553
1851
|
else if (historyMode !== "global" && currentItemDescriptor) {
|
|
1852
|
+
// Always load immediately for page-centric/current-version/timeline so we don't show
|
|
1853
|
+
// an empty history list for the debounce window (e.g. 600ms). The effect only runs on
|
|
1854
|
+
// mode or item id/lang/version change, not on every keystroke, so this avoids flaky
|
|
1855
|
+
// undo/redo tests and improves UX when switching to page-centric.
|
|
1554
1856
|
if (!historyInitialLoadDoneRef.current) {
|
|
1555
1857
|
historyInitialLoadDoneRef.current = true;
|
|
1556
|
-
refreshHistoryRef.current(historyMode);
|
|
1557
|
-
}
|
|
1558
|
-
else {
|
|
1559
|
-
debouncedRefreshHistoryRef.current(historyMode);
|
|
1560
1858
|
}
|
|
1859
|
+
refreshHistoryRef.current(historyMode);
|
|
1561
1860
|
}
|
|
1562
1861
|
else if (historyMode !== "global" && !currentItemDescriptor) {
|
|
1563
1862
|
// Clear history if no item loaded in page-centric modes
|
|
@@ -1570,6 +1869,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1570
1869
|
historyMode,
|
|
1571
1870
|
showOnlyMyChanges,
|
|
1572
1871
|
filterByCurrentLanguage,
|
|
1872
|
+
historySearchQuery,
|
|
1573
1873
|
currentItemDescriptor?.id,
|
|
1574
1874
|
currentItemDescriptor?.language,
|
|
1575
1875
|
currentItemDescriptor?.version,
|
|
@@ -1634,6 +1934,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1634
1934
|
if (isInitialLoad)
|
|
1635
1935
|
return;
|
|
1636
1936
|
const current = new URLSearchParams(window.location.search);
|
|
1937
|
+
const urlWorkspace = current.get("workspace");
|
|
1938
|
+
const urlView = current.get("view");
|
|
1939
|
+
const isWorkspaceTransitioning = !!urlWorkspace && urlWorkspace !== viewName;
|
|
1637
1940
|
// Sync item-related parameters only when an item is selected
|
|
1638
1941
|
if (currentItemDescriptor) {
|
|
1639
1942
|
if (current.get("itemid") !== currentItemDescriptor.id) {
|
|
@@ -1664,10 +1967,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1664
1967
|
// If reviewId or urlItemId exists, preserve itemid/lang/version from URL
|
|
1665
1968
|
}
|
|
1666
1969
|
// Always sync workspace-related parameters regardless of item selection
|
|
1667
|
-
if (current.get("workspace") !== viewName) {
|
|
1970
|
+
if (!isWorkspaceTransitioning && current.get("workspace") !== viewName) {
|
|
1668
1971
|
current.set("workspace", viewName);
|
|
1669
1972
|
}
|
|
1670
|
-
current.delete("view"); // Remove legacy view param
|
|
1671
1973
|
// Sync sidebar state
|
|
1672
1974
|
const currentSidebars = current.get("sidebar") ?? "";
|
|
1673
1975
|
const newSidebars = openSidebars.join(",");
|
|
@@ -1679,6 +1981,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1679
1981
|
current.delete("sidebar");
|
|
1680
1982
|
}
|
|
1681
1983
|
}
|
|
1984
|
+
if (showHelpTerminal) {
|
|
1985
|
+
current.set("help", selectedHelpSectionId ?? "contents");
|
|
1986
|
+
}
|
|
1987
|
+
else {
|
|
1988
|
+
current.delete("help");
|
|
1989
|
+
}
|
|
1682
1990
|
if (!compareMode) {
|
|
1683
1991
|
current.delete("compare");
|
|
1684
1992
|
current.delete("compareLanguage");
|
|
@@ -1703,15 +2011,15 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1703
2011
|
else {
|
|
1704
2012
|
current.delete("wizardid");
|
|
1705
2013
|
}
|
|
1706
|
-
//
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
}
|
|
1713
|
-
else {
|
|
2014
|
+
// Preserve settings-specific parameters while transitioning into Settings too.
|
|
2015
|
+
// Some callers update the URL first and switch the workspace state a moment later.
|
|
2016
|
+
const isSettingsNavigation = viewName === "settings" ||
|
|
2017
|
+
urlWorkspace === "settings" ||
|
|
2018
|
+
urlView === "settings";
|
|
2019
|
+
if (!isSettingsNavigation) {
|
|
1714
2020
|
current.delete("ccpanel");
|
|
2021
|
+
current.delete("providerId");
|
|
2022
|
+
current.delete("modelId");
|
|
1715
2023
|
}
|
|
1716
2024
|
// Preserve reviewId parameter if it exists (for review links)
|
|
1717
2025
|
// This ensures review links don't lose the reviewId when URL is synced
|
|
@@ -1724,17 +2032,42 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1724
2032
|
const browserPathname = typeof window !== "undefined" ? window.location.pathname : pathname;
|
|
1725
2033
|
const newUrl = `${browserPathname}?${current.toString()}`;
|
|
1726
2034
|
const oldUrl = `${browserPathname}${window.location.search}`;
|
|
2035
|
+
const isRestoringLastSyncedUrl = newUrl === lastUrlRef.current;
|
|
1727
2036
|
// Skip pushing to history if we're handling a popstate event (browser back/forward)
|
|
1728
2037
|
// This prevents the URL sync from overwriting the history during back navigation
|
|
1729
2038
|
if (isHandlingPopStateRef.current) {
|
|
1730
2039
|
return;
|
|
1731
2040
|
}
|
|
1732
2041
|
if (newUrl !== oldUrl) {
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
2042
|
+
if (isFirstUrlSyncRef.current ||
|
|
2043
|
+
switchWorkspacePushedRef.current ||
|
|
2044
|
+
isRestoringLastSyncedUrl) {
|
|
2045
|
+
window.history.replaceState(getCurrentHistoryState(), "", newUrl);
|
|
2046
|
+
isFirstUrlSyncRef.current = false;
|
|
2047
|
+
switchWorkspacePushedRef.current = false;
|
|
2048
|
+
}
|
|
2049
|
+
else {
|
|
2050
|
+
window.history.pushState(getCurrentHistoryState(), "", newUrl);
|
|
2051
|
+
}
|
|
1736
2052
|
lastUrlRef.current = newUrl;
|
|
1737
2053
|
}
|
|
2054
|
+
else {
|
|
2055
|
+
// Even when the first sync is a no-op (URL already matches state), consume
|
|
2056
|
+
// the first-sync flag. Otherwise the *next* real change (e.g. user selecting
|
|
2057
|
+
// a different item) would hit the replaceState branch and overwrite the
|
|
2058
|
+
// landing history entry — eating the "back" target and making browser back
|
|
2059
|
+
// skip past the initial page.
|
|
2060
|
+
if (isFirstUrlSyncRef.current) {
|
|
2061
|
+
isFirstUrlSyncRef.current = false;
|
|
2062
|
+
lastUrlRef.current = newUrl;
|
|
2063
|
+
}
|
|
2064
|
+
if (switchWorkspacePushedRef.current) {
|
|
2065
|
+
// A workspace change may already have pushed the exact target URL. If we leave
|
|
2066
|
+
// this flag set, the next unrelated item navigation will incorrectly replace
|
|
2067
|
+
// history instead of pushing a new entry, which breaks browser back/forward.
|
|
2068
|
+
switchWorkspacePushedRef.current = false;
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
1738
2071
|
}, [
|
|
1739
2072
|
currentItemDescriptor,
|
|
1740
2073
|
viewName,
|
|
@@ -1746,6 +2079,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1746
2079
|
fullscreen,
|
|
1747
2080
|
currentWizardId,
|
|
1748
2081
|
openSidebars,
|
|
2082
|
+
showHelpTerminal,
|
|
2083
|
+
selectedHelpSectionId,
|
|
1749
2084
|
]);
|
|
1750
2085
|
useEffect(() => {
|
|
1751
2086
|
async function load() {
|
|
@@ -1842,12 +2177,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1842
2177
|
"en",
|
|
1843
2178
|
version: 0,
|
|
1844
2179
|
};
|
|
1845
|
-
const loadedItem = await itemsRepository.getItem(itemToLoad);
|
|
1846
|
-
if (!loadedItem) {
|
|
1847
|
-
return undefined;
|
|
1848
|
-
}
|
|
1849
2180
|
// ensure this is object has no additional properties
|
|
1850
2181
|
itemToLoad = getItemDescriptor(itemToLoad);
|
|
2182
|
+
const requestedItemKey = makeItemKey(itemToLoad);
|
|
1851
2183
|
// Check if item is already open in any slot - if so, reuse that slot instead of creating a new one
|
|
1852
2184
|
const existingSlotForItem = editorSlots.find((s) => s.itemDescriptor.id === itemToLoad.id &&
|
|
1853
2185
|
s.itemDescriptor.language === itemToLoad.language &&
|
|
@@ -1858,6 +2190,17 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1858
2190
|
!options?.openInNewSlot &&
|
|
1859
2191
|
!options?.targetSlotId) {
|
|
1860
2192
|
const isExistingSlotActive = existingSlotForItem.slotId === activeSlotIdRef.current;
|
|
2193
|
+
if (isExistingSlotActive) {
|
|
2194
|
+
latestGlobalLoadKeyRef.current = requestedItemKey;
|
|
2195
|
+
}
|
|
2196
|
+
const loadedItem = await itemsRepository.getItem(itemToLoad);
|
|
2197
|
+
if (!loadedItem) {
|
|
2198
|
+
return undefined;
|
|
2199
|
+
}
|
|
2200
|
+
if (isExistingSlotActive &&
|
|
2201
|
+
latestGlobalLoadKeyRef.current !== requestedItemKey) {
|
|
2202
|
+
return loadedItem;
|
|
2203
|
+
}
|
|
1861
2204
|
// If forceRefresh is true, update the slot with a new refreshToken to trigger a reload
|
|
1862
2205
|
if (options?.forceRefresh) {
|
|
1863
2206
|
setEditorSlots((prev) => {
|
|
@@ -1932,6 +2275,17 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1932
2275
|
// - If the slot is active (or is becoming active as the first slot), do update.
|
|
1933
2276
|
const isFirstSlot = editorSlots.length === 0;
|
|
1934
2277
|
const shouldUpdateGlobalCurrentItem = isFirstSlot || targetSlotId === activeSlotIdRef.current;
|
|
2278
|
+
if (shouldUpdateGlobalCurrentItem) {
|
|
2279
|
+
latestGlobalLoadKeyRef.current = requestedItemKey;
|
|
2280
|
+
}
|
|
2281
|
+
const loadedItem = await itemsRepository.getItem(itemToLoad);
|
|
2282
|
+
if (!loadedItem) {
|
|
2283
|
+
return undefined;
|
|
2284
|
+
}
|
|
2285
|
+
if (shouldUpdateGlobalCurrentItem &&
|
|
2286
|
+
latestGlobalLoadKeyRef.current !== requestedItemKey) {
|
|
2287
|
+
return loadedItem;
|
|
2288
|
+
}
|
|
1935
2289
|
// Active slot is controlled ONLY by mouse hover - only activate the first slot on initial load
|
|
1936
2290
|
if (isFirstSlot) {
|
|
1937
2291
|
activeSlotIdRef.current = targetSlotId;
|
|
@@ -2039,9 +2393,10 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2039
2393
|
useEffect(() => {
|
|
2040
2394
|
if (fullscreen &&
|
|
2041
2395
|
!searchParams.get("fullscreen") &&
|
|
2042
|
-
!configuration.forceFullscreen
|
|
2396
|
+
!configuration.forceFullscreen &&
|
|
2397
|
+
!isMobile)
|
|
2043
2398
|
setShowFullscreenHint(true);
|
|
2044
|
-
}, [fullscreen, configuration.forceFullscreen, searchParams]);
|
|
2399
|
+
}, [fullscreen, configuration.forceFullscreen, searchParams, isMobile]);
|
|
2045
2400
|
const state = {
|
|
2046
2401
|
page,
|
|
2047
2402
|
configuration,
|
|
@@ -2140,20 +2495,35 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2140
2495
|
? existingOp.progress
|
|
2141
2496
|
: op.progress;
|
|
2142
2497
|
// IMPORTANT: Once canUndo becomes true, never downgrade it to false,
|
|
2143
|
-
// UNLESS this update indicates the operation was undone.
|
|
2144
|
-
//
|
|
2145
|
-
//
|
|
2146
|
-
|
|
2498
|
+
// UNLESS this update indicates the operation was undone.
|
|
2499
|
+
// Runtime logs show long-running undo completion can arrive as:
|
|
2500
|
+
// - existing: canUndo=true, executionStatus=executing
|
|
2501
|
+
// - incoming: canUndo=false, executionStatus=completed
|
|
2502
|
+
// without explicit undone/canRedo flags yet.
|
|
2503
|
+
const isExplicitUndoUpdate = op.undone === true || op.canRedo === true;
|
|
2504
|
+
const isInferredUndoCompletion = existingOp.executionStatus === "executing" &&
|
|
2505
|
+
op.executionStatus === "completed" &&
|
|
2506
|
+
existingOp.canUndo === true &&
|
|
2507
|
+
op.canUndo === false;
|
|
2508
|
+
const isUndoUpdate = isExplicitUndoUpdate || isInferredUndoCompletion;
|
|
2147
2509
|
const mergedCanUndo = isUndoUpdate
|
|
2148
2510
|
? false
|
|
2149
2511
|
: existingOp.canUndo === true
|
|
2150
2512
|
? true
|
|
2151
2513
|
: op.canUndo;
|
|
2514
|
+
const mergedCanRedo = isUndoUpdate
|
|
2515
|
+
? true
|
|
2516
|
+
: op.canRedo ?? existingOp.canRedo;
|
|
2517
|
+
const mergedUndone = isUndoUpdate
|
|
2518
|
+
? true
|
|
2519
|
+
: op.undone ?? existingOp.undone;
|
|
2152
2520
|
const mergedOp = {
|
|
2153
2521
|
...existingOp,
|
|
2154
2522
|
...op,
|
|
2155
2523
|
progress: mergedProgress,
|
|
2156
2524
|
canUndo: mergedCanUndo,
|
|
2525
|
+
canRedo: mergedCanRedo,
|
|
2526
|
+
undone: mergedUndone,
|
|
2157
2527
|
};
|
|
2158
2528
|
// Ensure undone operations always have canRedo: true.
|
|
2159
2529
|
if (mergedOp.undone && !mergedOp.canRedo) {
|
|
@@ -2207,10 +2577,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2207
2577
|
});
|
|
2208
2578
|
// Ref for markOperationComplete callback (needed because operationsContext is created later)
|
|
2209
2579
|
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
2580
|
// WebSocket message handler and connection
|
|
2215
2581
|
const messageHandler = useSocketMessageHandler({
|
|
2216
2582
|
sessionId,
|
|
@@ -2236,28 +2602,31 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2236
2602
|
markOperationCompleteRef.current?.(operationId);
|
|
2237
2603
|
},
|
|
2238
2604
|
});
|
|
2605
|
+
// Concurrent user limit error state
|
|
2606
|
+
const [concurrentUserLimitError, setConcurrentUserLimitError] = useState(null);
|
|
2607
|
+
concurrentUserLimitErrorRef.current = concurrentUserLimitError;
|
|
2608
|
+
const handleRetryConnection = useCallback(() => {
|
|
2609
|
+
setConcurrentUserLimitError(null);
|
|
2610
|
+
// Force reconnection by triggering a new connection attempt
|
|
2611
|
+
// The useEditorWebSocket hook will check availability again
|
|
2612
|
+
const socket = globalThis.editorSocket;
|
|
2613
|
+
if (socket) {
|
|
2614
|
+
socket.close();
|
|
2615
|
+
delete globalThis.editorSocket;
|
|
2616
|
+
}
|
|
2617
|
+
// The hook will automatically attempt to reconnect
|
|
2618
|
+
}, []);
|
|
2239
2619
|
const { socketRef: socketInstanceRef } = useEditorWebSocket({
|
|
2240
2620
|
sessionId,
|
|
2241
2621
|
onMessage: messageHandler,
|
|
2242
2622
|
onOpen: async () => {
|
|
2623
|
+
// Clear concurrent user limit error on successful connection
|
|
2624
|
+
setConcurrentUserLimitError(null);
|
|
2625
|
+
// Startup WebSocket probe may have failed with a blocking error while seats were full;
|
|
2626
|
+
// re-run status checks quietly so "1 error" does not stick after a successful connect.
|
|
2627
|
+
void startupChecks.recheckQuiet();
|
|
2243
2628
|
// Increment socket connection version to trigger re-subscriptions
|
|
2244
2629
|
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
2630
|
// Fetch any running operations on (re)connect for auto-resume
|
|
2262
2631
|
// This ensures the UI shows operations that are still executing
|
|
2263
2632
|
try {
|
|
@@ -2273,24 +2642,37 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2273
2642
|
}
|
|
2274
2643
|
},
|
|
2275
2644
|
onError: (error) => console.error("WebSocket error:", error),
|
|
2645
|
+
onConcurrentUserLimit: (error) => {
|
|
2646
|
+
setConcurrentUserLimitError(error);
|
|
2647
|
+
},
|
|
2648
|
+
onSessionRevoked: () => promptSessionReconnect("session-revoked"),
|
|
2276
2649
|
connectSocket,
|
|
2277
2650
|
requestQuota,
|
|
2278
2651
|
sendClientInfo,
|
|
2652
|
+
setSocketDiagnostics,
|
|
2279
2653
|
});
|
|
2654
|
+
useEffect(() => {
|
|
2655
|
+
const hasMySession = activeSessions.some((s) => s.sessionId === sessionId);
|
|
2656
|
+
if (hasMySession &&
|
|
2657
|
+
socketInstanceRef.current?.readyState === WebSocket.OPEN) {
|
|
2658
|
+
toast.dismiss("session-revoked");
|
|
2659
|
+
}
|
|
2660
|
+
}, [activeSessions, sessionId, socketConnectionVersion, socketInstanceRef]);
|
|
2661
|
+
useEffect(() => {
|
|
2662
|
+
const handleRevoked = (event) => {
|
|
2663
|
+
const customEvent = event;
|
|
2664
|
+
promptSessionReconnect(customEvent?.detail?.reason);
|
|
2665
|
+
};
|
|
2666
|
+
window.addEventListener("parhelia:session-revoked", handleRevoked);
|
|
2667
|
+
return () => {
|
|
2668
|
+
window.removeEventListener("parhelia:session-revoked", handleRevoked);
|
|
2669
|
+
};
|
|
2670
|
+
}, [promptSessionReconnect]);
|
|
2280
2671
|
const sendSocketMessage = useCallback((message) => {
|
|
2281
2672
|
if (socketInstanceRef.current &&
|
|
2282
2673
|
socketInstanceRef.current.readyState === WebSocket.OPEN) {
|
|
2283
2674
|
socketInstanceRef.current.send(JSON.stringify(message));
|
|
2284
2675
|
}
|
|
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
2676
|
}, [socketInstanceRef]);
|
|
2295
2677
|
// URL update helper - defined early so it can be used by workspace/sidebar functions
|
|
2296
2678
|
const updateUrl = useCallback((params) => {
|
|
@@ -2309,7 +2691,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2309
2691
|
? `${browserPathname}?${queryString}`
|
|
2310
2692
|
: browserPathname;
|
|
2311
2693
|
if (typeof window !== "undefined") {
|
|
2312
|
-
window.history.pushState(
|
|
2694
|
+
window.history.pushState(getCurrentHistoryState(), "", newUrl);
|
|
2695
|
+
lastUrlRef.current = newUrl;
|
|
2313
2696
|
}
|
|
2314
2697
|
else {
|
|
2315
2698
|
router.push(newUrl, { scroll: false });
|
|
@@ -2334,8 +2717,13 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2334
2717
|
if (!options?.skipNavigationHistory) {
|
|
2335
2718
|
addNavigationEntry(targetWorkspaceId, item);
|
|
2336
2719
|
}
|
|
2337
|
-
//
|
|
2338
|
-
|
|
2720
|
+
// Mark that we're pushing from switchWorkspace so the URL sync effect
|
|
2721
|
+
// will replaceState instead of pushing a second history entry.
|
|
2722
|
+
switchWorkspacePushedRef.current = true;
|
|
2723
|
+
updateUrl({
|
|
2724
|
+
workspace: targetWorkspaceId,
|
|
2725
|
+
...options?.urlParams,
|
|
2726
|
+
});
|
|
2339
2727
|
if (typeof document.startViewTransition === "function") {
|
|
2340
2728
|
document.startViewTransition(() => {
|
|
2341
2729
|
flushSync(() => {
|
|
@@ -2355,14 +2743,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2355
2743
|
updateUrl,
|
|
2356
2744
|
handleSetShowAgentsPanel,
|
|
2357
2745
|
]);
|
|
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
2746
|
// Helper: get all sidebar IDs that should be preserved (locked sidebars + their stack mates)
|
|
2367
2747
|
const getPreservedSidebarIds = useCallback(() => {
|
|
2368
2748
|
const lockedSet = new Set(lockedSidebarsRef.current);
|
|
@@ -2400,6 +2780,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2400
2780
|
setOpenSidebars(newSidebars);
|
|
2401
2781
|
setLockedSidebars((locked) => locked.filter((id) => id !== sidebarId));
|
|
2402
2782
|
setSidebarStacks((prevStacks) => normalizeSidebarStacks(newSidebars, prevStacks));
|
|
2783
|
+
if (sidebarId === "agents-panel") {
|
|
2784
|
+
setUserPreferences({ showAgentsPanel: false });
|
|
2785
|
+
}
|
|
2403
2786
|
startTransition(() => {
|
|
2404
2787
|
updateUrl({ sidebar: newSidebars.join(",") || undefined });
|
|
2405
2788
|
});
|
|
@@ -2414,34 +2797,69 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2414
2797
|
];
|
|
2415
2798
|
openSidebarsRef.current = newSidebars;
|
|
2416
2799
|
setOpenSidebars(newSidebars);
|
|
2800
|
+
ensureSidebarPinned(sidebarId);
|
|
2417
2801
|
setSidebarStacks((prevStacks) => normalizeSidebarStacks(newSidebars, prevStacks));
|
|
2802
|
+
if (sidebarId === "agents-panel") {
|
|
2803
|
+
setUserPreferences({ showAgentsPanel: true });
|
|
2804
|
+
}
|
|
2805
|
+
// On mobile, close the editor form panel when opening a sidebar
|
|
2806
|
+
if (isMobile) {
|
|
2807
|
+
setMobileEditorPanelOpenRaw(false);
|
|
2808
|
+
}
|
|
2418
2809
|
startTransition(() => {
|
|
2419
2810
|
updateUrl({ sidebar: newSidebars.join(",") || undefined });
|
|
2420
2811
|
});
|
|
2421
|
-
}, [
|
|
2812
|
+
}, [
|
|
2813
|
+
updateUrl,
|
|
2814
|
+
getPreservedSidebarIds,
|
|
2815
|
+
normalizeSidebarStacks,
|
|
2816
|
+
ensureSidebarPinned,
|
|
2817
|
+
setUserPreferences,
|
|
2818
|
+
isMobile,
|
|
2819
|
+
]);
|
|
2422
2820
|
// Ensure a sidebar is open (without toggling it closed if already open)
|
|
2423
|
-
const openSidebar = useCallback((sidebarId) => {
|
|
2821
|
+
const openSidebar = useCallback((sidebarId, options) => {
|
|
2424
2822
|
const currentOpenSidebars = openSidebarsRef.current;
|
|
2425
2823
|
if (currentOpenSidebars.includes(sidebarId)) {
|
|
2426
2824
|
// Already open, nothing to do
|
|
2427
2825
|
return;
|
|
2428
2826
|
}
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2827
|
+
const preservedSet = options?.preserveOpenSidebars
|
|
2828
|
+
? undefined
|
|
2829
|
+
: getPreservedSidebarIds();
|
|
2830
|
+
const newSidebars = options?.preserveOpenSidebars
|
|
2831
|
+
? [...currentOpenSidebars, sidebarId]
|
|
2832
|
+
: [
|
|
2833
|
+
...currentOpenSidebars.filter((id) => preservedSet?.has(id)),
|
|
2834
|
+
sidebarId,
|
|
2835
|
+
];
|
|
2435
2836
|
openSidebarsRef.current = newSidebars;
|
|
2436
2837
|
setOpenSidebars(newSidebars);
|
|
2838
|
+
ensureSidebarPinned(sidebarId);
|
|
2437
2839
|
setSidebarStacks((prevStacks) => normalizeSidebarStacks(newSidebars, prevStacks));
|
|
2840
|
+
if (sidebarId === "agents-panel") {
|
|
2841
|
+
setUserPreferences({ showAgentsPanel: true });
|
|
2842
|
+
}
|
|
2843
|
+
// On mobile, close the editor form panel when opening a sidebar
|
|
2844
|
+
if (isMobile) {
|
|
2845
|
+
setMobileEditorPanelOpenRaw(false);
|
|
2846
|
+
}
|
|
2438
2847
|
startTransition(() => {
|
|
2439
2848
|
updateUrl({ sidebar: newSidebars.join(",") || undefined });
|
|
2440
2849
|
});
|
|
2441
|
-
}, [
|
|
2850
|
+
}, [
|
|
2851
|
+
updateUrl,
|
|
2852
|
+
getPreservedSidebarIds,
|
|
2853
|
+
normalizeSidebarStacks,
|
|
2854
|
+
ensureSidebarPinned,
|
|
2855
|
+
setUserPreferences,
|
|
2856
|
+
isMobile,
|
|
2857
|
+
]);
|
|
2442
2858
|
// Toggle lock state for a sidebar stack (keeps it visible when selecting another)
|
|
2443
2859
|
// When toggling any sidebar, we toggle the entire stack it belongs to
|
|
2444
2860
|
const toggleSidebarLock = useCallback((sidebarId) => {
|
|
2861
|
+
if (isMobile)
|
|
2862
|
+
return;
|
|
2445
2863
|
const currentStacks = sidebarStacksRef.current;
|
|
2446
2864
|
const stackContainingSidebar = currentStacks.find((stack) => stack.includes(sidebarId));
|
|
2447
2865
|
const sidebarIdsToToggle = stackContainingSidebar || [sidebarId];
|
|
@@ -2459,7 +2877,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2459
2877
|
];
|
|
2460
2878
|
}
|
|
2461
2879
|
});
|
|
2462
|
-
}, []);
|
|
2880
|
+
}, [isMobile]);
|
|
2463
2881
|
const stackSidebar = useCallback((sidebarId, targetSidebarId, position = "after") => {
|
|
2464
2882
|
if (!sidebarId || !targetSidebarId || sidebarId === targetSidebarId)
|
|
2465
2883
|
return;
|
|
@@ -2491,6 +2909,38 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2491
2909
|
return normalizeSidebarStacks(openIds, next);
|
|
2492
2910
|
});
|
|
2493
2911
|
}, [normalizeSidebarStacks]);
|
|
2912
|
+
const moveSidebarToColumn = useCallback((sidebarId, targetSidebarId, position = "after") => {
|
|
2913
|
+
if (!sidebarId || !targetSidebarId || sidebarId === targetSidebarId) {
|
|
2914
|
+
return;
|
|
2915
|
+
}
|
|
2916
|
+
const currentOpen = openSidebarsRef.current;
|
|
2917
|
+
const baseOpen = currentOpen.filter((id) => id !== sidebarId);
|
|
2918
|
+
const targetIndex = baseOpen.indexOf(targetSidebarId);
|
|
2919
|
+
if (targetIndex === -1) {
|
|
2920
|
+
return;
|
|
2921
|
+
}
|
|
2922
|
+
const insertIndex = position === "before" ? targetIndex : targetIndex + 1;
|
|
2923
|
+
const nextOpen = [...baseOpen];
|
|
2924
|
+
nextOpen.splice(insertIndex, 0, sidebarId);
|
|
2925
|
+
openSidebarsRef.current = nextOpen;
|
|
2926
|
+
setOpenSidebars(nextOpen);
|
|
2927
|
+
startTransition(() => {
|
|
2928
|
+
updateUrl({ sidebar: nextOpen.join(",") || undefined });
|
|
2929
|
+
});
|
|
2930
|
+
setSidebarStacks((prev) => {
|
|
2931
|
+
const normalized = normalizeSidebarStacks(nextOpen, prev);
|
|
2932
|
+
const next = normalized
|
|
2933
|
+
.map((stack) => stack.filter((id) => id !== sidebarId))
|
|
2934
|
+
.filter((stack) => stack.length > 0);
|
|
2935
|
+
const targetStackIndex = next.findIndex((stack) => stack.includes(targetSidebarId));
|
|
2936
|
+
if (targetStackIndex === -1) {
|
|
2937
|
+
next.push([sidebarId]);
|
|
2938
|
+
return normalizeSidebarStacks(nextOpen, next);
|
|
2939
|
+
}
|
|
2940
|
+
next.splice(position === "before" ? targetStackIndex : targetStackIndex + 1, 0, [sidebarId]);
|
|
2941
|
+
return normalizeSidebarStacks(nextOpen, next);
|
|
2942
|
+
});
|
|
2943
|
+
}, [normalizeSidebarStacks, updateUrl]);
|
|
2494
2944
|
const unstackSidebar = useCallback((sidebarId) => {
|
|
2495
2945
|
setSidebarStacks((prev) => {
|
|
2496
2946
|
const openIds = openSidebarsRef.current;
|
|
@@ -2536,8 +2986,34 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2536
2986
|
// Get resolved sidebar (with panels materialized)
|
|
2537
2987
|
// Note: This is defined as a function that will be called later when editContext is available
|
|
2538
2988
|
const sidebars = configuration.editor.sidebars ?? [];
|
|
2989
|
+
const taskboardSidebarIds = new Set([
|
|
2990
|
+
"taskboard-project-list",
|
|
2991
|
+
"taskboard-my-tasks",
|
|
2992
|
+
]);
|
|
2993
|
+
const getSidebarsForWorkspace = useCallback((targetWorkspaceId) => {
|
|
2994
|
+
const isTaskboardWorkspace = targetWorkspaceId === "taskboard";
|
|
2995
|
+
const workspaceAllowedSidebarIds = userInfo.workspaces?.find((w) => w.id === targetWorkspaceId)
|
|
2996
|
+
?.sidebars ?? [];
|
|
2997
|
+
return sidebars.filter((s) => {
|
|
2998
|
+
const isTaskboardSidebar = taskboardSidebarIds.has(s.id);
|
|
2999
|
+
if (isTaskboardWorkspace && !isTaskboardSidebar)
|
|
3000
|
+
return false;
|
|
3001
|
+
if (!isTaskboardWorkspace && isTaskboardSidebar)
|
|
3002
|
+
return false;
|
|
3003
|
+
// Always show agents-panel regardless of workspace settings.
|
|
3004
|
+
if (s.id === "agents-panel") {
|
|
3005
|
+
return true;
|
|
3006
|
+
}
|
|
3007
|
+
// If no workspace settings or no sidebars defined for current workspace, show all.
|
|
3008
|
+
if (workspaceAllowedSidebarIds.length === 0) {
|
|
3009
|
+
return true;
|
|
3010
|
+
}
|
|
3011
|
+
// Only show sidebars that are in the allowed list for the current workspace.
|
|
3012
|
+
return workspaceAllowedSidebarIds.includes(s.id);
|
|
3013
|
+
});
|
|
3014
|
+
}, [sidebars, userInfo.workspaces]);
|
|
2539
3015
|
const getResolvedSidebar = useCallback((sidebarId) => {
|
|
2540
|
-
const sidebar =
|
|
3016
|
+
const sidebar = getSidebarsForWorkspace(workspaceId).find((s) => s.id === sidebarId);
|
|
2541
3017
|
if (!sidebar)
|
|
2542
3018
|
return undefined;
|
|
2543
3019
|
// Resolve panel factories using editContextRef to avoid circular dependency
|
|
@@ -2551,7 +3027,35 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2551
3027
|
...sidebar,
|
|
2552
3028
|
panels: resolvedPanels,
|
|
2553
3029
|
};
|
|
2554
|
-
}, [
|
|
3030
|
+
}, [getSidebarsForWorkspace, workspaceId]);
|
|
3031
|
+
useEffect(() => {
|
|
3032
|
+
if (!currentWorkspace.supportsSidebars) {
|
|
3033
|
+
return;
|
|
3034
|
+
}
|
|
3035
|
+
const allowedIds = new Set(getSidebarsForWorkspace(workspaceId).map((sidebar) => sidebar.id));
|
|
3036
|
+
const currentOpen = openSidebarsRef.current;
|
|
3037
|
+
let nextOpen = currentOpen.filter((id) => allowedIds.has(id));
|
|
3038
|
+
if (nextOpen.length === 0 && currentWorkspace.defaultSidebars?.length) {
|
|
3039
|
+
nextOpen = currentWorkspace.defaultSidebars.filter((id) => allowedIds.has(id));
|
|
3040
|
+
}
|
|
3041
|
+
const unchanged = nextOpen.length === currentOpen.length &&
|
|
3042
|
+
nextOpen.every((id, index) => id === currentOpen[index]);
|
|
3043
|
+
if (unchanged) {
|
|
3044
|
+
return;
|
|
3045
|
+
}
|
|
3046
|
+
openSidebarsRef.current = nextOpen;
|
|
3047
|
+
setOpenSidebars(nextOpen);
|
|
3048
|
+
setSidebarStacks((prev) => normalizeSidebarStacks(nextOpen, prev));
|
|
3049
|
+
startTransition(() => {
|
|
3050
|
+
updateUrl({ sidebar: nextOpen.join(",") || undefined });
|
|
3051
|
+
});
|
|
3052
|
+
}, [
|
|
3053
|
+
currentWorkspace,
|
|
3054
|
+
getSidebarsForWorkspace,
|
|
3055
|
+
normalizeSidebarStacks,
|
|
3056
|
+
updateUrl,
|
|
3057
|
+
workspaceId,
|
|
3058
|
+
]);
|
|
2555
3059
|
// Listen for switch-workspace and open-sidebar commands from agents via websocket
|
|
2556
3060
|
useEffect(() => {
|
|
2557
3061
|
const handleAgentMessage = (message) => {
|
|
@@ -2765,9 +3269,45 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2765
3269
|
}
|
|
2766
3270
|
return true;
|
|
2767
3271
|
}, [operations, ignoreBlur, sessionId]);
|
|
3272
|
+
const quickSwitcherEntries = useMemo(() => {
|
|
3273
|
+
const entries = [];
|
|
3274
|
+
const seen = new Set();
|
|
3275
|
+
const pushEntry = (entry) => {
|
|
3276
|
+
const key = entry.item
|
|
3277
|
+
? `${entry.workspaceId}:${entry.item.id}:${entry.item.language}:${entry.item.version}`
|
|
3278
|
+
: `${entry.workspaceId}:no-item`;
|
|
3279
|
+
if (seen.has(key))
|
|
3280
|
+
return;
|
|
3281
|
+
seen.add(key);
|
|
3282
|
+
entries.push(entry);
|
|
3283
|
+
};
|
|
3284
|
+
for (const entry of navigationHistory) {
|
|
3285
|
+
pushEntry(entry);
|
|
3286
|
+
}
|
|
3287
|
+
for (const entry of browseHistory) {
|
|
3288
|
+
if (!entry.id || !entry.language)
|
|
3289
|
+
continue;
|
|
3290
|
+
pushEntry({
|
|
3291
|
+
workspaceId,
|
|
3292
|
+
item: {
|
|
3293
|
+
id: entry.id,
|
|
3294
|
+
language: entry.language,
|
|
3295
|
+
version: entry.version ?? 0,
|
|
3296
|
+
},
|
|
3297
|
+
timestamp: entry.visitedAt
|
|
3298
|
+
? new Date(entry.visitedAt).getTime()
|
|
3299
|
+
: Date.now(),
|
|
3300
|
+
displayName: entry.name || workspaceId,
|
|
3301
|
+
itemName: entry.name,
|
|
3302
|
+
itemPath: entry.path,
|
|
3303
|
+
itemIcon: entry.icon,
|
|
3304
|
+
});
|
|
3305
|
+
}
|
|
3306
|
+
return entries.slice(0, 25);
|
|
3307
|
+
}, [navigationHistory, browseHistory, workspaceId]);
|
|
2768
3308
|
// Quick switcher handlers
|
|
2769
3309
|
const showQuickSwitcher = useCallback((show) => {
|
|
2770
|
-
if (show &&
|
|
3310
|
+
if (show && quickSwitcherEntries.length > 1) {
|
|
2771
3311
|
setQuickSwitcherVisible(true);
|
|
2772
3312
|
// Start with index 1 (second entry - previous entry) for quick switching
|
|
2773
3313
|
setQuickSwitcherSelectedIndex(1);
|
|
@@ -2775,11 +3315,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2775
3315
|
else {
|
|
2776
3316
|
setQuickSwitcherVisible(false);
|
|
2777
3317
|
}
|
|
2778
|
-
}, [
|
|
3318
|
+
}, [quickSwitcherEntries]);
|
|
2779
3319
|
const cycleQuickSwitcher = useCallback((direction) => {
|
|
2780
3320
|
if (!quickSwitcherVisible)
|
|
2781
3321
|
return;
|
|
2782
|
-
const maxItems = Math.min(5,
|
|
3322
|
+
const maxItems = Math.min(5, quickSwitcherEntries.length);
|
|
2783
3323
|
setQuickSwitcherSelectedIndex((current) => {
|
|
2784
3324
|
let newIndex = current;
|
|
2785
3325
|
// Determine grid layout (responsive columns)
|
|
@@ -2824,9 +3364,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2824
3364
|
}
|
|
2825
3365
|
return newIndex;
|
|
2826
3366
|
});
|
|
2827
|
-
}, [quickSwitcherVisible,
|
|
3367
|
+
}, [quickSwitcherVisible, quickSwitcherEntries]);
|
|
2828
3368
|
const handleQuickSwitcherSelect = useCallback((index) => {
|
|
2829
|
-
const selectedEntry =
|
|
3369
|
+
const selectedEntry = quickSwitcherEntries[index];
|
|
2830
3370
|
if (selectedEntry) {
|
|
2831
3371
|
// Determine target workspace: entries with items should go to "editor" workspace
|
|
2832
3372
|
// (fixes issue where browse history entries initialized with wrong workspaceId)
|
|
@@ -2878,23 +3418,46 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2878
3418
|
}
|
|
2879
3419
|
setQuickSwitcherVisible(false);
|
|
2880
3420
|
}, [
|
|
2881
|
-
|
|
3421
|
+
quickSwitcherEntries,
|
|
2882
3422
|
loadItem,
|
|
2883
3423
|
switchWorkspace,
|
|
2884
3424
|
workspaceId,
|
|
2885
3425
|
item,
|
|
2886
3426
|
setNavigationHistory,
|
|
2887
3427
|
]);
|
|
3428
|
+
useEffect(() => {
|
|
3429
|
+
if (typeof window === "undefined" ||
|
|
3430
|
+
process.env.PARHELIA_DEV_MODE !== "true") {
|
|
3431
|
+
return;
|
|
3432
|
+
}
|
|
3433
|
+
window.__parheliaQuickSwitcherTestApi = {
|
|
3434
|
+
open: () => showQuickSwitcher(true),
|
|
3435
|
+
cycle: (direction = "next") => cycleQuickSwitcher(direction),
|
|
3436
|
+
closeWithoutSelection: () => setQuickSwitcherVisible(false),
|
|
3437
|
+
selectCurrent: () => handleQuickSwitcherSelect(quickSwitcherSelectedIndex),
|
|
3438
|
+
getState: () => ({
|
|
3439
|
+
visible: quickSwitcherVisible,
|
|
3440
|
+
selectedIndex: quickSwitcherSelectedIndex,
|
|
3441
|
+
entryCount: quickSwitcherEntries.length,
|
|
3442
|
+
entries: quickSwitcherEntries.map((entry) => ({
|
|
3443
|
+
workspaceId: entry.workspaceId,
|
|
3444
|
+
itemId: entry.item?.id,
|
|
3445
|
+
displayName: entry.displayName,
|
|
3446
|
+
})),
|
|
3447
|
+
}),
|
|
3448
|
+
};
|
|
3449
|
+
return () => {
|
|
3450
|
+
delete window.__parheliaQuickSwitcherTestApi;
|
|
3451
|
+
};
|
|
3452
|
+
}, [
|
|
3453
|
+
showQuickSwitcher,
|
|
3454
|
+
cycleQuickSwitcher,
|
|
3455
|
+
handleQuickSwitcherSelect,
|
|
3456
|
+
quickSwitcherSelectedIndex,
|
|
3457
|
+
]);
|
|
2888
3458
|
const { handleKeyDown } = useKeyboardNavigation({
|
|
2889
3459
|
editContextRef,
|
|
2890
|
-
|
|
2891
|
-
pageViewContext: activePageViewContext,
|
|
2892
|
-
configuration,
|
|
2893
|
-
item,
|
|
2894
|
-
browseHistory,
|
|
2895
|
-
loadItem,
|
|
2896
|
-
showInfoToast,
|
|
2897
|
-
showErrorToast,
|
|
3460
|
+
keyboardCommands: activeKeyboardCommands,
|
|
2898
3461
|
executeCommand,
|
|
2899
3462
|
showQuickSwitcher,
|
|
2900
3463
|
cycleQuickSwitcher,
|
|
@@ -3014,10 +3577,19 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3014
3577
|
}
|
|
3015
3578
|
return null;
|
|
3016
3579
|
};
|
|
3580
|
+
let modifierWasPressedOnMouseDown = false;
|
|
3581
|
+
const handleMouseDown = (event) => {
|
|
3582
|
+
modifierWasPressedOnMouseDown = event.ctrlKey || event.metaKey;
|
|
3583
|
+
};
|
|
3017
3584
|
const handleCtrlClick = async (event) => {
|
|
3018
|
-
// Only proceed if Ctrl
|
|
3585
|
+
// Only proceed if Ctrl/Cmd was already pressed when the mouse interaction started.
|
|
3586
|
+
// This avoids accidental navigation when users press Ctrl after selecting text to copy.
|
|
3587
|
+
if (!modifierWasPressedOnMouseDown)
|
|
3588
|
+
return;
|
|
3589
|
+
// Also require the modifier to still be held for the final click event.
|
|
3019
3590
|
if (!event.ctrlKey && !event.metaKey)
|
|
3020
3591
|
return;
|
|
3592
|
+
modifierWasPressedOnMouseDown = false;
|
|
3021
3593
|
const target = event.target;
|
|
3022
3594
|
const text = getTextFromElement(target);
|
|
3023
3595
|
if (text && isGuid(text)) {
|
|
@@ -3032,8 +3604,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3032
3604
|
skipViewChange: true,
|
|
3033
3605
|
});
|
|
3034
3606
|
if (item) {
|
|
3035
|
-
|
|
3036
|
-
switchView("editor", {
|
|
3607
|
+
switchWorkspace("editor", {
|
|
3037
3608
|
skipNavigationHistory: true,
|
|
3038
3609
|
});
|
|
3039
3610
|
showInfoToast({
|
|
@@ -3059,12 +3630,14 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3059
3630
|
}
|
|
3060
3631
|
};
|
|
3061
3632
|
if (typeof document !== "undefined") {
|
|
3633
|
+
document.addEventListener("mousedown", handleMouseDown, true);
|
|
3062
3634
|
document.addEventListener("click", handleCtrlClick, true);
|
|
3063
3635
|
return () => {
|
|
3636
|
+
document.removeEventListener("mousedown", handleMouseDown, true);
|
|
3064
3637
|
document.removeEventListener("click", handleCtrlClick, true);
|
|
3065
3638
|
};
|
|
3066
3639
|
}
|
|
3067
|
-
}, [loadItem,
|
|
3640
|
+
}, [loadItem, switchWorkspace, showInfoToast, showErrorToast]);
|
|
3068
3641
|
useEffect(() => {
|
|
3069
3642
|
const handleGlobalBlur = () => {
|
|
3070
3643
|
operations.onFieldBlur?.();
|
|
@@ -3102,21 +3675,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3102
3675
|
// Otherwise, only show workspaces that are in the settings
|
|
3103
3676
|
return workspaceIdsFromSettings.includes(w.id) && !w.visible;
|
|
3104
3677
|
});
|
|
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
3678
|
// Handle initial mode setup from URL (only on initial load)
|
|
3121
3679
|
useEffect(() => {
|
|
3122
3680
|
if (!isInitialLoad)
|
|
@@ -3161,7 +3719,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3161
3719
|
// This is especially important when called from the tour, where the current workspace
|
|
3162
3720
|
// might be the editor and URL-only navigation would get "corrected" back.
|
|
3163
3721
|
try {
|
|
3164
|
-
|
|
3722
|
+
switchWorkspace("home", {
|
|
3165
3723
|
skipConfirmation: true,
|
|
3166
3724
|
skipNavigationHistory: true,
|
|
3167
3725
|
});
|
|
@@ -3208,7 +3766,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3208
3766
|
const current = new URLSearchParams(searchParams.toString());
|
|
3209
3767
|
current.delete("version");
|
|
3210
3768
|
current.delete("itemid");
|
|
3211
|
-
current.delete("view"); // Remove legacy param
|
|
3212
3769
|
current.delete("workspace"); // Clear workspace
|
|
3213
3770
|
current.set("create", "1");
|
|
3214
3771
|
const newUrl = `${pathname}?${current.toString()}`;
|
|
@@ -3488,6 +4045,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3488
4045
|
setShowOnlyMyChanges,
|
|
3489
4046
|
filterByCurrentLanguage,
|
|
3490
4047
|
setFilterByCurrentLanguage,
|
|
4048
|
+
historySearchQuery,
|
|
4049
|
+
setHistorySearchQuery,
|
|
3491
4050
|
refreshHistory,
|
|
3492
4051
|
isRefreshing,
|
|
3493
4052
|
activeSessions,
|
|
@@ -3525,19 +4084,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3525
4084
|
workspaceId,
|
|
3526
4085
|
previousWorkspaceId,
|
|
3527
4086
|
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
|
-
}),
|
|
4087
|
+
// Sidebar state
|
|
4088
|
+
availableSidebars: getSidebarsForWorkspace(workspaceId),
|
|
3541
4089
|
openSidebars,
|
|
3542
4090
|
pinnedSidebars,
|
|
3543
4091
|
lockedSidebars,
|
|
@@ -3547,17 +4095,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3547
4095
|
toggleSidebarPin,
|
|
3548
4096
|
toggleSidebarLock,
|
|
3549
4097
|
stackSidebar,
|
|
4098
|
+
moveSidebarToColumn,
|
|
3550
4099
|
unstackSidebar,
|
|
3551
4100
|
reorderSidebarInStack,
|
|
3552
4101
|
reorderPinnedSidebars,
|
|
3553
4102
|
reorderOpenSidebars,
|
|
3554
4103
|
getResolvedSidebar,
|
|
3555
|
-
// Legacy compatibility (deprecated)
|
|
3556
|
-
viewName,
|
|
3557
|
-
previousViewName,
|
|
3558
|
-
switchView,
|
|
3559
|
-
view: currentView,
|
|
3560
|
-
visibleViews,
|
|
3561
4104
|
compareMode,
|
|
3562
4105
|
setCompareMode,
|
|
3563
4106
|
fullscreen,
|
|
@@ -3597,6 +4140,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3597
4140
|
addSocketMessageListener,
|
|
3598
4141
|
sendSocketMessage,
|
|
3599
4142
|
socketConnectionVersion,
|
|
4143
|
+
socketDiagnostics,
|
|
3600
4144
|
currentItemDescriptor,
|
|
3601
4145
|
editorSlots,
|
|
3602
4146
|
activeSlotId,
|
|
@@ -3609,6 +4153,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3609
4153
|
setActiveSlot,
|
|
3610
4154
|
revision,
|
|
3611
4155
|
notifyPageModelReady,
|
|
4156
|
+
pageModelReadyToken,
|
|
3612
4157
|
selectedComment,
|
|
3613
4158
|
setSelectedComment,
|
|
3614
4159
|
comments,
|
|
@@ -3687,6 +4232,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3687
4232
|
setEnableCompletions,
|
|
3688
4233
|
showComponentNavigator,
|
|
3689
4234
|
setShowComponentNavigator: handleSetShowComponentNavigator,
|
|
4235
|
+
isComponentNavigatorOpenForSlot,
|
|
4236
|
+
setComponentNavigatorOpenForSlot,
|
|
3690
4237
|
showAgentsPanel,
|
|
3691
4238
|
setShowAgentsPanel: handleSetShowAgentsPanel,
|
|
3692
4239
|
showMinimap,
|
|
@@ -3698,8 +4245,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3698
4245
|
helpTerminalProfileName,
|
|
3699
4246
|
helpTerminalActiveTab,
|
|
3700
4247
|
setHelpTerminalActiveTab,
|
|
4248
|
+
selectedHelpSectionId,
|
|
4249
|
+
setSelectedHelpSectionId,
|
|
3701
4250
|
showAgentsWorkspaceEditor,
|
|
3702
4251
|
setShowAgentsWorkspaceEditor: handleSetShowAgentsWorkspaceEditor,
|
|
4252
|
+
selectedAgentsWorkspaceAgentId,
|
|
4253
|
+
setSelectedAgentsWorkspaceAgentId,
|
|
3703
4254
|
activeEditorTab,
|
|
3704
4255
|
setActiveEditorTab,
|
|
3705
4256
|
showLayoutComponents,
|
|
@@ -3708,6 +4259,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3708
4259
|
isQuotaExceeded: isQuotaExceeded(),
|
|
3709
4260
|
getQuotaWarningMessage,
|
|
3710
4261
|
isMobile,
|
|
4262
|
+
mobileEditorPanelOpen,
|
|
4263
|
+
setMobileEditorPanelOpen: handleSetMobileEditorPanelOpen,
|
|
3711
4264
|
openDialog,
|
|
3712
4265
|
webSocketMessages,
|
|
3713
4266
|
clearWebSocketMessages: () => setWebSocketMessages([]),
|
|
@@ -3746,7 +4299,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3746
4299
|
configuration,
|
|
3747
4300
|
updateUrl,
|
|
3748
4301
|
workspaceId,
|
|
3749
|
-
switchView,
|
|
3750
4302
|
pathname,
|
|
3751
4303
|
router,
|
|
3752
4304
|
item,
|
|
@@ -3776,7 +4328,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3776
4328
|
currentWorkspace,
|
|
3777
4329
|
previousWorkspaceId,
|
|
3778
4330
|
switchWorkspace,
|
|
3779
|
-
allowedSidebarIds,
|
|
3780
4331
|
openSidebars,
|
|
3781
4332
|
pinnedSidebars,
|
|
3782
4333
|
lockedSidebars,
|
|
@@ -3788,9 +4339,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3788
4339
|
reorderOpenSidebars,
|
|
3789
4340
|
getResolvedSidebar,
|
|
3790
4341
|
viewName,
|
|
3791
|
-
previousViewName,
|
|
3792
|
-
currentView,
|
|
3793
|
-
visibleViews,
|
|
3794
4342
|
compareMode,
|
|
3795
4343
|
// Important: in multi-slot mode the active PageViewContext can change
|
|
3796
4344
|
// without the base `pageViewContext` identity changing (e.g. switching slots).
|
|
@@ -3815,6 +4363,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3815
4363
|
currentItemDescriptor,
|
|
3816
4364
|
revision,
|
|
3817
4365
|
notifyPageModelReady,
|
|
4366
|
+
pageModelReadyToken,
|
|
3818
4367
|
selectedComment,
|
|
3819
4368
|
comments,
|
|
3820
4369
|
availableCommentTags,
|
|
@@ -3833,6 +4382,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3833
4382
|
quickSwitcherSelectedIndex,
|
|
3834
4383
|
handleQuickSwitcherSelect,
|
|
3835
4384
|
webSocketMessages,
|
|
4385
|
+
socketDiagnostics,
|
|
3836
4386
|
factoriesRef,
|
|
3837
4387
|
user,
|
|
3838
4388
|
statusMessage,
|
|
@@ -3841,7 +4391,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3841
4391
|
isQuotaExceeded,
|
|
3842
4392
|
getQuotaWarningMessage,
|
|
3843
4393
|
isMobile,
|
|
4394
|
+
mobileEditorPanelOpen,
|
|
4395
|
+
handleSetMobileEditorPanelOpen,
|
|
3844
4396
|
showComponentNavigator,
|
|
4397
|
+
isComponentNavigatorOpenForSlot,
|
|
4398
|
+
setComponentNavigatorOpenForSlot,
|
|
3845
4399
|
handleSetShowComponentNavigator,
|
|
3846
4400
|
showAgentsPanel,
|
|
3847
4401
|
handleSetShowAgentsPanel,
|
|
@@ -3853,7 +4407,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3853
4407
|
helpTerminalProfileName,
|
|
3854
4408
|
helpTerminalActiveTab,
|
|
3855
4409
|
setHelpTerminalActiveTab,
|
|
4410
|
+
selectedHelpSectionId,
|
|
3856
4411
|
showAgentsWorkspaceEditor,
|
|
4412
|
+
selectedAgentsWorkspaceAgentId,
|
|
3857
4413
|
activeEditorTab,
|
|
3858
4414
|
showLayoutComponents,
|
|
3859
4415
|
openDialog,
|
|
@@ -4098,18 +4654,40 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
4098
4654
|
// prevDependencies.current = currentDependencies;
|
|
4099
4655
|
// editContextRef.current = editContext;
|
|
4100
4656
|
// }, [editContext]);
|
|
4657
|
+
// Auto-open the mobile editor panel for new selection/intent changes, but
|
|
4658
|
+
// keep a manual close sticky for the exact same context.
|
|
4659
|
+
useEffect(() => {
|
|
4660
|
+
if (!isMobile || workspaceId !== "editor")
|
|
4661
|
+
return;
|
|
4662
|
+
if (activeEditorTab) {
|
|
4663
|
+
handleSetMobileEditorPanelOpen(true);
|
|
4664
|
+
return;
|
|
4665
|
+
}
|
|
4666
|
+
if (!(selection.length > 0 || insertMode))
|
|
4667
|
+
return;
|
|
4668
|
+
if (dismissedMobilePanelToken === mobilePanelDismissToken)
|
|
4669
|
+
return;
|
|
4670
|
+
handleSetMobileEditorPanelOpen(true);
|
|
4671
|
+
}, [
|
|
4672
|
+
activeEditorTab,
|
|
4673
|
+
dismissedMobilePanelToken,
|
|
4674
|
+
handleSetMobileEditorPanelOpen,
|
|
4675
|
+
insertMode,
|
|
4676
|
+
isMobile,
|
|
4677
|
+
mobilePanelDismissToken,
|
|
4678
|
+
selection,
|
|
4679
|
+
workspaceId,
|
|
4680
|
+
]);
|
|
4101
4681
|
useEffect(() => {
|
|
4102
4682
|
fieldsEditContext.clearModifiedFields();
|
|
4103
4683
|
}, [currentItemDescriptor]);
|
|
4104
|
-
if (!
|
|
4684
|
+
if (!currentWorkspace)
|
|
4105
4685
|
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: () => {
|
|
4686
|
+
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
4687
|
setTimeout(() => {
|
|
4108
4688
|
setShowFullscreenHint(false);
|
|
4109
4689
|
}, 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: () => {
|
|
4690
|
+
}, "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
4691
|
setIsTourActive(false);
|
|
4114
4692
|
// Remove tour state from URL
|
|
4115
4693
|
// Use history.replaceState instead of router.replace to avoid triggering React navigation
|
|
@@ -4119,8 +4697,14 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
4119
4697
|
const newUrl = queryString
|
|
4120
4698
|
? `${window.location.pathname}?${queryString}`
|
|
4121
4699
|
: 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, {}) })),
|
|
4700
|
+
window.history.replaceState(getCurrentHistoryState(), "", newUrl);
|
|
4701
|
+
}, configuration: configuration, restoredFromUrl: tourRestoredRef.current })), _jsx(FeatureGate, { feature: LicenseFeatures.AI, children: _jsx(GuidanceOverlay, {}) }), _jsx(FeatureGate, { feature: LicenseFeatures.AI, children: _jsx(AgentDialogHandler, {}) })] }));
|
|
4702
|
+
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" ||
|
|
4703
|
+
currentWorkspace.id === "taskboard") &&
|
|
4704
|
+
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) => {
|
|
4705
|
+
if (!open) {
|
|
4706
|
+
setConcurrentUserLimitError(null);
|
|
4707
|
+
}
|
|
4708
|
+
}, 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
4709
|
}
|
|
4126
4710
|
//# sourceMappingURL=EditorShell.js.map
|