@alpaca-editor/core 1.0.4085 → 1.0.4088
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/card.d.ts +1 -1
- package/dist/components/ui/paste-button.d.ts +14 -0
- package/dist/components/ui/paste-button.js +114 -0
- package/dist/components/ui/paste-button.js.map +1 -0
- package/dist/config/config.js +60 -3
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +25 -0
- package/dist/editor/ContentTree.js +43 -21
- package/dist/editor/ContentTree.js.map +1 -1
- package/dist/editor/FieldListField.js +62 -2
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.d.ts +3 -1
- package/dist/editor/ai/AgentTerminal.js +96 -74
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/Agents.js +46 -2
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.js +171 -75
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/AiTerminal.js +27 -14
- package/dist/editor/ai/AiTerminal.js.map +1 -1
- package/dist/editor/client/EditorShell.js +110 -17
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +4 -0
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/client/hooks/useSocketMessageHandler.d.ts +1 -0
- package/dist/editor/client/hooks/useSocketMessageHandler.js +54 -20
- package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
- package/dist/editor/client/hooks/useWorkbox.d.ts +1 -1
- package/dist/editor/client/hooks/useWorkbox.js +4 -4
- package/dist/editor/client/hooks/useWorkbox.js.map +1 -1
- package/dist/editor/client/itemsRepository.d.ts +13 -1
- package/dist/editor/client/itemsRepository.js +34 -21
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/client/pageModelBuilder.js +1 -1
- package/dist/editor/client/pageModelBuilder.js.map +1 -1
- package/dist/editor/control-center/Setup.d.ts +1 -0
- package/dist/editor/control-center/Setup.js +18 -0
- package/dist/editor/control-center/Setup.js.map +1 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep.d.ts +2 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep.js +287 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep.js.map +1 -0
- package/dist/editor/control-center/setup-steps/DbSetupStep.d.ts +2 -0
- package/dist/editor/control-center/setup-steps/DbSetupStep.js +46 -0
- package/dist/editor/control-center/setup-steps/DbSetupStep.js.map +1 -0
- package/dist/editor/control-center/setup-steps/IndexSetupStep.d.ts +2 -0
- package/dist/editor/control-center/setup-steps/IndexSetupStep.js +34 -0
- package/dist/editor/control-center/setup-steps/IndexSetupStep.js.map +1 -0
- package/dist/editor/control-center/setup-steps/SettingsSetupStep.d.ts +2 -0
- package/dist/editor/control-center/setup-steps/SettingsSetupStep.js +104 -0
- package/dist/editor/control-center/setup-steps/SettingsSetupStep.js.map +1 -0
- package/dist/editor/field-types/InternalLinkFieldEditor.js +3 -1
- package/dist/editor/field-types/InternalLinkFieldEditor.js.map +1 -1
- package/dist/editor/field-types/RichTextEditorComponent.js +1 -1
- package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
- package/dist/editor/field-types/richtext/components/ReactSlate.js +2 -2
- package/dist/editor/field-types/richtext/components/ReactSlate.js.map +1 -1
- package/dist/editor/field-types/richtext/utils/profileServiceCache.d.ts +1 -1
- package/dist/editor/field-types/richtext/utils/profileServiceCache.js +16 -14
- package/dist/editor/field-types/richtext/utils/profileServiceCache.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/CompareControls.js +1 -1
- package/dist/editor/menubar/toolbar-sections/CompareControls.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/menubar/toolbar-sections/ViewportControls.js +1 -1
- package/dist/editor/menubar/toolbar-sections/ViewportControls.js.map +1 -1
- package/dist/editor/page-editor-chrome/InlineEditor.js +25 -6
- package/dist/editor/page-editor-chrome/InlineEditor.js.map +1 -1
- package/dist/editor/page-viewer/EditorForm.js +9 -2
- package/dist/editor/page-viewer/EditorForm.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +5 -0
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/pageModel.d.ts +1 -0
- package/dist/editor/reviews/Comment.js +1 -1
- package/dist/editor/reviews/Comment.js.map +1 -1
- package/dist/editor/reviews/CommentDisplayPopover.js +3 -24
- package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -1
- package/dist/editor/reviews/CommentPopover.js +3 -23
- package/dist/editor/reviews/CommentPopover.js.map +1 -1
- package/dist/editor/reviews/CommentView.js +2 -1
- package/dist/editor/reviews/CommentView.js.map +1 -1
- package/dist/editor/reviews/Comments.js +88 -37
- package/dist/editor/reviews/Comments.js.map +1 -1
- package/dist/editor/reviews/commentAi.js +3 -0
- package/dist/editor/reviews/commentAi.js.map +1 -1
- package/dist/editor/sidebar/Debug.js +1 -5
- package/dist/editor/sidebar/Debug.js.map +1 -1
- package/dist/editor/sidebar/ViewSelector.js +72 -6
- package/dist/editor/sidebar/ViewSelector.js.map +1 -1
- package/dist/editor/ui/Icons.d.ts +5 -0
- package/dist/editor/ui/Icons.js +14 -0
- package/dist/editor/ui/Icons.js.map +1 -1
- package/dist/editor/utils.d.ts +5 -0
- package/dist/editor/utils.js +29 -0
- package/dist/editor/utils.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/splash-screen/SplashScreen.js +2 -2
- package/dist/splash-screen/SplashScreen.js.map +1 -1
- package/dist/styles.css +3 -0
- package/dist/types.d.ts +2 -0
- package/package.json +1 -1
- package/src/components/ui/card.tsx +1 -1
- package/src/components/ui/paste-button.tsx +163 -0
- package/src/config/config.tsx +68 -2
- package/src/config/types.ts +26 -0
- package/src/editor/ContentTree.tsx +48 -23
- package/src/editor/FieldListField.tsx +75 -2
- package/src/editor/ai/AgentTerminal.tsx +118 -71
- package/src/editor/ai/Agents.tsx +52 -1
- package/src/editor/ai/AiResponseMessage.tsx +234 -78
- package/src/editor/ai/AiTerminal.tsx +30 -14
- package/src/editor/client/EditorShell.tsx +128 -25
- package/src/editor/client/editContext.ts +1 -0
- package/src/editor/client/hooks/useSocketMessageHandler.ts +70 -21
- package/src/editor/client/hooks/useWorkbox.ts +4 -4
- package/src/editor/client/itemsRepository.ts +56 -25
- package/src/editor/client/pageModelBuilder.ts +1 -1
- package/src/editor/control-center/Setup.tsx +26 -0
- package/src/editor/control-center/setup-steps/AiSetupStep.tsx +462 -0
- package/src/editor/control-center/setup-steps/DbSetupStep.tsx +84 -0
- package/src/editor/control-center/setup-steps/IndexSetupStep.tsx +56 -0
- package/src/editor/control-center/setup-steps/SettingsSetupStep.tsx +176 -0
- package/src/editor/field-types/InternalLinkFieldEditor.tsx +3 -1
- package/src/editor/field-types/RichTextEditorComponent.tsx +0 -1
- package/src/editor/field-types/richtext/components/ReactSlate.tsx +14 -6
- package/src/editor/field-types/richtext/utils/profileServiceCache.ts +42 -32
- package/src/editor/menubar/toolbar-sections/CompareControls.tsx +1 -0
- package/src/editor/menubar/toolbar-sections/EditControls.tsx +1 -0
- package/src/editor/menubar/toolbar-sections/ViewportControls.tsx +1 -0
- package/src/editor/page-editor-chrome/InlineEditor.tsx +29 -6
- package/src/editor/page-viewer/EditorForm.tsx +13 -2
- package/src/editor/page-viewer/PageViewerFrame.tsx +4 -0
- package/src/editor/pageModel.ts +1 -0
- package/src/editor/reviews/Comment.tsx +1 -1
- package/src/editor/reviews/CommentDisplayPopover.tsx +2 -22
- package/src/editor/reviews/CommentPopover.tsx +3 -24
- package/src/editor/reviews/CommentView.tsx +3 -2
- package/src/editor/reviews/Comments.tsx +162 -35
- package/src/editor/reviews/commentAi.ts +5 -0
- package/src/editor/sidebar/Debug.tsx +1 -5
- package/src/editor/sidebar/ViewSelector.tsx +144 -28
- package/src/editor/ui/Icons.tsx +55 -0
- package/src/editor/utils.ts +33 -0
- package/src/revision.ts +2 -2
- package/src/splash-screen/SplashScreen.tsx +5 -6
- package/src/types.ts +3 -0
|
@@ -122,7 +122,10 @@ import { Toaster } from "../../components/ui/sonner";
|
|
|
122
122
|
import { Tour } from "../../tour/Tour";
|
|
123
123
|
import { usePageViewContext } from "../page-viewer/pageViewContext";
|
|
124
124
|
|
|
125
|
-
import {
|
|
125
|
+
import {
|
|
126
|
+
getComments,
|
|
127
|
+
getAvailableCommentTags,
|
|
128
|
+
} from "../services/reviewsService";
|
|
126
129
|
import { useReviews } from "../reviews/useReviews";
|
|
127
130
|
import uuid from "react-uuid";
|
|
128
131
|
import { flushSync } from "react-dom";
|
|
@@ -256,6 +259,10 @@ export function EditorShell({
|
|
|
256
259
|
}
|
|
257
260
|
});
|
|
258
261
|
|
|
262
|
+
// Block editing until the server acknowledges our client-info for the current item
|
|
263
|
+
const [isClientInfoAcknowledged, setIsClientInfoAcknowledged] =
|
|
264
|
+
useState(false);
|
|
265
|
+
|
|
259
266
|
if (typeof window !== "undefined")
|
|
260
267
|
sessionStorage?.setItem("sessionId", sessionId);
|
|
261
268
|
|
|
@@ -305,6 +312,9 @@ export function EditorShell({
|
|
|
305
312
|
const [suggestedEdits, setSuggestedEdits] = useState<SuggestedEdit[]>([]);
|
|
306
313
|
const [showSuggestedEdits, setShowSuggestedEdits] = useState(false);
|
|
307
314
|
const [showSuggestedEditsDiff, setShowSuggestedEditsDiff] = useState(false);
|
|
315
|
+
const [availableCommentTags, setAvailableCommentTags] = useState<
|
|
316
|
+
{ label: string; color: string }[]
|
|
317
|
+
>([]);
|
|
308
318
|
|
|
309
319
|
const [showComments, setShowComments] = useState<boolean>(() => {
|
|
310
320
|
const savedShowComments =
|
|
@@ -439,6 +449,34 @@ export function EditorShell({
|
|
|
439
449
|
configuration,
|
|
440
450
|
});
|
|
441
451
|
|
|
452
|
+
// Track which items are rendered/used by the current page for targeted refresh
|
|
453
|
+
const pageItemsSetRef = useRef<Set<string>>(new Set());
|
|
454
|
+
const makeItemKey = useCallback(
|
|
455
|
+
(d: ItemDescriptor) => `${d.id}-${d.language}-${d.version}`.toLowerCase(),
|
|
456
|
+
[],
|
|
457
|
+
);
|
|
458
|
+
useEffect(() => {
|
|
459
|
+
const newSet = new Set<string>();
|
|
460
|
+
const collect = (component?: Component) => {
|
|
461
|
+
if (!component) return;
|
|
462
|
+
if (component.datasourceItem)
|
|
463
|
+
newSet.add(makeItemKey(component.datasourceItem));
|
|
464
|
+
component.items?.forEach((i) => newSet.add(makeItemKey(i)));
|
|
465
|
+
component.placeholders?.forEach((ph) => ph.components?.forEach(collect));
|
|
466
|
+
};
|
|
467
|
+
if (pageViewContext?.page) {
|
|
468
|
+
// Include the page item itself
|
|
469
|
+
const pageDescriptor = pageViewContext.page.item.descriptor;
|
|
470
|
+
newSet.add(makeItemKey(pageDescriptor));
|
|
471
|
+
collect(pageViewContext.page.rootComponent);
|
|
472
|
+
}
|
|
473
|
+
pageItemsSetRef.current = newSet;
|
|
474
|
+
}, [pageViewContext.page?.editRevision]);
|
|
475
|
+
const isItemUsedInCurrentPage = useCallback(
|
|
476
|
+
(item: ItemDescriptor) => pageItemsSetRef.current.has(makeItemKey(item)),
|
|
477
|
+
[makeItemKey],
|
|
478
|
+
);
|
|
479
|
+
|
|
442
480
|
const socketMessageListeners = useRef<Set<(data: any) => void>>(new Set());
|
|
443
481
|
|
|
444
482
|
const addSocketMessageListener = useCallback(
|
|
@@ -450,7 +488,7 @@ export function EditorShell({
|
|
|
450
488
|
);
|
|
451
489
|
|
|
452
490
|
const reviews = useReviews({
|
|
453
|
-
currentItemDescriptor
|
|
491
|
+
currentItemDescriptor,
|
|
454
492
|
addSocketMessageListener: addSocketMessageListener,
|
|
455
493
|
});
|
|
456
494
|
|
|
@@ -488,7 +526,7 @@ export function EditorShell({
|
|
|
488
526
|
}
|
|
489
527
|
}, [currentView]);
|
|
490
528
|
|
|
491
|
-
const
|
|
529
|
+
const sendClientInfoImmediate = useCallback(async () => {
|
|
492
530
|
const clientInfoMessage = {
|
|
493
531
|
type: "client-info",
|
|
494
532
|
sessionId: sessionId,
|
|
@@ -502,15 +540,18 @@ export function EditorShell({
|
|
|
502
540
|
: null,
|
|
503
541
|
};
|
|
504
542
|
|
|
505
|
-
// const socket = (globalThis as any).editorSocket;
|
|
506
|
-
|
|
507
|
-
//if (socket.readyState !== WebSocket.OPEN) {
|
|
508
543
|
const url = "/alpaca/editor/client";
|
|
544
|
+
|
|
509
545
|
await post(url, clientInfoMessage);
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
546
|
+
}, [sessionId]);
|
|
547
|
+
|
|
548
|
+
const debouncedSendClientInfo = useDebouncedCallback(() => {
|
|
549
|
+
void sendClientInfoImmediate();
|
|
550
|
+
}, 100);
|
|
551
|
+
|
|
552
|
+
const sendClientInfo = useCallback(() => {
|
|
553
|
+
debouncedSendClientInfo();
|
|
554
|
+
}, [debouncedSendClientInfo]);
|
|
514
555
|
|
|
515
556
|
const startTour = useCallback(() => {
|
|
516
557
|
setIsTourActive(true);
|
|
@@ -797,9 +838,26 @@ export function EditorShell({
|
|
|
797
838
|
}, [currentItemDescriptor, pageViewContext.page?.item.id]);
|
|
798
839
|
|
|
799
840
|
useEffect(() => {
|
|
841
|
+
// When the item changes, reset acknowledgment and re-send client info
|
|
842
|
+
setIsClientInfoAcknowledged(false);
|
|
800
843
|
sendClientInfo();
|
|
801
844
|
}, [currentItemDescriptor]);
|
|
802
845
|
|
|
846
|
+
// Detect acknowledgment via presence of our session with matching item in active-sessions
|
|
847
|
+
useEffect(() => {
|
|
848
|
+
if (!currentItemDescriptor) return;
|
|
849
|
+
try {
|
|
850
|
+
const me = activeSessions.find((s) => s.sessionId === sessionId);
|
|
851
|
+
const item = me?.item;
|
|
852
|
+
const acknowledged =
|
|
853
|
+
!!item &&
|
|
854
|
+
item.id === currentItemDescriptor.id &&
|
|
855
|
+
item.language === currentItemDescriptor.language &&
|
|
856
|
+
item.version === currentItemDescriptor.version;
|
|
857
|
+
if (acknowledged) setIsClientInfoAcknowledged(true);
|
|
858
|
+
} catch {}
|
|
859
|
+
}, [activeSessions, currentItemDescriptor, sessionId]);
|
|
860
|
+
|
|
803
861
|
const loadComments = useCallback(async () => {
|
|
804
862
|
if (!currentItemDescriptor) return;
|
|
805
863
|
const result = await getComments(
|
|
@@ -914,6 +972,31 @@ export function EditorShell({
|
|
|
914
972
|
loadSuggestedEdits();
|
|
915
973
|
}, [currentItemDescriptor]);
|
|
916
974
|
|
|
975
|
+
// Load available comment tags once per current item descriptor
|
|
976
|
+
useEffect(() => {
|
|
977
|
+
let cancelled = false;
|
|
978
|
+
const loadTags = async () => {
|
|
979
|
+
try {
|
|
980
|
+
if (!currentItemDescriptor) {
|
|
981
|
+
if (!cancelled) setAvailableCommentTags([]);
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
const tags = await getAvailableCommentTags(currentItemDescriptor);
|
|
985
|
+
if (!cancelled) setAvailableCommentTags(tags || []);
|
|
986
|
+
} catch {
|
|
987
|
+
if (!cancelled) setAvailableCommentTags([]);
|
|
988
|
+
}
|
|
989
|
+
};
|
|
990
|
+
loadTags();
|
|
991
|
+
return () => {
|
|
992
|
+
cancelled = true;
|
|
993
|
+
};
|
|
994
|
+
}, [
|
|
995
|
+
currentItemDescriptor?.id,
|
|
996
|
+
currentItemDescriptor?.language,
|
|
997
|
+
currentItemDescriptor?.version,
|
|
998
|
+
]);
|
|
999
|
+
|
|
917
1000
|
// Load favorites on mount
|
|
918
1001
|
useEffect(() => {
|
|
919
1002
|
loadFavorites();
|
|
@@ -1278,7 +1361,7 @@ export function EditorShell({
|
|
|
1278
1361
|
|
|
1279
1362
|
const { workboxItems } = useWorkbox({
|
|
1280
1363
|
page,
|
|
1281
|
-
|
|
1364
|
+
currentItemDescriptor,
|
|
1282
1365
|
validate,
|
|
1283
1366
|
});
|
|
1284
1367
|
|
|
@@ -1301,6 +1384,7 @@ export function EditorShell({
|
|
|
1301
1384
|
loadHistory,
|
|
1302
1385
|
addRecentEdit,
|
|
1303
1386
|
socketMessageListeners,
|
|
1387
|
+
isItemUsedInCurrentPage,
|
|
1304
1388
|
});
|
|
1305
1389
|
|
|
1306
1390
|
const { socketRef: socketInstanceRef } = useEditorWebSocket({
|
|
@@ -1444,7 +1528,9 @@ export function EditorShell({
|
|
|
1444
1528
|
!contentEditorItem ||
|
|
1445
1529
|
!contentEditorItem.canWriteItem ||
|
|
1446
1530
|
!contentEditorItem.canWriteLanguage ||
|
|
1447
|
-
!contentEditorItem.canWriteWorkflow
|
|
1531
|
+
!contentEditorItem.canWriteWorkflow ||
|
|
1532
|
+
// Block editing until server acknowledges client-info for the current item
|
|
1533
|
+
(currentItemDescriptor ? !isClientInfoAcknowledged : false);
|
|
1448
1534
|
|
|
1449
1535
|
const updateUrl = useCallback(
|
|
1450
1536
|
(params: Record<string, string | undefined>) => {
|
|
@@ -1711,18 +1797,31 @@ export function EditorShell({
|
|
|
1711
1797
|
}
|
|
1712
1798
|
|
|
1713
1799
|
if (actionButton.action) {
|
|
1714
|
-
setActiveFieldActions((prevFieldActions) =>
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
)
|
|
1724
|
-
|
|
1725
|
-
|
|
1800
|
+
setActiveFieldActions((prevFieldActions) => {
|
|
1801
|
+
const isSameField = (x: FieldAction) =>
|
|
1802
|
+
x.field.fieldId == fieldDescriptor.fieldId &&
|
|
1803
|
+
x.field.item.id == fieldItem.id &&
|
|
1804
|
+
x.field.item.language == fieldItem.language &&
|
|
1805
|
+
x.field.item.version == fieldItem.version;
|
|
1806
|
+
|
|
1807
|
+
const existing = prevFieldActions.find(isSameField);
|
|
1808
|
+
|
|
1809
|
+
// Preserve any existing state/message (e.g., an early websocket error)
|
|
1810
|
+
// to avoid overwriting it with a stale "running" state
|
|
1811
|
+
const mergedOp: FieldAction = existing
|
|
1812
|
+
? {
|
|
1813
|
+
...op,
|
|
1814
|
+
state: existing.state,
|
|
1815
|
+
message: existing.message,
|
|
1816
|
+
label: existing.label ?? op.label,
|
|
1817
|
+
}
|
|
1818
|
+
: op;
|
|
1819
|
+
|
|
1820
|
+
return [
|
|
1821
|
+
...prevFieldActions.filter((x) => !isSameField(x)),
|
|
1822
|
+
mergedOp,
|
|
1823
|
+
];
|
|
1824
|
+
});
|
|
1726
1825
|
await executeFieldServerAction(
|
|
1727
1826
|
fieldDescriptor.item,
|
|
1728
1827
|
fieldDescriptor.fieldId,
|
|
@@ -1741,7 +1840,10 @@ export function EditorShell({
|
|
|
1741
1840
|
|
|
1742
1841
|
itemsRepository.refreshItems([fieldDescriptor.item]);
|
|
1743
1842
|
|
|
1744
|
-
|
|
1843
|
+
// Only mark as success if no websocket error has been reported
|
|
1844
|
+
if (op.state === "running") {
|
|
1845
|
+
op.state = "success";
|
|
1846
|
+
}
|
|
1745
1847
|
requestRefresh("immediate");
|
|
1746
1848
|
}
|
|
1747
1849
|
},
|
|
@@ -1853,6 +1955,7 @@ export function EditorShell({
|
|
|
1853
1955
|
selectedComment,
|
|
1854
1956
|
setSelectedComment,
|
|
1855
1957
|
comments,
|
|
1958
|
+
availableCommentTags,
|
|
1856
1959
|
loadComments,
|
|
1857
1960
|
setComments,
|
|
1858
1961
|
showComments,
|
|
@@ -136,6 +136,7 @@ export type EditContextType = {
|
|
|
136
136
|
comments: Comment[];
|
|
137
137
|
suggestedEdits: SuggestedEdit[];
|
|
138
138
|
setSuggestedEdits: React.Dispatch<React.SetStateAction<SuggestedEdit[]>>;
|
|
139
|
+
availableCommentTags: { label: string; color: string }[];
|
|
139
140
|
loadComments: () => void;
|
|
140
141
|
setComments: React.Dispatch<React.SetStateAction<Comment[]>>;
|
|
141
142
|
selectedComment: Comment | undefined;
|
|
@@ -48,6 +48,7 @@ export function useSocketMessageHandler(deps: {
|
|
|
48
48
|
loadHistory: (item: ItemDescriptor) => void;
|
|
49
49
|
addRecentEdit: (edit: any) => void;
|
|
50
50
|
socketMessageListeners: React.MutableRefObject<Set<(data: any) => void>>;
|
|
51
|
+
isItemUsedInCurrentPage: (item: ItemDescriptor) => boolean;
|
|
51
52
|
}) {
|
|
52
53
|
const {
|
|
53
54
|
sessionId,
|
|
@@ -67,6 +68,7 @@ export function useSocketMessageHandler(deps: {
|
|
|
67
68
|
loadHistory,
|
|
68
69
|
addRecentEdit,
|
|
69
70
|
socketMessageListeners,
|
|
71
|
+
isItemUsedInCurrentPage,
|
|
70
72
|
} = deps;
|
|
71
73
|
|
|
72
74
|
const messageHandler = useCallback(
|
|
@@ -114,23 +116,37 @@ export function useSocketMessageHandler(deps: {
|
|
|
114
116
|
} catch {}
|
|
115
117
|
}
|
|
116
118
|
|
|
117
|
-
if (message.type === "item-deleted") {
|
|
118
|
-
itemsRepository.onItemsDeleted([
|
|
119
|
-
{ item: message.payload.item, parentId: message.payload.parentId },
|
|
120
|
-
]);
|
|
121
|
-
if (message.payload.item.id === currentItemDescriptor?.id) {
|
|
122
|
-
loadItem({
|
|
123
|
-
id: message.payload.parentId,
|
|
124
|
-
language: currentItemDescriptor?.language ?? "en",
|
|
125
|
-
version: 0,
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
119
|
if (message.type === "item-changed") {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
120
|
+
if (message.payload.action === "delete") {
|
|
121
|
+
const parentId =
|
|
122
|
+
message.payload?.parentId ?? message.payload?.changes?.parentId;
|
|
123
|
+
itemsRepository.onItemsDeleted([
|
|
124
|
+
{ item: message.payload.item, parentId },
|
|
125
|
+
]);
|
|
126
|
+
if (
|
|
127
|
+
message.payload.item.id === currentItemDescriptor?.id &&
|
|
128
|
+
parentId
|
|
129
|
+
) {
|
|
130
|
+
loadItem({
|
|
131
|
+
id: parentId,
|
|
132
|
+
language: currentItemDescriptor?.language ?? "en",
|
|
133
|
+
version: 0,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
} else {
|
|
137
|
+
await itemsRepository.refreshItems(
|
|
138
|
+
[message.payload.item],
|
|
139
|
+
message.payload.changes,
|
|
140
|
+
);
|
|
141
|
+
const changes = message?.payload?.changes;
|
|
142
|
+
const isPropertiesChange = changes?.properties !== false; // default true
|
|
143
|
+
if (
|
|
144
|
+
isPropertiesChange &&
|
|
145
|
+
message.payload.item.id === currentItemDescriptor?.id
|
|
146
|
+
) {
|
|
147
|
+
await loadItemVersions();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
134
150
|
}
|
|
135
151
|
|
|
136
152
|
if (message.type === "item-version-added") {
|
|
@@ -242,10 +258,32 @@ export function useSocketMessageHandler(deps: {
|
|
|
242
258
|
field.type !== "multi-line text" &&
|
|
243
259
|
field.type !== "rich text")
|
|
244
260
|
) {
|
|
245
|
-
itemsRepository.refreshItems(
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
261
|
+
itemsRepository.refreshItems(
|
|
262
|
+
[
|
|
263
|
+
{
|
|
264
|
+
...editFieldOperation.mainItem,
|
|
265
|
+
id: editFieldOperation.itemId,
|
|
266
|
+
},
|
|
267
|
+
],
|
|
268
|
+
{ properties: true },
|
|
269
|
+
);
|
|
270
|
+
// Only refresh the page if it is affected
|
|
271
|
+
const pageDescriptor: ItemDescriptor | undefined =
|
|
272
|
+
currentItemRef.current?.descriptor;
|
|
273
|
+
const isSamePage =
|
|
274
|
+
!!pageDescriptor &&
|
|
275
|
+
!!op.mainItem &&
|
|
276
|
+
op.mainItem.id === pageDescriptor.id &&
|
|
277
|
+
op.mainItem.language === pageDescriptor.language &&
|
|
278
|
+
op.mainItem.version === pageDescriptor.version;
|
|
279
|
+
const targetItem: ItemDescriptor = {
|
|
280
|
+
id: editFieldOperation.itemId,
|
|
281
|
+
language: editFieldOperation.mainItem?.language,
|
|
282
|
+
version: editFieldOperation.mainItem?.version,
|
|
283
|
+
};
|
|
284
|
+
if (isSamePage || isItemUsedInCurrentPage(targetItem)) {
|
|
285
|
+
requestRefresh("immediate");
|
|
286
|
+
}
|
|
249
287
|
} else {
|
|
250
288
|
itemsRepository.updateFieldValue(
|
|
251
289
|
{
|
|
@@ -263,7 +301,18 @@ export function useSocketMessageHandler(deps: {
|
|
|
263
301
|
);
|
|
264
302
|
}
|
|
265
303
|
} else {
|
|
266
|
-
|
|
304
|
+
// For non-field operations, refresh only if the current page matches the operation's main item
|
|
305
|
+
const pageDescriptor: ItemDescriptor | undefined =
|
|
306
|
+
currentItemRef.current?.descriptor;
|
|
307
|
+
const shouldRefresh =
|
|
308
|
+
!!pageDescriptor &&
|
|
309
|
+
!!op.mainItem &&
|
|
310
|
+
op.mainItem.id === pageDescriptor.id &&
|
|
311
|
+
op.mainItem.language === pageDescriptor.language &&
|
|
312
|
+
op.mainItem.version === pageDescriptor.version;
|
|
313
|
+
if (shouldRefresh) {
|
|
314
|
+
requestRefresh("immediate");
|
|
315
|
+
}
|
|
267
316
|
}
|
|
268
317
|
if (
|
|
269
318
|
op.mainItem &&
|
|
@@ -7,10 +7,10 @@ import type { WorkboxItem } from "../../../types";
|
|
|
7
7
|
|
|
8
8
|
export function useWorkbox(params: {
|
|
9
9
|
page: any;
|
|
10
|
-
|
|
10
|
+
currentItemDescriptor?: ItemDescriptor;
|
|
11
11
|
validate: (items: ItemDescriptor[]) => void;
|
|
12
12
|
}) {
|
|
13
|
-
const { page,
|
|
13
|
+
const { page, currentItemDescriptor, validate } = params;
|
|
14
14
|
const [workboxItems, setWorkboxItems] = useState<WorkboxItem[]>([]);
|
|
15
15
|
|
|
16
16
|
useEffect(() => {
|
|
@@ -45,11 +45,11 @@ export function useWorkbox(params: {
|
|
|
45
45
|
|
|
46
46
|
useEffect(() => {
|
|
47
47
|
const items: ItemDescriptor[] = [];
|
|
48
|
-
if (
|
|
48
|
+
if (currentItemDescriptor) items.push(currentItemDescriptor);
|
|
49
49
|
if (page) collectAllItems(page.rootComponent, items);
|
|
50
50
|
loadWorkboxDebounced(items.filter(Boolean));
|
|
51
51
|
return () => loadWorkboxDebounced.cancel();
|
|
52
|
-
}, [page,
|
|
52
|
+
}, [page, currentItemDescriptor, loadWorkboxDebounced]);
|
|
53
53
|
|
|
54
54
|
function collectAllItems(component: Component, items: ItemDescriptor[]) {
|
|
55
55
|
component.placeholders.forEach((x) => {
|
|
@@ -18,6 +18,14 @@ export type ItemChange = {
|
|
|
18
18
|
item: ItemDescriptor;
|
|
19
19
|
action: "add" | "delete" | "update";
|
|
20
20
|
parentId?: string;
|
|
21
|
+
changes: {
|
|
22
|
+
fields?: string[];
|
|
23
|
+
children?: boolean;
|
|
24
|
+
properties?: boolean;
|
|
25
|
+
parentId?: string;
|
|
26
|
+
name?: string;
|
|
27
|
+
templateId?: string;
|
|
28
|
+
};
|
|
21
29
|
};
|
|
22
30
|
export type DeletedItem = { item: ItemDescriptor; parentId: string };
|
|
23
31
|
|
|
@@ -34,7 +42,10 @@ export type ItemsRepository = {
|
|
|
34
42
|
rawValue?: string | null,
|
|
35
43
|
) => Promise<number>;
|
|
36
44
|
|
|
37
|
-
refreshItems: (
|
|
45
|
+
refreshItems: (
|
|
46
|
+
items: ItemDescriptor[],
|
|
47
|
+
changes?: { properties?: boolean; children?: boolean; fields?: string[] },
|
|
48
|
+
) => Promise<void>;
|
|
38
49
|
revision: number;
|
|
39
50
|
onFieldSaved: (
|
|
40
51
|
field: FieldDescriptor,
|
|
@@ -62,32 +73,14 @@ const isTemplateChange = async (
|
|
|
62
73
|
getItem: (descriptor: ItemDescriptor) => Promise<FullItem | undefined>,
|
|
63
74
|
): Promise<boolean> => {
|
|
64
75
|
try {
|
|
65
|
-
const item = await getItem(change.item);
|
|
66
|
-
if (!item) return false;
|
|
67
|
-
|
|
68
76
|
// Check if the item is under the /sitecore/templates path
|
|
69
77
|
if (
|
|
70
|
-
item
|
|
71
|
-
item.path.toLowerCase().startsWith("/sitecore/templates")
|
|
78
|
+
change.item?.path &&
|
|
79
|
+
change.item.path.toLowerCase().startsWith("/sitecore/templates")
|
|
72
80
|
) {
|
|
73
81
|
return true;
|
|
74
82
|
}
|
|
75
83
|
|
|
76
|
-
// Check if the item's templateId indicates it's a template-related item
|
|
77
|
-
const templateId = item.templateId?.toLowerCase();
|
|
78
|
-
if (
|
|
79
|
-
templateId === TEMPLATE_TEMPLATE_ID.toLowerCase() ||
|
|
80
|
-
templateId === TEMPLATE_FIELD_TEMPLATE_ID.toLowerCase() ||
|
|
81
|
-
templateId === TEMPLATE_SECTION_TEMPLATE_ID.toLowerCase()
|
|
82
|
-
) {
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Check if the item is under the templates root using the configured templates root ID
|
|
87
|
-
if (item.path && item.path.includes(templatesRootItemId)) {
|
|
88
|
-
return true;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
84
|
return false;
|
|
92
85
|
} catch (error) {
|
|
93
86
|
console.error("Error checking if item change is template-related:", error);
|
|
@@ -215,8 +208,10 @@ export function useItemsRepository(
|
|
|
215
208
|
descriptor: x,
|
|
216
209
|
}));
|
|
217
210
|
|
|
211
|
+
// Always refetch version 0 ("latest") to avoid stale caches after new versions are created
|
|
218
212
|
const itemsMissing = keys.filter(
|
|
219
|
-
(
|
|
213
|
+
(entry) =>
|
|
214
|
+
entry.descriptor.version === 0 || !itemsMap.current.has(entry.key),
|
|
220
215
|
);
|
|
221
216
|
const map = itemsMap;
|
|
222
217
|
|
|
@@ -306,6 +301,12 @@ export function useItemsRepository(
|
|
|
306
301
|
const itemsToFetch: ItemDescriptor[] = [];
|
|
307
302
|
|
|
308
303
|
for (const { key, descriptor } of keys) {
|
|
304
|
+
// Always refetch version 0 ("latest") stubs to avoid stale cache
|
|
305
|
+
if (descriptor.version === 0) {
|
|
306
|
+
itemsToFetch.push(descriptor);
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
|
|
309
310
|
// If we have the full item, use it
|
|
310
311
|
const fullItem = itemsMap.current.get(key);
|
|
311
312
|
if (fullItem) {
|
|
@@ -442,7 +443,26 @@ export function useItemsRepository(
|
|
|
442
443
|
);
|
|
443
444
|
|
|
444
445
|
const refreshItems = useCallback(
|
|
445
|
-
async (
|
|
446
|
+
async (
|
|
447
|
+
items: ItemDescriptor[],
|
|
448
|
+
changes?: { properties?: boolean; children?: boolean; fields?: string[] },
|
|
449
|
+
) => {
|
|
450
|
+
// If only children changed and no properties changed, avoid fetching items.
|
|
451
|
+
// Still notify listeners so UIs like ContentTree can reload children lists.
|
|
452
|
+
const onlyChildrenChanged =
|
|
453
|
+
changes?.children === true && changes?.fields === undefined;
|
|
454
|
+
|
|
455
|
+
if (onlyChildrenChanged) {
|
|
456
|
+
triggerItemsChangedEvent(
|
|
457
|
+
items.map((x) => ({
|
|
458
|
+
item: x,
|
|
459
|
+
action: "update",
|
|
460
|
+
changes: changes ?? {},
|
|
461
|
+
})),
|
|
462
|
+
);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
|
|
446
466
|
// Filter to only include items that are already cached
|
|
447
467
|
const itemsToFetch = items.filter((item) =>
|
|
448
468
|
itemsMap.current.has(generateKey(item)),
|
|
@@ -530,7 +550,11 @@ export function useItemsRepository(
|
|
|
530
550
|
setRevision((prev) => prev + 1);
|
|
531
551
|
}
|
|
532
552
|
triggerItemsChangedEvent(
|
|
533
|
-
items.map((x) => ({
|
|
553
|
+
items.map((x) => ({
|
|
554
|
+
item: x,
|
|
555
|
+
action: "update",
|
|
556
|
+
changes: changes ?? {},
|
|
557
|
+
})),
|
|
534
558
|
);
|
|
535
559
|
},
|
|
536
560
|
[setRevision, fetchItemsFromServer, setModifiedFields],
|
|
@@ -575,7 +599,13 @@ export function useItemsRepository(
|
|
|
575
599
|
if (dependencies) {
|
|
576
600
|
refreshItems(dependencies);
|
|
577
601
|
}
|
|
578
|
-
triggerItemsChangedEvent([
|
|
602
|
+
triggerItemsChangedEvent([
|
|
603
|
+
{
|
|
604
|
+
item: field.item,
|
|
605
|
+
action: "update",
|
|
606
|
+
changes: { fields: [field.fieldId] },
|
|
607
|
+
},
|
|
608
|
+
]);
|
|
579
609
|
},
|
|
580
610
|
[setModifiedFields, refreshItems],
|
|
581
611
|
);
|
|
@@ -593,6 +623,7 @@ export function useItemsRepository(
|
|
|
593
623
|
item: x.item,
|
|
594
624
|
action: "delete",
|
|
595
625
|
parentId: x.parentId,
|
|
626
|
+
changes: {},
|
|
596
627
|
})),
|
|
597
628
|
);
|
|
598
629
|
setRevision((prev) => prev + 1);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useEditContext } from "../client/editContext";
|
|
3
|
+
import { DbSetupStep } from "./setup-steps/DbSetupStep";
|
|
4
|
+
import { SettingsSetupStep } from "./setup-steps/SettingsSetupStep";
|
|
5
|
+
import { AiSetupStep } from "./setup-steps/AiSetupStep";
|
|
6
|
+
import { IndexSetupStep } from "./setup-steps/IndexSetupStep";
|
|
7
|
+
|
|
8
|
+
export function Setup() {
|
|
9
|
+
const editContext = useEditContext();
|
|
10
|
+
const configuredSteps = editContext?.configuration?.setup?.steps;
|
|
11
|
+
const steps = React.useMemo(() => {
|
|
12
|
+
if (configuredSteps && configuredSteps.length > 0) return configuredSteps;
|
|
13
|
+
return [DbSetupStep, SettingsSetupStep, AiSetupStep, IndexSetupStep];
|
|
14
|
+
}, [configuredSteps]);
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div className="h-full overflow-auto p-4">
|
|
18
|
+
<div className="mb-4 text-lg font-semibold text-gray-800">Setup</div>
|
|
19
|
+
<div className="grid grid-cols-1 gap-4">
|
|
20
|
+
{steps.map((Step, idx) => (
|
|
21
|
+
<Step key={idx} />
|
|
22
|
+
))}
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
}
|