@parhelia/core 0.1.12910 → 0.1.12912

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 (185) hide show
  1. package/dist/agents-view/AgentGalleryView.d.ts +1 -1
  2. package/dist/agents-view/AgentGalleryView.js +3 -2
  3. package/dist/agents-view/AgentGalleryView.js.map +1 -1
  4. package/dist/agents-view/AgentsView.js +1 -1
  5. package/dist/agents-view/AgentsView.js.map +1 -1
  6. package/dist/components/PageHeader.d.ts +7 -2
  7. package/dist/components/PageHeader.js +8 -3
  8. package/dist/components/PageHeader.js.map +1 -1
  9. package/dist/components/ui/UserPicker.d.ts +5 -1
  10. package/dist/components/ui/UserPicker.js +17 -3
  11. package/dist/components/ui/UserPicker.js.map +1 -1
  12. package/dist/components/ui/button.js +1 -1
  13. package/dist/components/ui/button.js.map +1 -1
  14. package/dist/components/ui/command.js +1 -1
  15. package/dist/components/ui/delete-button.d.ts +1 -1
  16. package/dist/components/ui/delete-button.js +2 -2
  17. package/dist/components/ui/delete-button.js.map +1 -1
  18. package/dist/components/ui/dropdown-menu.js +3 -3
  19. package/dist/components/ui/input.js +1 -1
  20. package/dist/components/ui/input.js.map +1 -1
  21. package/dist/components/ui/label.js +1 -1
  22. package/dist/components/ui/select.d.ts +2 -0
  23. package/dist/components/ui/select.js +8 -6
  24. package/dist/components/ui/select.js.map +1 -1
  25. package/dist/components/ui/textarea.js +1 -3
  26. package/dist/components/ui/textarea.js.map +1 -1
  27. package/dist/config/config.js +5 -0
  28. package/dist/config/config.js.map +1 -1
  29. package/dist/editor/ContentTree.d.ts +3 -2
  30. package/dist/editor/ContentTree.js +4 -4
  31. package/dist/editor/ContentTree.js.map +1 -1
  32. package/dist/editor/EditorWarning.js +1 -1
  33. package/dist/editor/EditorWarning.js.map +1 -1
  34. package/dist/editor/FieldHistory.js +6 -4
  35. package/dist/editor/FieldHistory.js.map +1 -1
  36. package/dist/editor/ai/Agents.js +17 -23
  37. package/dist/editor/ai/Agents.js.map +1 -1
  38. package/dist/editor/ai/ContentInspectorPopover.js +2 -1
  39. package/dist/editor/ai/ContentInspectorPopover.js.map +1 -1
  40. package/dist/editor/ai/agentCollection.d.ts +2 -0
  41. package/dist/editor/ai/agentCollection.js +8 -0
  42. package/dist/editor/ai/agentCollection.js.map +1 -0
  43. package/dist/editor/ai/agentMessageHelpers.d.ts +1 -0
  44. package/dist/editor/ai/agentMessageHelpers.js +4 -1
  45. package/dist/editor/ai/agentMessageHelpers.js.map +1 -1
  46. package/dist/editor/ai/terminal/agentSessionSnapshot.js +4 -1
  47. package/dist/editor/ai/terminal/agentSessionSnapshot.js.map +1 -1
  48. package/dist/editor/ai/terminal/components/AgentLoadingContent.js +2 -2
  49. package/dist/editor/ai/terminal/components/AgentLoadingContent.js.map +1 -1
  50. package/dist/editor/ai/terminal/components/AiResponseMessage.js +9 -3
  51. package/dist/editor/ai/terminal/components/AiResponseMessage.js.map +1 -1
  52. package/dist/editor/ai/terminal/components/EditOperationsPanel.js +3 -3
  53. package/dist/editor/ai/terminal/components/EditOperationsPanel.js.map +1 -1
  54. package/dist/editor/ai/terminal/useAgentDraftInitializer.js +3 -1
  55. package/dist/editor/ai/terminal/useAgentDraftInitializer.js.map +1 -1
  56. package/dist/editor/ai/terminal/useAgentLoadErrorHandler.js +3 -1
  57. package/dist/editor/ai/terminal/useAgentLoadErrorHandler.js.map +1 -1
  58. package/dist/editor/ai/terminal/useAgentSessionFacade.js +7 -3
  59. package/dist/editor/ai/terminal/useAgentSessionFacade.js.map +1 -1
  60. package/dist/editor/bridge/protocol.d.ts +13 -0
  61. package/dist/editor/bridge/protocol.js +2 -1
  62. package/dist/editor/bridge/protocol.js.map +1 -1
  63. package/dist/editor/client/EditorShell.js +179 -30
  64. package/dist/editor/client/EditorShell.js.map +1 -1
  65. package/dist/editor/client/editContext.d.ts +7 -5
  66. package/dist/editor/client/editContext.js.map +1 -1
  67. package/dist/editor/client/errorToast.d.ts +12 -0
  68. package/dist/editor/client/errorToast.js +67 -0
  69. package/dist/editor/client/errorToast.js.map +1 -0
  70. package/dist/editor/client/helpers.d.ts +2 -4
  71. package/dist/editor/client/helpers.js +3 -2
  72. package/dist/editor/client/helpers.js.map +1 -1
  73. package/dist/editor/client/hooks/useSocketMessageHandler.js +5 -3
  74. package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
  75. package/dist/editor/client/operations.d.ts +2 -4
  76. package/dist/editor/client/operations.js +11 -2
  77. package/dist/editor/client/operations.js.map +1 -1
  78. package/dist/editor/client/ui/FullscreenControls.d.ts +0 -1
  79. package/dist/editor/client/ui/FullscreenControls.js +4 -17
  80. package/dist/editor/client/ui/FullscreenControls.js.map +1 -1
  81. package/dist/editor/field-types/DateFieldEditor.js +1 -1
  82. package/dist/editor/field-types/DateFieldEditor.js.map +1 -1
  83. package/dist/editor/field-types/DateTimeFieldEditor.js +1 -1
  84. package/dist/editor/field-types/DateTimeFieldEditor.js.map +1 -1
  85. package/dist/editor/field-types/useTextCompletion.js +12 -43
  86. package/dist/editor/field-types/useTextCompletion.js.map +1 -1
  87. package/dist/editor/media-selector/AiImageSearchPrompt.js +8 -17
  88. package/dist/editor/media-selector/AiImageSearchPrompt.js.map +1 -1
  89. package/dist/editor/menubar/toolbar-sections/DeviceSwitchToolbar.d.ts +6 -1
  90. package/dist/editor/menubar/toolbar-sections/DeviceSwitchToolbar.js +21 -13
  91. package/dist/editor/menubar/toolbar-sections/DeviceSwitchToolbar.js.map +1 -1
  92. package/dist/editor/page-editor-chrome/BridgeInlineFormatOverlay.js +6 -5
  93. package/dist/editor/page-editor-chrome/BridgeInlineFormatOverlay.js.map +1 -1
  94. package/dist/editor/page-editor-chrome/FrameMenu.js +8 -2
  95. package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
  96. package/dist/editor/page-editor-chrome/InsertComponentMenu.js +1 -1
  97. package/dist/editor/page-editor-chrome/InsertComponentMenu.js.map +1 -1
  98. package/dist/editor/page-editor-chrome/overlay/IframeOverlayProvider.d.ts +3 -1
  99. package/dist/editor/page-editor-chrome/overlay/IframeOverlayProvider.js +34 -2
  100. package/dist/editor/page-editor-chrome/overlay/IframeOverlayProvider.js.map +1 -1
  101. package/dist/editor/page-viewer/MiniMap.d.ts +5 -1
  102. package/dist/editor/page-viewer/MiniMap.js +21 -9
  103. package/dist/editor/page-viewer/MiniMap.js.map +1 -1
  104. package/dist/editor/page-viewer/PageViewerFrame.js +291 -4
  105. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  106. package/dist/editor/page-viewer/RenderingParametersSection.js +1 -1
  107. package/dist/editor/page-viewer/RenderingParametersSection.js.map +1 -1
  108. package/dist/editor/page-viewer/bridgeFieldPatch.js +2 -1
  109. package/dist/editor/page-viewer/bridgeFieldPatch.js.map +1 -1
  110. package/dist/editor/reviews/CreateReviewConfirmStep.d.ts +1 -2
  111. package/dist/editor/reviews/CreateReviewConfirmStep.js +3 -3
  112. package/dist/editor/reviews/CreateReviewConfirmStep.js.map +1 -1
  113. package/dist/editor/reviews/CreateReviewDialog.js +27 -3
  114. package/dist/editor/reviews/CreateReviewDialog.js.map +1 -1
  115. package/dist/editor/reviews/DecisionsMatrix.js +1 -1
  116. package/dist/editor/reviews/DecisionsMatrix.js.map +1 -1
  117. package/dist/editor/services/contentService.d.ts +3 -2
  118. package/dist/editor/services/contentService.js +19 -5
  119. package/dist/editor/services/contentService.js.map +1 -1
  120. package/dist/editor/services/contextService.d.ts +8 -0
  121. package/dist/editor/services/contextService.js +19 -0
  122. package/dist/editor/services/contextService.js.map +1 -1
  123. package/dist/editor/services/textCompletionService.d.ts +14 -0
  124. package/dist/editor/services/textCompletionService.js +45 -0
  125. package/dist/editor/services/textCompletionService.js.map +1 -0
  126. package/dist/editor/settings/About.js +40 -263
  127. package/dist/editor/settings/About.js.map +1 -1
  128. package/dist/editor/settings/SettingsBreadcrumb.js +5 -1
  129. package/dist/editor/settings/SettingsBreadcrumb.js.map +1 -1
  130. package/dist/editor/settings/panels/AgentToolsPanel.js +3 -7
  131. package/dist/editor/settings/panels/AgentToolsPanel.js.map +1 -1
  132. package/dist/editor/settings/panels/AgentsPanel.js +2 -3
  133. package/dist/editor/settings/panels/AgentsPanel.js.map +1 -1
  134. package/dist/editor/settings/panels/ClusterInstancesPanel.js +2 -3
  135. package/dist/editor/settings/panels/ClusterInstancesPanel.js.map +1 -1
  136. package/dist/editor/settings/panels/FieldTypeSerializersPanel.js +2 -4
  137. package/dist/editor/settings/panels/FieldTypeSerializersPanel.js.map +1 -1
  138. package/dist/editor/settings/panels/JavaScriptToolsPanel.js +2 -3
  139. package/dist/editor/settings/panels/JavaScriptToolsPanel.js.map +1 -1
  140. package/dist/editor/settings/panels/McpToolsPanel.js +3 -4
  141. package/dist/editor/settings/panels/McpToolsPanel.js.map +1 -1
  142. package/dist/editor/settings/panels/ProjectTemplatesPanel.js +2 -3
  143. package/dist/editor/settings/panels/ProjectTemplatesPanel.js.map +1 -1
  144. package/dist/editor/setup-wizard/steps/LicenseActivationStep.d.ts +3 -1
  145. package/dist/editor/setup-wizard/steps/LicenseActivationStep.js +9 -5
  146. package/dist/editor/setup-wizard/steps/LicenseActivationStep.js.map +1 -1
  147. package/dist/editor/setup-wizard/steps/LicenseEmailStep.d.ts +2 -1
  148. package/dist/editor/setup-wizard/steps/LicenseEmailStep.js +6 -3
  149. package/dist/editor/setup-wizard/steps/LicenseEmailStep.js.map +1 -1
  150. package/dist/editor/sidebar/EditHistory.js +11 -3
  151. package/dist/editor/sidebar/EditHistory.js.map +1 -1
  152. package/dist/editor/sidebar/WorkspaceRailMobile.js +1 -3
  153. package/dist/editor/sidebar/WorkspaceRailMobile.js.map +1 -1
  154. package/dist/editor/ui/ItemCollectionEditor.d.ts +6 -2
  155. package/dist/editor/ui/ItemCollectionEditor.js +163 -84
  156. package/dist/editor/ui/ItemCollectionEditor.js.map +1 -1
  157. package/dist/editor/ui/LoadingState.js +4 -4
  158. package/dist/editor/ui/LoadingState.js.map +1 -1
  159. package/dist/editor/ui/PerfectTree.d.ts +5 -1
  160. package/dist/editor/ui/PerfectTree.js +10 -6
  161. package/dist/editor/ui/PerfectTree.js.map +1 -1
  162. package/dist/editor/ui/SimpleIconButton.js +1 -1
  163. package/dist/editor/ui/TreeListSelector.d.ts +6 -2
  164. package/dist/editor/ui/TreeListSelector.js +2 -2
  165. package/dist/editor/ui/TreeListSelector.js.map +1 -1
  166. package/dist/index.d.ts +4 -1
  167. package/dist/index.js +3 -1
  168. package/dist/index.js.map +1 -1
  169. package/dist/revision.d.ts +2 -2
  170. package/dist/revision.js +2 -2
  171. package/dist/splash-screen/ModernSplashScreen.d.ts +2 -0
  172. package/dist/splash-screen/ModernSplashScreen.js +8 -3
  173. package/dist/splash-screen/ModernSplashScreen.js.map +1 -1
  174. package/dist/task-board/components/ProjectDashboard.js +2 -1
  175. package/dist/task-board/components/ProjectDashboard.js.map +1 -1
  176. package/dist/task-board/components/ProjectPropertiesPanel.js +1 -1
  177. package/dist/task-board/components/ProjectPropertiesPanel.js.map +1 -1
  178. package/dist/task-board/components/TaskAssigneeBadge.d.ts +1 -1
  179. package/dist/task-board/components/TaskAssigneeBadge.js +1 -1
  180. package/dist/task-board/components/TaskAssigneeBadge.js.map +1 -1
  181. package/dist/task-board/components/TaskAssigneePicker.js +1 -1
  182. package/dist/task-board/components/TaskAssigneePicker.js.map +1 -1
  183. package/dist/task-board/components/TaskRow.js +1 -1
  184. package/dist/task-board/components/TaskRow.js.map +1 -1
  185. package/package.json +1 -1
@@ -9,6 +9,7 @@ import { useRouter, useSearchParams, usePathname } from "./navigation";
9
9
  import { findComponent, getComponentById } from "../componentTreeHelper";
10
10
  import { getOperationsContext } from "./operations";
11
11
  import { handleErrorResult } from "./helpers";
12
+ import { showExpandableErrorToast, } from "./errorToast";
12
13
  import { isManualShowMeFocusTransfer, isManualShowMeTarget, } from "./manualShowMeFocus";
13
14
  import { executeFieldAction as executeFieldServerAction, connectSocket, getEditHistory, getRunningOperations, reconnectSession, releaseFieldLocks, validateItems, } from "../services/editService";
14
15
  import { emitAgentDocumentsChanged } from "../services/agentService";
@@ -22,6 +23,7 @@ import "react-json-view-lite/dist/index.css";
22
23
  import { MediaSelector, } from "../media-selector/MediaSelector";
23
24
  import { getComponentCommands } from "../commands/componentCommands";
24
25
  import { getLanguagesAndVersions, } from "../services/contentService";
26
+ import { getEditorSettings } from "../services/systemService";
25
27
  import { getAllFavorites } from "../services/favouritesService";
26
28
  import ConfirmationDialog from "../ConfirmationDialog";
27
29
  import { getItemDescriptor } from "../utils";
@@ -67,12 +69,37 @@ import { useGlobalEditorKeyDown } from "./hooks/useGlobalEditorKeyDown";
67
69
  import { useStartupChecks } from "../settings/status/index";
68
70
  import { EditorLoadingOverlay } from "../EditorLoadingOverlay";
69
71
  import { FeatureGate, LicenseFeatures, LicenseProvider, LicenseOverlay, } from "../../licensing";
72
+ const GLOBAL_SESSION_HISTORY_LIMIT = 200;
73
+ const GLOBAL_HISTORY_LIMIT = 200;
74
+ const GLOBAL_SESSION_HISTORY_LOAD_MORE_INCREMENT = 50;
75
+ const GLOBAL_HISTORY_LOAD_MORE_INCREMENT = 100;
76
+ function getInitialGlobalHistoryLimit(filterBySession) {
77
+ return filterBySession ? GLOBAL_SESSION_HISTORY_LIMIT : GLOBAL_HISTORY_LIMIT;
78
+ }
79
+ function getGlobalHistoryLoadMoreIncrement(filterBySession) {
80
+ return filterBySession
81
+ ? GLOBAL_SESSION_HISTORY_LOAD_MORE_INCREMENT
82
+ : GLOBAL_HISTORY_LOAD_MORE_INCREMENT;
83
+ }
70
84
  // Sentinel written to the `sidebar` URL param when the user has explicitly closed
71
85
  // every sidebar. Distinguishes "no preference yet" (param absent) from "user wants
72
86
  // nothing open" (param present with this value) so reload can honor the user's intent.
73
87
  const SIDEBAR_NONE_SENTINEL = "none";
88
+ const PARHELIA_SETTINGS_ITEM_ID = "a2f3c168bb2841ff9673ee7f3cc624af";
89
+ const PARHELIA_SETTINGS_ITEM_PATH = "/sitecore/system/parhelia/settings";
90
+ const PARHELIA_SHOW_MINIMAP_FIELD_ID = "e75976d2179e4578b4140147b7525fa5";
74
91
  const sidebarUrlValue = (ids) => ids.length ? ids.join(",") : SIDEBAR_NONE_SENTINEL;
75
92
  // moved to hooks/useQuota
93
+ function isParheliaSettingsSnapshotChange(changes) {
94
+ return changes.some((change) => {
95
+ const changedItemId = cleanId(change.item?.id);
96
+ const changedItemPath = change.item?.path?.toLowerCase();
97
+ const changedFieldIds = change.changes.fields?.map((fieldId) => cleanId(fieldId));
98
+ return (changedItemId === PARHELIA_SETTINGS_ITEM_ID ||
99
+ changedItemPath === PARHELIA_SETTINGS_ITEM_PATH ||
100
+ changedFieldIds?.includes(PARHELIA_SHOW_MINIMAP_FIELD_ID));
101
+ });
102
+ }
76
103
  function getInitialOpenSidebars(configuration, workspaceId) {
77
104
  const workspace = configuration.editor.workspaces?.find((candidate) => candidate.id === workspaceId);
78
105
  if (workspace?.supportsSidebars && workspace.defaultSidebars?.length) {
@@ -143,6 +170,10 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
143
170
  const router = useRouter();
144
171
  const pathname = usePathname();
145
172
  const searchParams = useSearchParams();
173
+ const [effectiveParheliaSettings, setEffectiveParheliaSettings] = useState(parheliaSettings);
174
+ const [effectiveParheliaSettingsLoaded, setEffectiveParheliaSettingsLoaded] = useState(parheliaSettingsLoaded === true);
175
+ const parheliaSettingsRefreshInFlightRef = useRef(null);
176
+ const minimapEnabledInSettings = effectiveParheliaSettings?.showMinimap !== false;
146
177
  const [selection, setSelection] = useState([]);
147
178
  const [contentTreeSelection, setContentTreeSelection] = useState([]);
148
179
  const [selectedForInsertion, setSelectedForInsertion] = useState("");
@@ -159,6 +190,34 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
159
190
  const confirmationDialogRef = useRef(null);
160
191
  const contextMenuRef = useRef(null);
161
192
  const editContextRef = useRef(undefined);
193
+ useEffect(() => {
194
+ setEffectiveParheliaSettings(parheliaSettings);
195
+ setEffectiveParheliaSettingsLoaded(parheliaSettingsLoaded === true);
196
+ }, [parheliaSettings, parheliaSettingsLoaded]);
197
+ const refreshParheliaSettings = useCallback(async () => {
198
+ if (parheliaSettingsRefreshInFlightRef.current) {
199
+ return parheliaSettingsRefreshInFlightRef.current;
200
+ }
201
+ const refresh = getEditorSettings()
202
+ .then((result) => {
203
+ if (result.type === "success" && result.data) {
204
+ setEffectiveParheliaSettings(result.data);
205
+ setEffectiveParheliaSettingsLoaded(true);
206
+ return;
207
+ }
208
+ console.warn("Failed to refresh Parhelia settings:", result.summary || result.details);
209
+ })
210
+ .catch((error) => {
211
+ console.error("Failed to refresh Parhelia settings:", error);
212
+ });
213
+ parheliaSettingsRefreshInFlightRef.current = refresh;
214
+ void refresh.finally(() => {
215
+ if (parheliaSettingsRefreshInFlightRef.current === refresh) {
216
+ parheliaSettingsRefreshInFlightRef.current = null;
217
+ }
218
+ });
219
+ return refresh;
220
+ }, []);
162
221
  const [currentOverlay, setCurrentOverlay] = useState();
163
222
  const [contextMenuSource, setContextMenuSource] = useState();
164
223
  const [item, setItem] = useState();
@@ -192,6 +251,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
192
251
  const [showOnlyMyChanges, setShowOnlyMyChanges] = useState(true);
193
252
  const [filterByCurrentLanguage, setFilterByCurrentLanguage] = useState(true);
194
253
  const [historySearchQuery, setHistorySearchQuery] = useState("");
254
+ const [isHistoryLoading, setIsHistoryLoading] = useState(false);
255
+ const [isHistoryLoadingMore, setIsHistoryLoadingMore] = useState(false);
256
+ const [canLoadMoreHistory, setCanLoadMoreHistory] = useState(false);
257
+ const [globalHistoryLimit, setGlobalHistoryLimit] = useState(GLOBAL_SESSION_HISTORY_LIMIT);
258
+ const historyLoadingRequestCountRef = useRef(0);
259
+ const historyLoadingMoreRequestCountRef = useRef(0);
195
260
  const [recentEdits, setRecentEdits] = useState([]);
196
261
  const addRecentEdit = useCallback((edit) => {
197
262
  setRecentEdits((prevEditedFields) => {
@@ -696,6 +761,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
696
761
  const [editorFormHintVisible, setEditorFormHintVisible] = useState(false);
697
762
  const editorFormToggleButtonRef = useRef(null);
698
763
  const [showMinimap, setShowMinimap] = useState(userPreferences.showMinimap ?? true);
764
+ const effectiveShowMinimap = minimapEnabledInSettings && showMinimap;
699
765
  const [showHelpTerminal, setShowHelpTerminal] = useState(false);
700
766
  const [helpTerminalInitialPrompt, setHelpTerminalInitialPrompt] = useState(undefined);
701
767
  const [helpTerminalProfileName, setHelpTerminalProfileName] = useState(undefined);
@@ -803,13 +869,29 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
803
869
  const [currentPageItem, setCurrentPageItem] = useState();
804
870
  // Create itemsRepository with current page item (will be updated when page loads)
805
871
  const itemsRepository = useItemsRepository(addRecentEdit, currentPageItem, getFieldModificationStores);
872
+ // The fallback is a null-object: it backs activePageViewContext only when no slot
873
+ // context is registered (the startup gap, or all slots closed). Its *resolved page* is
874
+ // rendered on screen ONLY by the fullscreen branch (the <PageViewerFrame> in the
875
+ // `fullscreen ? (...)` render); in non-fullscreen the visible iframe comes from the
876
+ // slot's own primaryPageViewContext. So only feed it a descriptor when it is actually
877
+ // the render source — otherwise it pointlessly re-resolves the page the slot already
878
+ // resolves, producing a duplicate /parhelia/resolve at startup.
879
+ const hasActiveSlotContext = !!(activeSlotId && slotContexts.get(activeSlotId));
806
880
  const fallbackPageViewContext = usePageViewContext({
807
- pageItemDescriptor: currentItemDescriptor,
881
+ pageItemDescriptor: fullscreen && !hasActiveSlotContext ? currentItemDescriptor : undefined,
808
882
  itemsRepository,
809
883
  configuration,
810
- parheliaSettings,
884
+ parheliaSettings: effectiveParheliaSettings,
811
885
  layoutMode,
812
886
  });
887
+ useEffect(() => {
888
+ return itemsRepository.subscribeItemsChanged((changes) => {
889
+ const persistedChanges = changes.filter((change) => change.source !== "local-field-update");
890
+ if (isParheliaSettingsSnapshotChange(persistedChanges)) {
891
+ void refreshParheliaSettings();
892
+ }
893
+ });
894
+ }, [itemsRepository, refreshParheliaSettings]);
813
895
  const registerSlotContext = useCallback((slotId, ctx) => {
814
896
  setSlotContexts((prev) => {
815
897
  if (prev.get(slotId) === ctx)
@@ -1929,6 +2011,34 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
1929
2011
  }
1930
2012
  return [];
1931
2013
  }, []);
2014
+ const getEditHistoryWithLoading = useCallback(async (options, loadingMode = "initial") => {
2015
+ const loadingMore = loadingMode === "more";
2016
+ if (loadingMore) {
2017
+ historyLoadingMoreRequestCountRef.current += 1;
2018
+ setIsHistoryLoadingMore(true);
2019
+ }
2020
+ else {
2021
+ historyLoadingRequestCountRef.current += 1;
2022
+ setIsHistoryLoading(true);
2023
+ }
2024
+ try {
2025
+ return await getEditHistory(options);
2026
+ }
2027
+ finally {
2028
+ if (loadingMore) {
2029
+ historyLoadingMoreRequestCountRef.current = Math.max(0, historyLoadingMoreRequestCountRef.current - 1);
2030
+ if (historyLoadingMoreRequestCountRef.current === 0) {
2031
+ setIsHistoryLoadingMore(false);
2032
+ }
2033
+ }
2034
+ else {
2035
+ historyLoadingRequestCountRef.current = Math.max(0, historyLoadingRequestCountRef.current - 1);
2036
+ if (historyLoadingRequestCountRef.current === 0) {
2037
+ setIsHistoryLoading(false);
2038
+ }
2039
+ }
2040
+ }
2041
+ }, []);
1932
2042
  useEffect(() => {
1933
2043
  // Read fresh page from the mutable slot context ref chain.
1934
2044
  // The slot context uses a stable ref that is mutated in-place (see editorSlotContext.ts),
@@ -1949,11 +2059,19 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
1949
2059
  const loadHistory = useDebouncedCallback(async () => {
1950
2060
  if (!sessionId)
1951
2061
  return;
1952
- const result = await getEditHistory({ sessionId });
2062
+ const requestedLimit = GLOBAL_SESSION_HISTORY_LIMIT;
2063
+ const result = await getEditHistoryWithLoading({
2064
+ sessionId,
2065
+ limit: requestedLimit + 1,
2066
+ });
1953
2067
  if (handleErrorResult(result, ui, state))
1954
2068
  return;
2069
+ const nextOperations = normalizeEditHistoryPayload(result.data);
2070
+ const limitedOperations = nextOperations.slice(0, requestedLimit);
2071
+ setGlobalHistoryLimit(requestedLimit);
2072
+ setCanLoadMoreHistory(nextOperations.length > requestedLimit);
1955
2073
  setEditHistory((prev) => {
1956
- const next = normalizeEditHistoryPayload(result.data);
2074
+ const next = limitedOperations;
1957
2075
  if (!prev.length)
1958
2076
  return sortEditHistoryByDateDesc(next);
1959
2077
  if (!next.length)
@@ -2004,7 +2122,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
2004
2122
  return sortEditHistoryByDateDesc(merged);
2005
2123
  });
2006
2124
  }, 600);
2007
- const refreshHistory = useCallback(async (mode, filterBySession, filterByLanguage) => {
2125
+ const refreshHistory = useCallback(async (mode, filterBySession, filterByLanguage, historyLimitOverride, loadingMode = "initial") => {
2008
2126
  const currentMode = mode || historyMode;
2009
2127
  // Use showOnlyMyChanges state if filterBySession not explicitly provided
2010
2128
  const shouldFilterBySession = filterBySession !== undefined ? filterBySession : showOnlyMyChanges;
@@ -2045,14 +2163,16 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
2045
2163
  if (currentMode === "global") {
2046
2164
  // Global mode: optionally filter by session and/or language
2047
2165
  const currentLanguage = item?.descriptor?.language || currentItemDescriptor?.language;
2048
- const result = await getEditHistory({
2166
+ const requestedLimit = historyLimitOverride ??
2167
+ getInitialGlobalHistoryLimit(shouldFilterBySession);
2168
+ const result = await getEditHistoryWithLoading({
2049
2169
  sessionId: shouldFilterBySession ? sessionId : undefined,
2050
- limit: shouldFilterBySession ? 100 : 500,
2170
+ limit: requestedLimit + 1,
2051
2171
  language: shouldFilterByLanguage && currentLanguage
2052
2172
  ? currentLanguage
2053
2173
  : undefined,
2054
2174
  query: historyQuery,
2055
- });
2175
+ }, loadingMode);
2056
2176
  if (handleErrorResult(result, ui, state)) {
2057
2177
  console.error("[EditorShell] Failed to load history:", result);
2058
2178
  return;
@@ -2060,8 +2180,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
2060
2180
  if (isStaleHistoryResponse()) {
2061
2181
  return;
2062
2182
  }
2183
+ const nextOperations = normalizeEditHistoryPayload(result.data);
2184
+ const limitedOperations = nextOperations.slice(0, requestedLimit);
2185
+ setGlobalHistoryLimit(requestedLimit);
2186
+ setCanLoadMoreHistory(nextOperations.length > requestedLimit);
2063
2187
  setEditHistory((prev) => {
2064
- const next = normalizeEditHistoryPayload(result.data);
2188
+ const next = limitedOperations;
2065
2189
  const scope = {
2066
2190
  mode: currentMode,
2067
2191
  filterBySession: shouldFilterBySession,
@@ -2130,19 +2254,24 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
2130
2254
  if (!currentItem) {
2131
2255
  console.warn(`[EditorShell] Cannot load ${currentMode} history: no item loaded`);
2132
2256
  setEditHistory([]);
2257
+ setCanLoadMoreHistory(false);
2133
2258
  return;
2134
2259
  }
2135
2260
  // For page-centric/timeline: fetch all versions (itemId + language only)
2136
2261
  // For current-version: fetch specific version (itemId + language + version)
2137
2262
  const itemFilter = currentMode === "page-centric" || currentMode === "timeline"
2138
- ? { id: currentItem.id, language: currentItem.language, version: 0 } // version 0 signals "all versions" to backend
2263
+ ? {
2264
+ id: currentItem.id,
2265
+ language: currentItem.language,
2266
+ version: 0,
2267
+ } // version 0 signals "all versions" to backend
2139
2268
  : currentItem;
2140
2269
  // Request item-scoped history, optionally filtered by session
2141
- const result = await getEditHistory({
2270
+ const result = await getEditHistoryWithLoading({
2142
2271
  item: itemFilter,
2143
2272
  sessionId: shouldFilterBySession ? sessionId : undefined,
2144
2273
  query: historyQuery,
2145
- });
2274
+ }, loadingMode);
2146
2275
  if (handleErrorResult(result, ui, state)) {
2147
2276
  console.error("[EditorShell] Failed to load item history:", result);
2148
2277
  return;
@@ -2150,6 +2279,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
2150
2279
  if (isStaleHistoryResponse()) {
2151
2280
  return;
2152
2281
  }
2282
+ setCanLoadMoreHistory(false);
2153
2283
  let operations = normalizeEditHistoryPayload(result.data);
2154
2284
  // Client-side version filtering for current-version mode
2155
2285
  if (currentMode === "current-version") {
@@ -2232,10 +2362,26 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
2232
2362
  historySearchQuery,
2233
2363
  item,
2234
2364
  currentItemDescriptor,
2365
+ getEditHistoryWithLoading,
2235
2366
  matchesHistoryScope,
2236
2367
  normalizeEditHistoryPayload,
2237
2368
  sortEditHistoryByDateDesc,
2238
2369
  ]);
2370
+ const loadMoreHistory = useCallback(async () => {
2371
+ if (historyMode !== "global" || isHistoryLoading || isHistoryLoadingMore) {
2372
+ return;
2373
+ }
2374
+ const nextLimit = globalHistoryLimit + getGlobalHistoryLoadMoreIncrement(showOnlyMyChanges);
2375
+ await refreshHistory("global", showOnlyMyChanges, filterByCurrentLanguage, nextLimit, "more");
2376
+ }, [
2377
+ filterByCurrentLanguage,
2378
+ globalHistoryLimit,
2379
+ historyMode,
2380
+ isHistoryLoading,
2381
+ isHistoryLoadingMore,
2382
+ refreshHistory,
2383
+ showOnlyMyChanges,
2384
+ ]);
2239
2385
  // Debounced history refresh to avoid hammering `/parhelia/editHistory` on rapid UI changes.
2240
2386
  const debouncedRefreshHistory = useDebouncedCallback((mode) => {
2241
2387
  void refreshHistory(mode);
@@ -2289,11 +2435,14 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
2289
2435
  // from being displayed while the new data is loading
2290
2436
  if (prevHistoryModeRef.current !== historyMode) {
2291
2437
  setEditHistory([]);
2438
+ setCanLoadMoreHistory(false);
2292
2439
  prevHistoryModeRef.current = historyMode;
2293
2440
  }
2294
2441
  // Cancel any in-flight scheduled refresh before deciding what to do next.
2295
2442
  debouncedRefreshHistoryRef.current.cancel();
2296
2443
  if (historyMode === "global" && sessionId) {
2444
+ setGlobalHistoryLimit(getInitialGlobalHistoryLimit(showOnlyMyChanges));
2445
+ setCanLoadMoreHistory(false);
2297
2446
  // Use refreshHistory for immediate load on first initialization; debounce thereafter.
2298
2447
  if (!historyInitialLoadDoneRef.current) {
2299
2448
  historyInitialLoadDoneRef.current = true;
@@ -2304,6 +2453,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
2304
2453
  }
2305
2454
  }
2306
2455
  else if (historyMode !== "global" && currentItemDescriptor) {
2456
+ setCanLoadMoreHistory(false);
2307
2457
  // Always load immediately for page-centric/current-version/timeline so we don't show
2308
2458
  // an empty history list for the debounce window (e.g. 600ms). The effect only runs on
2309
2459
  // mode or item id/lang/version change, not on every keystroke, so this avoids flaky
@@ -2316,6 +2466,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
2316
2466
  else if (historyMode !== "global" && !currentItemDescriptor) {
2317
2467
  // Clear history if no item loaded in page-centric modes
2318
2468
  setEditHistory([]);
2469
+ setCanLoadMoreHistory(false);
2319
2470
  }
2320
2471
  // Note: refreshHistory and debouncedRefreshHistory are accessed via refs to avoid
2321
2472
  // re-running this effect when contentEditorItem changes (which would happen on every keystroke).
@@ -2947,17 +3098,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
2947
3098
  }
2948
3099
  }, [currentItemDescriptorRef.current, setItemVersions, setItemLanguages]);
2949
3100
  const showErrorToast = useCallback((error) => {
2950
- // Handle case where raw result object is passed instead of error shape
2951
- if (error &&
2952
- typeof error === "object" &&
2953
- ("summary" in error || "details" in error)) {
2954
- const { summary, details } = error;
2955
- toast.error(details || summary || "An error occurred");
2956
- }
2957
- else {
2958
- console.warn("showErrorToast called with unexpected value:", error);
2959
- toast.error("An error occurred");
2960
- }
3101
+ showExpandableErrorToast(error);
2961
3102
  }, []);
2962
3103
  const showInfoToast = useCallback(({ summary, details }) => {
2963
3104
  toast.info(details || summary || "Information");
@@ -3150,9 +3291,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
3150
3291
  requestQuota: requestQuotaAndUpdate,
3151
3292
  sendClientInfo,
3152
3293
  setSocketDiagnostics,
3153
- enableWebSocketTransport: parheliaSettings?.enableWebSocketTransport ?? true,
3154
- enableSseFallback: parheliaSettings?.enableSseFallback ?? true,
3155
- transportSettingsReady: parheliaSettingsLoaded === true,
3294
+ enableWebSocketTransport: effectiveParheliaSettings?.enableWebSocketTransport ?? true,
3295
+ enableSseFallback: effectiveParheliaSettings?.enableSseFallback ?? true,
3296
+ transportSettingsReady: effectiveParheliaSettingsLoaded === true,
3156
3297
  });
3157
3298
  useEffect(() => {
3158
3299
  const hasMySession = activeSessions.some((s) => s.sessionId === sessionId);
@@ -4692,6 +4833,10 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
4692
4833
  setHistorySearchQuery,
4693
4834
  refreshHistory,
4694
4835
  isRefreshing,
4836
+ isHistoryLoading,
4837
+ canLoadMoreHistory,
4838
+ isHistoryLoadingMore,
4839
+ loadMoreHistory,
4695
4840
  activeSessions,
4696
4841
  remoteCarets,
4697
4842
  unlockField: async () => {
@@ -4950,7 +5095,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
4950
5095
  showEditorFormHint,
4951
5096
  dismissEditorFormHint,
4952
5097
  editorFormToggleButtonRef,
4953
- showMinimap,
5098
+ showMinimap: effectiveShowMinimap,
4954
5099
  setShowMinimap: handleSetShowMinimap,
4955
5100
  showHelpTerminal,
4956
5101
  setShowHelpTerminal: handleSetShowHelpTerminal,
@@ -4979,7 +5124,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
4979
5124
  webSocketMessages,
4980
5125
  clearWebSocketMessages: () => setWebSocketMessages([]),
4981
5126
  userInfo: userInfo,
4982
- parheliaSettings,
5127
+ parheliaSettings: effectiveParheliaSettings,
4983
5128
  setUserPreferences,
4984
5129
  currentWizardId,
4985
5130
  setCurrentWizardId,
@@ -5094,9 +5239,13 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
5094
5239
  showResolvedComments,
5095
5240
  reviews,
5096
5241
  userInfo,
5097
- parheliaSettings,
5242
+ effectiveParheliaSettings,
5098
5243
  setUserPreferences,
5099
5244
  isRefreshing,
5245
+ isHistoryLoading,
5246
+ canLoadMoreHistory,
5247
+ isHistoryLoadingMore,
5248
+ loadMoreHistory,
5100
5249
  favorites,
5101
5250
  loadFavorites,
5102
5251
  refreshFavorites,
@@ -5143,7 +5292,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
5143
5292
  showEditorFormHint,
5144
5293
  dismissEditorFormHint,
5145
5294
  editorFormToggleButtonRef,
5146
- showMinimap,
5295
+ effectiveShowMinimap,
5147
5296
  handleSetShowMinimap,
5148
5297
  showHelpTerminal,
5149
5298
  toggleHelpTerminal,