@parhelia/core 0.1.12570 → 0.1.12585

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