@parhelia/core 0.3.12788 → 0.3.12791
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ui/LanguageSelector.js +4 -1
- package/dist/components/ui/LanguageSelector.js.map +1 -1
- package/dist/components/ui/select.js +12 -1
- package/dist/components/ui/select.js.map +1 -1
- package/dist/editor/FieldHistory.js +2 -2
- package/dist/editor/FieldHistory.js.map +1 -1
- package/dist/editor/ai/InlineAiDialog.js +35 -8
- package/dist/editor/ai/InlineAiDialog.js.map +1 -1
- package/dist/editor/ai/terminal/components/AgentProjectProgress.d.ts +4 -3
- package/dist/editor/ai/terminal/components/AgentProjectProgress.js +34 -50
- package/dist/editor/ai/terminal/components/AgentProjectProgress.js.map +1 -1
- package/dist/editor/ai/terminal/components/AgentTerminalView.d.ts +2 -1
- package/dist/editor/ai/terminal/components/AgentTerminalView.js +2 -2
- package/dist/editor/ai/terminal/components/AgentTerminalView.js.map +1 -1
- package/dist/editor/ai/terminal/useAgentTerminalController.js +1 -0
- package/dist/editor/ai/terminal/useAgentTerminalController.js.map +1 -1
- package/dist/editor/ai/terminal/useAgentTerminalViewProps.d.ts +1 -0
- package/dist/editor/ai/terminal/useAgentTerminalViewProps.js +2 -1
- package/dist/editor/ai/terminal/useAgentTerminalViewProps.js.map +1 -1
- package/dist/editor/bridge/protocol.d.ts +4 -0
- package/dist/editor/bridge/protocol.js +1 -0
- package/dist/editor/bridge/protocol.js.map +1 -1
- package/dist/editor/client/EditorShell.js +174 -65
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +13 -3
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/client/hooks/activeSessionSnapshots.d.ts +2 -0
- package/dist/editor/client/hooks/activeSessionSnapshots.js +43 -0
- package/dist/editor/client/hooks/activeSessionSnapshots.js.map +1 -0
- package/dist/editor/client/hooks/feedbackSocketState.js +3 -0
- package/dist/editor/client/hooks/feedbackSocketState.js.map +1 -1
- package/dist/editor/client/hooks/operationRefreshDeduplication.d.ts +22 -0
- package/dist/editor/client/hooks/operationRefreshDeduplication.js +45 -0
- package/dist/editor/client/hooks/operationRefreshDeduplication.js.map +1 -0
- package/dist/editor/client/hooks/socketMessageRetention.d.ts +7 -0
- package/dist/editor/client/hooks/socketMessageRetention.js +11 -0
- package/dist/editor/client/hooks/socketMessageRetention.js.map +1 -0
- package/dist/editor/client/hooks/useSocketMessageHandler.js +39 -19
- package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
- package/dist/editor/client/hooks/useWorkbox.d.ts +4 -0
- package/dist/editor/client/hooks/useWorkbox.js +132 -2
- package/dist/editor/client/hooks/useWorkbox.js.map +1 -1
- package/dist/editor/client/itemsRepository.d.ts +7 -1
- package/dist/editor/client/itemsRepository.js +41 -18
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/client/operations.d.ts +7 -3
- package/dist/editor/client/operations.js +216 -150
- package/dist/editor/client/operations.js.map +1 -1
- package/dist/editor/client/pageModelBuilder.js +15 -3
- package/dist/editor/client/pageModelBuilder.js.map +1 -1
- package/dist/editor/client/sidebarAvailability.d.ts +4 -0
- package/dist/editor/client/sidebarAvailability.js +15 -0
- package/dist/editor/client/sidebarAvailability.js.map +1 -1
- package/dist/editor/commands/itemCommands.js +1 -1
- package/dist/editor/commands/itemCommands.js.map +1 -1
- package/dist/editor/field-types/RichTextEditorComponent.js +6 -1
- package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
- package/dist/editor/field-types/SingleLineText.js +2 -3
- package/dist/editor/field-types/SingleLineText.js.map +1 -1
- package/dist/editor/field-types/richtext/components/ReactSlate.d.ts +6 -1
- package/dist/editor/field-types/richtext/components/ReactSlate.js +91 -5
- package/dist/editor/field-types/richtext/components/ReactSlate.js.map +1 -1
- package/dist/editor/field-types/singleLineTextValue.d.ts +3 -0
- package/dist/editor/field-types/singleLineTextValue.js +9 -0
- package/dist/editor/field-types/singleLineTextValue.js.map +1 -0
- package/dist/editor/menubar/ItemLanguageVersion.js +1 -1
- package/dist/editor/menubar/ItemLanguageVersion.js.map +1 -1
- package/dist/editor/menubar/VersionSelector.js +18 -6
- package/dist/editor/menubar/VersionSelector.js.map +1 -1
- package/dist/editor/menubar/WorkflowButton.js +8 -3
- package/dist/editor/menubar/WorkflowButton.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/EditControls.js +1 -1
- package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
- package/dist/editor/notifications/NotificationCenter.js +26 -7
- package/dist/editor/notifications/NotificationCenter.js.map +1 -1
- package/dist/editor/notifications/useNotifications.d.ts +3 -1
- package/dist/editor/notifications/useNotifications.js +27 -9
- package/dist/editor/notifications/useNotifications.js.map +1 -1
- package/dist/editor/page-editor-chrome/BridgeInlineFormatOverlay.js +18 -0
- package/dist/editor/page-editor-chrome/BridgeInlineFormatOverlay.js.map +1 -1
- package/dist/editor/page-editor-chrome/InlineFieldHistoryButton.js +30 -8
- package/dist/editor/page-editor-chrome/InlineFieldHistoryButton.js.map +1 -1
- package/dist/editor/page-editor-chrome/VersionDiffHighlightings.js +30 -8
- package/dist/editor/page-editor-chrome/VersionDiffHighlightings.js.map +1 -1
- package/dist/editor/page-editor-chrome/useBridgeInlineEditing.d.ts +5 -3
- package/dist/editor/page-editor-chrome/useBridgeInlineEditing.js +111 -19
- package/dist/editor/page-editor-chrome/useBridgeInlineEditing.js.map +1 -1
- package/dist/editor/page-viewer/EditorForm.d.ts +2 -1
- package/dist/editor/page-viewer/EditorForm.js +41 -12
- package/dist/editor/page-viewer/EditorForm.js.map +1 -1
- package/dist/editor/page-viewer/PageViewer.d.ts +2 -0
- package/dist/editor/page-viewer/PageViewer.js +11 -2
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +237 -42
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/page-viewer/bridgeInlineAiSelection.d.ts +17 -0
- package/dist/editor/page-viewer/bridgeInlineAiSelection.js +80 -0
- package/dist/editor/page-viewer/bridgeInlineAiSelection.js.map +1 -0
- package/dist/editor/page-viewer/bridgeInlineEditCommit.d.ts +18 -0
- package/dist/editor/page-viewer/bridgeInlineEditCommit.js +24 -0
- package/dist/editor/page-viewer/bridgeInlineEditCommit.js.map +1 -0
- package/dist/editor/page-viewer/bridgeInlineEditSession.d.ts +3 -0
- package/dist/editor/page-viewer/bridgeInlineEditSession.js +30 -0
- package/dist/editor/page-viewer/bridgeInlineEditSession.js.map +1 -1
- package/dist/editor/page-viewer/bridgeItemChangeTarget.d.ts +9 -0
- package/dist/editor/page-viewer/bridgeItemChangeTarget.js +38 -0
- package/dist/editor/page-viewer/bridgeItemChangeTarget.js.map +1 -0
- package/dist/editor/page-viewer/bridgePointerDragRelease.d.ts +31 -0
- package/dist/editor/page-viewer/bridgePointerDragRelease.js +32 -0
- package/dist/editor/page-viewer/bridgePointerDragRelease.js.map +1 -0
- package/dist/editor/page-viewer/renderUrl.d.ts +8 -0
- package/dist/editor/page-viewer/renderUrl.js +10 -0
- package/dist/editor/page-viewer/renderUrl.js.map +1 -1
- package/dist/editor/personalization/PersonalizationPanel.js +52 -25
- package/dist/editor/personalization/PersonalizationPanel.js.map +1 -1
- package/dist/editor/reviews/Comment.js +3 -1
- package/dist/editor/reviews/Comment.js.map +1 -1
- package/dist/editor/reviews/CommentDisplayPopover.js +2 -0
- package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -1
- package/dist/editor/reviews/Comments.js +16 -3
- package/dist/editor/reviews/Comments.js.map +1 -1
- package/dist/editor/reviews/SuggestedEdit.js +25 -2
- package/dist/editor/reviews/SuggestedEdit.js.map +1 -1
- package/dist/editor/reviews/SuggestionCommentThread.js +2 -0
- package/dist/editor/reviews/SuggestionCommentThread.js.map +1 -1
- package/dist/editor/reviews/applySuggestedEditActions.js +105 -25
- package/dist/editor/reviews/applySuggestedEditActions.js.map +1 -1
- package/dist/editor/reviews/commentState.d.ts +4 -0
- package/dist/editor/reviews/commentState.js +15 -0
- package/dist/editor/reviews/commentState.js.map +1 -0
- package/dist/editor/reviews/feedbackOrdering.d.ts +1 -0
- package/dist/editor/reviews/feedbackOrdering.js +6 -0
- package/dist/editor/reviews/feedbackOrdering.js.map +1 -1
- package/dist/editor/reviews/suggestionContentChange.d.ts +7 -0
- package/dist/editor/reviews/suggestionContentChange.js +10 -0
- package/dist/editor/reviews/suggestionContentChange.js.map +1 -1
- package/dist/editor/reviews/suggestionPatch.js +6 -0
- package/dist/editor/reviews/suggestionPatch.js.map +1 -1
- package/dist/editor/services/personalizationService.js +30 -6
- package/dist/editor/services/personalizationService.js.map +1 -1
- package/dist/editor/services/suggestedEditsService.d.ts +1 -0
- package/dist/editor/services/suggestedEditsService.js +3 -0
- package/dist/editor/services/suggestedEditsService.js.map +1 -1
- package/dist/editor/settings/SettingsView.js +15 -7
- package/dist/editor/settings/SettingsView.js.map +1 -1
- package/dist/editor/settings/panels/MemoriesPanel.js +3 -2
- package/dist/editor/settings/panels/MemoriesPanel.js.map +1 -1
- package/dist/editor/settings/panels/ProjectTemplatesPanel.d.ts +3 -0
- package/dist/editor/settings/panels/ProjectTemplatesPanel.js +84 -15
- package/dist/editor/settings/panels/ProjectTemplatesPanel.js.map +1 -1
- package/dist/editor/settings/panels/projectTemplateDuplication.d.ts +5 -0
- package/dist/editor/settings/panels/projectTemplateDuplication.js +52 -0
- package/dist/editor/settings/panels/projectTemplateDuplication.js.map +1 -0
- package/dist/editor/ui/PerfectTree.js +2 -2
- package/dist/editor/ui/PerfectTree.js.map +1 -1
- package/dist/editor/ui/PublishRestrictionsDialog.js +9 -1
- package/dist/editor/ui/PublishRestrictionsDialog.js.map +1 -1
- package/dist/editor/utils.js +5 -2
- package/dist/editor/utils.js.map +1 -1
- package/dist/editor/views/EditorSlot.js +5 -1
- package/dist/editor/views/EditorSlot.js.map +1 -1
- package/dist/editor/views/SingleEditView.d.ts +4 -1
- package/dist/editor/views/SingleEditView.js +2 -2
- package/dist/editor/views/SingleEditView.js.map +1 -1
- package/dist/licensing/LicenseContext.js +7 -0
- package/dist/licensing/LicenseContext.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/task-board/TaskBoardWorkspace.js +5 -1
- package/dist/task-board/TaskBoardWorkspace.js.map +1 -1
- package/dist/task-board/taskExecutionRecords.js +7 -0
- package/dist/task-board/taskExecutionRecords.js.map +1 -1
- package/dist/task-board/views/DependencyGraphView.d.ts +1 -0
- package/dist/task-board/views/DependencyGraphView.js +15 -12
- package/dist/task-board/views/DependencyGraphView.js.map +1 -1
- package/package.json +1 -1
|
@@ -13,7 +13,7 @@ import { showExpandableErrorToast } from "./errorToast";
|
|
|
13
13
|
import { isRedoStackEntry, reconcileNewHistoryOperation, } from "./editHistoryReconciliation";
|
|
14
14
|
import { isManualShowMeFocusTransfer, isManualShowMeTarget, } from "./manualShowMeFocus";
|
|
15
15
|
import { isAgentTerminalFocusTransfer, nextAgentTerminalInteractionAt, } from "./agentTerminalFocus";
|
|
16
|
-
import { getAvailableSidebarsForWorkspace } from "./sidebarAvailability";
|
|
16
|
+
import { getAvailableSidebarsForWorkspace, parseSidebarUrlValue, sidebarUrlValue, } from "./sidebarAvailability";
|
|
17
17
|
import { executeFieldAction as executeFieldServerAction, connectSocket, getEditHistory, getRunningOperations, reconnectSession, releaseFieldLocks, validateItems, } from "../services/editService";
|
|
18
18
|
import { emitAgentDocumentsChanged } from "../services/agentService";
|
|
19
19
|
import { onSocketReconnect as onAgentSocketReconnect } from "../services/agentSubscriptionRegistry";
|
|
@@ -37,6 +37,7 @@ import { EditorFormHintPopover } from "../page-viewer/EditorFormHintPopover";
|
|
|
37
37
|
import { FieldEditorPopup } from "../FieldEditorPopup";
|
|
38
38
|
import { ConcurrentUserLimitDialog } from "../ConcurrentUserLimitDialog";
|
|
39
39
|
import { SessionExpiredOverlay } from "./SessionExpiredOverlay";
|
|
40
|
+
import { filterRemovedComments } from "../reviews/commentState";
|
|
40
41
|
import { post } from "../services/serviceHelper";
|
|
41
42
|
import { CONTENT_TREE_SIDEBAR_ID } from "../sidebar/contentTreeConstants";
|
|
42
43
|
import { PageViewerFrame } from "../page-viewer/PageViewerFrame";
|
|
@@ -89,14 +90,9 @@ function getGlobalHistoryLoadMoreIncrement(filterBySession) {
|
|
|
89
90
|
? GLOBAL_SESSION_HISTORY_LOAD_MORE_INCREMENT
|
|
90
91
|
: GLOBAL_HISTORY_LOAD_MORE_INCREMENT;
|
|
91
92
|
}
|
|
92
|
-
// Sentinel written to the `sidebar` URL param when the user has explicitly closed
|
|
93
|
-
// every sidebar. Distinguishes "no preference yet" (param absent) from "user wants
|
|
94
|
-
// nothing open" (param present with this value) so reload can honor the user's intent.
|
|
95
|
-
const SIDEBAR_NONE_SENTINEL = "none";
|
|
96
93
|
const PARHELIA_SETTINGS_ITEM_ID = "a2f3c168bb2841ff9673ee7f3cc624af";
|
|
97
94
|
const PARHELIA_SETTINGS_ITEM_PATH = "/sitecore/system/parhelia/settings";
|
|
98
95
|
const PARHELIA_SHOW_MINIMAP_FIELD_ID = "e75976d2179e4578b4140147b7525fa5";
|
|
99
|
-
const sidebarUrlValue = (ids) => ids.length ? ids.join(",") : SIDEBAR_NONE_SENTINEL;
|
|
100
96
|
// moved to hooks/useQuota
|
|
101
97
|
function isParheliaSettingsSnapshotChange(changes) {
|
|
102
98
|
return changes.some((change) => {
|
|
@@ -373,17 +369,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
373
369
|
// Sidebar state
|
|
374
370
|
const [openSidebars, setOpenSidebars] = useState(() => {
|
|
375
371
|
const sidebarParam = searchParams.get("sidebar");
|
|
376
|
-
let sidebars =
|
|
377
|
-
|
|
378
|
-
if (sidebarParam !== null) {
|
|
379
|
-
sidebars =
|
|
380
|
-
sidebarParam === SIDEBAR_NONE_SENTINEL
|
|
381
|
-
? []
|
|
382
|
-
: sidebarParam.split(",").filter(Boolean);
|
|
383
|
-
}
|
|
384
|
-
else {
|
|
385
|
-
sidebars = getInitialOpenSidebars(configuration, initialWorkspaceId);
|
|
386
|
-
}
|
|
372
|
+
let sidebars = parseSidebarUrlValue(sidebarParam) ??
|
|
373
|
+
getInitialOpenSidebars(configuration, initialWorkspaceId);
|
|
387
374
|
// If workspace=reviews was requested, ensure reviews sidebar is open
|
|
388
375
|
if (isReviewsSidebarRequest && !sidebars.includes("reviews")) {
|
|
389
376
|
sidebars.push("reviews");
|
|
@@ -643,6 +630,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
643
630
|
const [itemLanguages, setItemLanguages] = useState([]);
|
|
644
631
|
const [itemVersions, setItemVersions] = useState([]);
|
|
645
632
|
const [isLoadingVersions, setIsLoadingVersions] = useState(false);
|
|
633
|
+
const versionLoadRequestRef = useRef(0);
|
|
634
|
+
const visibleVersionLoadRef = useRef(0);
|
|
635
|
+
const versionLoadsInFlightRef = useRef(new Map());
|
|
646
636
|
const [modifiedFields, setModifiedFields] = useState([]);
|
|
647
637
|
useEffect(() => {
|
|
648
638
|
const stores = getFieldModificationStores();
|
|
@@ -747,8 +737,16 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
747
737
|
const [mode, setModeState] = useState("edit");
|
|
748
738
|
const modeRef = useRef("edit");
|
|
749
739
|
modeRef.current = mode;
|
|
740
|
+
const modeTransitionChainRef = useRef(Promise.resolve());
|
|
750
741
|
const modeChangeParticipantsRef = useRef(new Set());
|
|
751
|
-
const loadSuggestedEditsRef = useRef(async () =>
|
|
742
|
+
const loadSuggestedEditsRef = useRef(async () => undefined);
|
|
743
|
+
const suggestedEditsLoadRequestRef = useRef(0);
|
|
744
|
+
const suggestedEditsAppliedRequestRef = useRef(0);
|
|
745
|
+
const suggestedEditsItemKey = item
|
|
746
|
+
? `${item.descriptor.id}:${item.descriptor.language}:${item.descriptor.version}`
|
|
747
|
+
: undefined;
|
|
748
|
+
const suggestedEditsItemKeyRef = useRef(suggestedEditsItemKey);
|
|
749
|
+
suggestedEditsItemKeyRef.current = suggestedEditsItemKey;
|
|
752
750
|
const [modeTransitionRevision, setModeTransitionRevision] = useState(0);
|
|
753
751
|
const [previewDate, setPreviewDate] = useState();
|
|
754
752
|
const [layoutMode, setLayoutMode] = useState("final");
|
|
@@ -763,12 +761,23 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
763
761
|
if (nextMode === previousMode)
|
|
764
762
|
return;
|
|
765
763
|
const participants = Array.from(modeChangeParticipantsRef.current);
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
764
|
+
// Start event-time capture while the current mode and its live iframe
|
|
765
|
+
// editing session still exist. These promises are awaited in the
|
|
766
|
+
// serialized transition below, but invoking them here prevents the mode
|
|
767
|
+
// render from making an uncommitted DOM value unreachable.
|
|
768
|
+
const captureBarriers = participants.map((participant) => {
|
|
769
|
+
try {
|
|
770
|
+
return Promise.resolve(participant.captureBeforeModeChange?.(nextMode, previousMode, options));
|
|
771
|
+
}
|
|
772
|
+
catch (error) {
|
|
773
|
+
return Promise.reject(error);
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
// Reflect the user's requested mode immediately. Persistence barriers
|
|
777
|
+
// still drain in order below, but keeping the old mode visible until a
|
|
778
|
+
// slow host/save round-trip finishes invites duplicate toggle actions
|
|
779
|
+
// and makes the editor appear unresponsive. Inline edit sessions retain
|
|
780
|
+
// modeAtStart, so the in-flight value keeps its original routing.
|
|
772
781
|
flushSync(() => {
|
|
773
782
|
modeRef.current = nextMode;
|
|
774
783
|
setModeState(nextMode);
|
|
@@ -782,9 +791,22 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
782
791
|
setShowComments(true);
|
|
783
792
|
}
|
|
784
793
|
});
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
794
|
+
const transition = modeTransitionChainRef.current
|
|
795
|
+
.catch(() => undefined)
|
|
796
|
+
.then(async () => {
|
|
797
|
+
await Promise.all(captureBarriers);
|
|
798
|
+
for (const participant of participants) {
|
|
799
|
+
await participant.beforeModeChange?.(nextMode, previousMode, options);
|
|
800
|
+
}
|
|
801
|
+
for (const participant of participants) {
|
|
802
|
+
participant.clearInlineDedupe?.(nextMode, previousMode, options);
|
|
803
|
+
}
|
|
804
|
+
if (nextMode === "suggestions" && modeRef.current === nextMode) {
|
|
805
|
+
await loadSuggestedEditsRef.current();
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
modeTransitionChainRef.current = transition;
|
|
809
|
+
await transition;
|
|
788
810
|
}, []);
|
|
789
811
|
const setMode = useCallback((nextMode) => {
|
|
790
812
|
const resolvedMode = typeof nextMode === "function" ? nextMode(modeRef.current) : nextMode;
|
|
@@ -1714,11 +1736,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1714
1736
|
}
|
|
1715
1737
|
// Handle sidebar changes — clear when absent so state matches the URL
|
|
1716
1738
|
const sidebarParam = urlParams.get("sidebar");
|
|
1717
|
-
const newSidebars = sidebarParam
|
|
1718
|
-
|
|
1719
|
-
: sidebarParam
|
|
1720
|
-
? sidebarParam.split(",").filter(Boolean)
|
|
1721
|
-
: [];
|
|
1739
|
+
const newSidebars = parseSidebarUrlValue(sidebarParam) ?? [];
|
|
1740
|
+
openSidebarsRef.current = newSidebars;
|
|
1722
1741
|
setOpenSidebars(newSidebars);
|
|
1723
1742
|
// Handle wizard ID changes
|
|
1724
1743
|
const wizardId = urlParams.get("wizardid");
|
|
@@ -2010,7 +2029,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2010
2029
|
if (handleErrorResult(result, ui, state))
|
|
2011
2030
|
return;
|
|
2012
2031
|
setComments((x) => {
|
|
2013
|
-
const loadedComments = result.data;
|
|
2032
|
+
const loadedComments = filterRemovedComments(result.data);
|
|
2014
2033
|
const newComments = x.filter((c) => c.isNew &&
|
|
2015
2034
|
c.mainItemId === currentItemDescriptor.id &&
|
|
2016
2035
|
c.language === currentItemDescriptor.language &&
|
|
@@ -2024,20 +2043,34 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2024
2043
|
// Assuming currentItemDescriptor, ui, state, handleErrorResult, and setSuggestedEdits
|
|
2025
2044
|
// are available in your component context.
|
|
2026
2045
|
const loadSuggestedEdits = useCallback(async () => {
|
|
2046
|
+
const requestId = ++suggestedEditsLoadRequestRef.current;
|
|
2027
2047
|
if (!item)
|
|
2028
|
-
return;
|
|
2048
|
+
return undefined;
|
|
2049
|
+
const requestedItemKey = `${item.descriptor.id}:${item.descriptor.language}:${item.descriptor.version}`;
|
|
2029
2050
|
const reviewId = searchParams.get("reviewId");
|
|
2030
2051
|
const result = await getSuggestedEdits(item.descriptor.id, item.descriptor.language, item.descriptor.version, reviewId ?? undefined);
|
|
2052
|
+
// Item initialization, navigation, and realtime feedback notifications can
|
|
2053
|
+
// overlap. Accept the first response that can populate the current item,
|
|
2054
|
+
// then prevent older snapshots from replacing a newer one. This preserves
|
|
2055
|
+
// useful data while a second post-subscription refresh is still in flight.
|
|
2056
|
+
if (requestedItemKey !== suggestedEditsItemKeyRef.current)
|
|
2057
|
+
return undefined;
|
|
2058
|
+
if (requestId < suggestedEditsAppliedRequestRef.current) {
|
|
2059
|
+
return suggestedEditsRef.current;
|
|
2060
|
+
}
|
|
2031
2061
|
if (handleErrorResult(result, ui, state))
|
|
2032
|
-
return;
|
|
2062
|
+
return undefined;
|
|
2063
|
+
suggestedEditsAppliedRequestRef.current = requestId;
|
|
2033
2064
|
// A refetch can race a locally reverted suggestion's server delete; don't
|
|
2034
2065
|
// resurrect suggestions the user already removed.
|
|
2035
2066
|
const edits = (result.data || []).filter((edit) => !isSuggestedEditRemoved(edit.id));
|
|
2036
2067
|
setSuggestedEdits(edits);
|
|
2068
|
+
return edits;
|
|
2037
2069
|
}, [item, searchParams]);
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2070
|
+
// URL-driven mode initialization runs in an earlier effect. Update the ref
|
|
2071
|
+
// during render so entering suggestions on the first committed frame cannot
|
|
2072
|
+
// observe the initial no-op loader.
|
|
2073
|
+
loadSuggestedEditsRef.current = loadSuggestedEdits;
|
|
2041
2074
|
const loadFavorites = useCallback(async () => {
|
|
2042
2075
|
try {
|
|
2043
2076
|
const result = await getAllFavorites();
|
|
@@ -2551,15 +2584,22 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2551
2584
|
return;
|
|
2552
2585
|
// The previous item's comments don't speak for the new one.
|
|
2553
2586
|
setCommentsLoaded(false);
|
|
2554
|
-
//
|
|
2555
|
-
//
|
|
2556
|
-
//
|
|
2557
|
-
//
|
|
2558
|
-
|
|
2559
|
-
return;
|
|
2587
|
+
// Load an immediate snapshot so an existing comment or suggestion does not
|
|
2588
|
+
// depend on websocket registration latency. The acknowledgement dependency
|
|
2589
|
+
// deliberately runs this effect again after the page topic is subscribed;
|
|
2590
|
+
// that second snapshot closes the gap between the first response and the
|
|
2591
|
+
// realtime event stream.
|
|
2560
2592
|
loadComments();
|
|
2561
2593
|
loadSuggestedEdits();
|
|
2562
|
-
}, [
|
|
2594
|
+
}, [
|
|
2595
|
+
currentItemDescriptor?.id,
|
|
2596
|
+
currentItemDescriptor?.language,
|
|
2597
|
+
currentItemDescriptor?.version,
|
|
2598
|
+
isClientInfoAcknowledged,
|
|
2599
|
+
item?.descriptor.id,
|
|
2600
|
+
item?.descriptor.language,
|
|
2601
|
+
item?.descriptor.version,
|
|
2602
|
+
]);
|
|
2563
2603
|
// Track the previous history mode to detect mode changes
|
|
2564
2604
|
const prevHistoryModeRef = useRef(historyMode);
|
|
2565
2605
|
const historyInitialLoadDoneRef = useRef(false);
|
|
@@ -3237,26 +3277,69 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3237
3277
|
useEffect(() => {
|
|
3238
3278
|
loadItemVersions();
|
|
3239
3279
|
}, [currentItemDescriptor]);
|
|
3240
|
-
const loadItemVersions = useCallback(async () => {
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3280
|
+
const loadItemVersions = useCallback(async (options) => {
|
|
3281
|
+
const descriptor = options?.descriptor
|
|
3282
|
+
? getItemDescriptor(options.descriptor)
|
|
3283
|
+
: currentItemDescriptorRef.current
|
|
3284
|
+
? getItemDescriptor(currentItemDescriptorRef.current)
|
|
3285
|
+
: undefined;
|
|
3286
|
+
if (!descriptor) {
|
|
3287
|
+
if (options?.clearOnMissing !== false) {
|
|
3288
|
+
setItemVersions([]);
|
|
3289
|
+
setItemLanguages([]);
|
|
3290
|
+
}
|
|
3244
3291
|
setIsLoadingVersions(false);
|
|
3245
3292
|
return;
|
|
3246
3293
|
}
|
|
3294
|
+
const descriptorKey = [
|
|
3295
|
+
descriptor.database || "master",
|
|
3296
|
+
descriptor.id,
|
|
3297
|
+
descriptor.language,
|
|
3298
|
+
descriptor.version,
|
|
3299
|
+
]
|
|
3300
|
+
.join(":")
|
|
3301
|
+
.toLowerCase();
|
|
3302
|
+
const requestId = ++versionLoadRequestRef.current;
|
|
3303
|
+
visibleVersionLoadRef.current = requestId;
|
|
3247
3304
|
setIsLoadingVersions(true);
|
|
3248
3305
|
try {
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
setItemVersions([]);
|
|
3255
|
-
setItemLanguages([]);
|
|
3256
|
-
showErrorToast({
|
|
3257
|
-
summary: "Error",
|
|
3258
|
-
details: "Failed to load item versions",
|
|
3306
|
+
let request = versionLoadsInFlightRef.current.get(descriptorKey);
|
|
3307
|
+
if (!request) {
|
|
3308
|
+
request = getLanguagesAndVersions(descriptor, {
|
|
3309
|
+
includePublishingRestrictions: true,
|
|
3310
|
+
includePublishingTargets: true,
|
|
3259
3311
|
});
|
|
3312
|
+
versionLoadsInFlightRef.current.set(descriptorKey, request);
|
|
3313
|
+
const releaseRequest = () => {
|
|
3314
|
+
if (versionLoadsInFlightRef.current.get(descriptorKey) === request) {
|
|
3315
|
+
versionLoadsInFlightRef.current.delete(descriptorKey);
|
|
3316
|
+
}
|
|
3317
|
+
};
|
|
3318
|
+
void request.then(releaseRequest, releaseRequest);
|
|
3319
|
+
}
|
|
3320
|
+
const result = await request;
|
|
3321
|
+
const currentDescriptor = currentItemDescriptorRef.current;
|
|
3322
|
+
const currentDescriptorKey = currentDescriptor
|
|
3323
|
+
? [
|
|
3324
|
+
currentDescriptor.database || "master",
|
|
3325
|
+
currentDescriptor.id,
|
|
3326
|
+
currentDescriptor.language,
|
|
3327
|
+
currentDescriptor.version,
|
|
3328
|
+
]
|
|
3329
|
+
.join(":")
|
|
3330
|
+
.toLowerCase()
|
|
3331
|
+
: undefined;
|
|
3332
|
+
if (currentDescriptorKey !== descriptorKey)
|
|
3333
|
+
return;
|
|
3334
|
+
if (!result?.data) {
|
|
3335
|
+
if (!options?.preserveOnError) {
|
|
3336
|
+
setItemVersions([]);
|
|
3337
|
+
setItemLanguages([]);
|
|
3338
|
+
showErrorToast({
|
|
3339
|
+
summary: "Error",
|
|
3340
|
+
details: "Failed to load item versions",
|
|
3341
|
+
});
|
|
3342
|
+
}
|
|
3260
3343
|
return;
|
|
3261
3344
|
}
|
|
3262
3345
|
const v = [...result.data.versions].reverse() || [];
|
|
@@ -3264,9 +3347,14 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3264
3347
|
setItemLanguages(result.data.languages);
|
|
3265
3348
|
}
|
|
3266
3349
|
finally {
|
|
3267
|
-
|
|
3350
|
+
// A superseded request must not hide the spinner for the descriptor
|
|
3351
|
+
// that is currently loading. Duplicate calls for the same descriptor
|
|
3352
|
+
// share one network request and settle together.
|
|
3353
|
+
if (visibleVersionLoadRef.current === requestId) {
|
|
3354
|
+
setIsLoadingVersions(false);
|
|
3355
|
+
}
|
|
3268
3356
|
}
|
|
3269
|
-
}, [
|
|
3357
|
+
}, [setItemVersions, setItemLanguages]);
|
|
3270
3358
|
const showErrorToast = useCallback((error) => {
|
|
3271
3359
|
showExpandableErrorToast(error);
|
|
3272
3360
|
}, []);
|
|
@@ -3912,6 +4000,23 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3912
4000
|
// workspace defaults are applied by the openSidebars state initializer, while
|
|
3913
4001
|
// explicit sidebar URL state such as sidebar=none is still respected.
|
|
3914
4002
|
const prevWorkspaceIdForDefaultsRef = useRef(null);
|
|
4003
|
+
// Next can retain this shell while hydrating or navigating within the same
|
|
4004
|
+
// route. Keep explicit sidebar URL state authoritative after the initializer
|
|
4005
|
+
// has run; the availability effect below still removes inaccessible panels.
|
|
4006
|
+
const sidebarParam = searchParams.get("sidebar");
|
|
4007
|
+
useEffect(() => {
|
|
4008
|
+
const requested = parseSidebarUrlValue(sidebarParam);
|
|
4009
|
+
if (requested === undefined)
|
|
4010
|
+
return;
|
|
4011
|
+
const current = openSidebarsRef.current;
|
|
4012
|
+
const unchanged = requested.length === current.length &&
|
|
4013
|
+
requested.every((id, index) => id === current[index]);
|
|
4014
|
+
if (unchanged)
|
|
4015
|
+
return;
|
|
4016
|
+
openSidebarsRef.current = requested;
|
|
4017
|
+
setOpenSidebars(requested);
|
|
4018
|
+
setSidebarStacks((prev) => normalizeSidebarStacks(requested, prev));
|
|
4019
|
+
}, [normalizeSidebarStacks, sidebarParam]);
|
|
3915
4020
|
useEffect(() => {
|
|
3916
4021
|
if (!currentWorkspace.supportsSidebars) {
|
|
3917
4022
|
return;
|
|
@@ -3942,6 +4047,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3942
4047
|
currentWorkspace,
|
|
3943
4048
|
getSidebarsForWorkspace,
|
|
3944
4049
|
normalizeSidebarStacks,
|
|
4050
|
+
sidebarParam,
|
|
3945
4051
|
updateUrl,
|
|
3946
4052
|
workspaceId,
|
|
3947
4053
|
]);
|
|
@@ -4819,8 +4925,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
4819
4925
|
// the drop target's parent here made the single-selection frame
|
|
4820
4926
|
// FLIP-animate component -> parent -> component around every drop.
|
|
4821
4927
|
let op;
|
|
4822
|
-
|
|
4823
|
-
|
|
4928
|
+
// A placeholder is rendered from this page model, so its insertion
|
|
4929
|
+
// must target the same item. The shell-level descriptor can briefly
|
|
4930
|
+
// lag while slots or a newly created page settle; using it here could
|
|
4931
|
+
// either discard a valid click or write the component to another page.
|
|
4932
|
+
const dropTargetDescriptor = page.item.descriptor;
|
|
4824
4933
|
let insertionSequence;
|
|
4825
4934
|
if ((spotPosition || spotPositionElement) && spotPositionAnchor) {
|
|
4826
4935
|
insertionSequence = ++insertingSequenceRef.current;
|
|
@@ -4838,10 +4947,10 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
4838
4947
|
// can exit cleanly and the editor header updates from "Add Component"
|
|
4839
4948
|
// to the actual component name after the operation completes.
|
|
4840
4949
|
setSelectedForInsertion("");
|
|
4841
|
-
operationPromise = operations.addComponent(insertOption.typeId, placeholderKey, index,
|
|
4950
|
+
operationPromise = operations.addComponent(insertOption.typeId, placeholderKey, index, dropTargetDescriptor, { showIndicator: false });
|
|
4842
4951
|
}
|
|
4843
4952
|
else if (droppedDragObject?.type == "template") {
|
|
4844
|
-
operationPromise = operations.addComponent(droppedDragObject.typeId, placeholderKey, index,
|
|
4953
|
+
operationPromise = operations.addComponent(droppedDragObject.typeId, placeholderKey, index, dropTargetDescriptor, { showIndicator: false });
|
|
4845
4954
|
}
|
|
4846
4955
|
else if (droppedDragObject?.type == "component" ||
|
|
4847
4956
|
(droppedDragObject?.type == "items" && droppedDragObject.items)) {
|