@alpaca-editor/core 1.0.4086 → 1.0.4089
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/config/config.js +10 -3
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +4 -0
- package/dist/editor/ContentTree.js +43 -21
- package/dist/editor/ContentTree.js.map +1 -1
- package/dist/editor/FieldListField.js +12 -1
- 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 +50 -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 +121 -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.js +12 -223
- package/dist/editor/control-center/Setup.js.map +1 -1
- 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/ImageFieldEditor.js +1 -1
- package/dist/editor/field-types/ImageFieldEditor.js.map +1 -1
- package/dist/editor/field-types/MultiLineText.js +1 -1
- package/dist/editor/field-types/MultiLineText.js.map +1 -1
- package/dist/editor/field-types/PictureFieldEditor.js +1 -1
- package/dist/editor/field-types/PictureFieldEditor.js.map +1 -1
- package/dist/editor/field-types/RawEditor.js +1 -1
- package/dist/editor/field-types/RawEditor.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/SingleLineText.js +1 -1
- package/dist/editor/field-types/SingleLineText.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 +6 -1
- 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/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 +0 -5
- package/dist/types.d.ts +2 -0
- package/package.json +1 -1
- package/src/components/ui/card.tsx +1 -1
- package/src/config/config.tsx +9 -2
- package/src/config/types.ts +5 -0
- package/src/editor/ContentTree.tsx +48 -23
- package/src/editor/FieldListField.tsx +13 -2
- package/src/editor/ai/AgentTerminal.tsx +118 -71
- package/src/editor/ai/Agents.tsx +56 -1
- package/src/editor/ai/AiResponseMessage.tsx +234 -78
- package/src/editor/ai/AiTerminal.tsx +30 -14
- package/src/editor/client/EditorShell.tsx +140 -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 +14 -420
- 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/ImageFieldEditor.tsx +0 -1
- package/src/editor/field-types/MultiLineText.tsx +0 -1
- package/src/editor/field-types/PictureFieldEditor.tsx +0 -1
- package/src/editor/field-types/RawEditor.tsx +0 -1
- package/src/editor/field-types/RichTextEditorComponent.tsx +0 -1
- package/src/editor/field-types/SingleLineText.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 +5 -1
- 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/revision.ts +2 -2
- package/src/splash-screen/SplashScreen.tsx +5 -6
- package/src/types.ts +3 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import React, { useCallback, useState } from "react";
|
|
2
|
+
import { Card } from "../../../components/ui/card";
|
|
3
|
+
import { Button } from "../../../components/ui/button";
|
|
4
|
+
import { useEditContext } from "../../client/editContext";
|
|
5
|
+
import { ItemDescriptor } from "../../pageModel";
|
|
6
|
+
import { getChildren } from "../../services/contentService";
|
|
7
|
+
import type { ItemTreeNodeData } from "../../services/contentService";
|
|
8
|
+
import { contentItemId } from "../../../config/config";
|
|
9
|
+
import { CheckCircle, AlertCircle, Settings, RefreshCw } from "lucide-react";
|
|
10
|
+
|
|
11
|
+
type StepState = "idle" | "checking" | "success" | "error";
|
|
12
|
+
|
|
13
|
+
function findByName(
|
|
14
|
+
items: ItemTreeNodeData[],
|
|
15
|
+
name: string,
|
|
16
|
+
): ItemTreeNodeData | undefined {
|
|
17
|
+
const target = name.toLowerCase();
|
|
18
|
+
return items.find(
|
|
19
|
+
(x) =>
|
|
20
|
+
(((x.displayName as string) || x.name || "") as string).toLowerCase() ===
|
|
21
|
+
target,
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function SettingsSetupStep() {
|
|
26
|
+
const editContext = useEditContext();
|
|
27
|
+
const [settingsState, setSettingsState] = useState<StepState>("checking");
|
|
28
|
+
const [settingsItem, setSettingsItem] = useState<ItemDescriptor | null>(null);
|
|
29
|
+
const [settingsError, setSettingsError] = useState<string | null>(null);
|
|
30
|
+
const [creatingSettings, setCreatingSettings] = useState(false);
|
|
31
|
+
|
|
32
|
+
const userLang = editContext?.contentEditorItem?.language || "en";
|
|
33
|
+
|
|
34
|
+
const statusIcon = useCallback((state: StepState) => {
|
|
35
|
+
if (state === "success")
|
|
36
|
+
return <CheckCircle className="h-4 w-4 text-green-600" strokeWidth={1} />;
|
|
37
|
+
if (state === "error")
|
|
38
|
+
return <AlertCircle className="h-4 w-4 text-red-600" strokeWidth={1} />;
|
|
39
|
+
return (
|
|
40
|
+
<RefreshCw
|
|
41
|
+
className="h-4 w-4 animate-spin text-amber-600"
|
|
42
|
+
strokeWidth={1}
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
45
|
+
}, []);
|
|
46
|
+
|
|
47
|
+
const resolvePathUnderContent = useCallback(
|
|
48
|
+
async (names: string[]): Promise<ItemDescriptor | null> => {
|
|
49
|
+
if (!editContext?.sessionId) return null;
|
|
50
|
+
let currentId = contentItemId;
|
|
51
|
+
for (const segment of names) {
|
|
52
|
+
const children = (await getChildren(
|
|
53
|
+
currentId,
|
|
54
|
+
editContext.sessionId,
|
|
55
|
+
[],
|
|
56
|
+
false,
|
|
57
|
+
userLang,
|
|
58
|
+
"path",
|
|
59
|
+
)) as unknown as ItemTreeNodeData[];
|
|
60
|
+
const next = findByName(children, segment);
|
|
61
|
+
if (!next) return null;
|
|
62
|
+
currentId = next.id;
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
id: currentId,
|
|
66
|
+
language: userLang,
|
|
67
|
+
version: 0,
|
|
68
|
+
} as ItemDescriptor;
|
|
69
|
+
},
|
|
70
|
+
[editContext?.sessionId, userLang],
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const checkSettingsItem = useCallback(async () => {
|
|
74
|
+
try {
|
|
75
|
+
setSettingsState("checking");
|
|
76
|
+
setSettingsError(null);
|
|
77
|
+
setSettingsItem(null);
|
|
78
|
+
|
|
79
|
+
const editorItem = await resolvePathUnderContent(["Settings", "Editor"]);
|
|
80
|
+
if (editorItem) {
|
|
81
|
+
setSettingsItem(editorItem);
|
|
82
|
+
setSettingsState("success");
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
setSettingsState("error");
|
|
87
|
+
setSettingsError(
|
|
88
|
+
"Editor settings item not found at /sitecore/content/Settings/Editor",
|
|
89
|
+
);
|
|
90
|
+
} catch (e: any) {
|
|
91
|
+
setSettingsState("error");
|
|
92
|
+
setSettingsError(e?.message || "Failed to check settings item");
|
|
93
|
+
}
|
|
94
|
+
}, [resolvePathUnderContent]);
|
|
95
|
+
|
|
96
|
+
const createSettingsItem = useCallback(async () => {
|
|
97
|
+
if (!editContext?.itemsRepository) return;
|
|
98
|
+
try {
|
|
99
|
+
setCreatingSettings(true);
|
|
100
|
+
setSettingsError(null);
|
|
101
|
+
|
|
102
|
+
const parentDescriptor: ItemDescriptor = {
|
|
103
|
+
id: "{2DE1F76B-39F0-49A0-8589-8F360E0B3F7E}",
|
|
104
|
+
language: userLang,
|
|
105
|
+
version: 0,
|
|
106
|
+
} as any;
|
|
107
|
+
|
|
108
|
+
const branchOrTemplateId = "f133cae9-8fbc-4cdf-868e-321a533dec5b";
|
|
109
|
+
|
|
110
|
+
const created = await editContext.operations.createItem(
|
|
111
|
+
parentDescriptor,
|
|
112
|
+
branchOrTemplateId,
|
|
113
|
+
"Editor",
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
if (created) {
|
|
117
|
+
setSettingsItem(created);
|
|
118
|
+
setSettingsState("success");
|
|
119
|
+
} else {
|
|
120
|
+
setSettingsState("error");
|
|
121
|
+
setSettingsError("Failed to create settings item");
|
|
122
|
+
}
|
|
123
|
+
} catch (e: any) {
|
|
124
|
+
setSettingsState("error");
|
|
125
|
+
setSettingsError(e?.message || "Failed to create settings item");
|
|
126
|
+
} finally {
|
|
127
|
+
setCreatingSettings(false);
|
|
128
|
+
}
|
|
129
|
+
}, [editContext, userLang]);
|
|
130
|
+
|
|
131
|
+
React.useEffect(() => {
|
|
132
|
+
checkSettingsItem();
|
|
133
|
+
}, [checkSettingsItem]);
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<Card
|
|
137
|
+
icon={<Settings strokeWidth={1} className="h-5 w-5" />}
|
|
138
|
+
title="Editor settings"
|
|
139
|
+
description="Checks presence of /sitecore/content/Settings/Editor and allows creating it."
|
|
140
|
+
>
|
|
141
|
+
<div className="flex items-center justify-between gap-2">
|
|
142
|
+
<div className="flex items-center gap-2">
|
|
143
|
+
{statusIcon(settingsItem ? "success" : settingsState)}
|
|
144
|
+
<span className="text-sm text-gray-700">
|
|
145
|
+
{settingsItem
|
|
146
|
+
? "Settings item present"
|
|
147
|
+
: settingsState === "error"
|
|
148
|
+
? "Settings item missing"
|
|
149
|
+
: "Checking..."}
|
|
150
|
+
</span>
|
|
151
|
+
</div>
|
|
152
|
+
{!settingsItem && (
|
|
153
|
+
<Button
|
|
154
|
+
size="sm"
|
|
155
|
+
onClick={createSettingsItem}
|
|
156
|
+
disabled={creatingSettings}
|
|
157
|
+
>
|
|
158
|
+
{creatingSettings ? (
|
|
159
|
+
<RefreshCw strokeWidth={1} className="h-4 w-4 animate-spin" />
|
|
160
|
+
) : (
|
|
161
|
+
<CheckCircle strokeWidth={1} className="h-4 w-4" />
|
|
162
|
+
)}
|
|
163
|
+
Create
|
|
164
|
+
</Button>
|
|
165
|
+
)}
|
|
166
|
+
</div>
|
|
167
|
+
{settingsError && (
|
|
168
|
+
<div className="mt-2 rounded border border-yellow-200 bg-yellow-50 p-2 text-xs whitespace-pre-wrap text-yellow-800">
|
|
169
|
+
{settingsError}
|
|
170
|
+
</div>
|
|
171
|
+
)}
|
|
172
|
+
</Card>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export default SettingsSetupStep;
|
|
@@ -40,7 +40,10 @@ import { LinkEditorDialog } from "../../../LinkEditorDialog";
|
|
|
40
40
|
|
|
41
41
|
import { htmlToSlate, slateToHtml } from "../utils/conversion";
|
|
42
42
|
import { createPluginsFromConfig } from "../config/pluginFactory";
|
|
43
|
-
import {
|
|
43
|
+
import {
|
|
44
|
+
createKeyboardHandler,
|
|
45
|
+
generateInternalLinkUrl,
|
|
46
|
+
} from "../utils/plugins";
|
|
44
47
|
import { normalizeUrl } from "../../../utils/urlUtils";
|
|
45
48
|
|
|
46
49
|
import { useCachedSimplifiedProfile } from "../hooks/useProfileCache";
|
|
@@ -774,11 +777,15 @@ export const ReactSlate = forwardRef<any, ReactSlateProps>((props, ref) => {
|
|
|
774
777
|
textAlign: element.align || "left",
|
|
775
778
|
};
|
|
776
779
|
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
780
|
+
if (element.type === "link") {
|
|
781
|
+
const isInternal = element.link?.type === "internal";
|
|
782
|
+
const url = isInternal
|
|
783
|
+
? generateInternalLinkUrl(
|
|
784
|
+
element.link?.itemId,
|
|
785
|
+
element.link?.targetItemLongId,
|
|
786
|
+
element.link?.queryString,
|
|
787
|
+
)
|
|
788
|
+
: normalizeUrl(element.url || element.link?.url || "#");
|
|
782
789
|
|
|
783
790
|
return (
|
|
784
791
|
<a
|
|
@@ -965,6 +972,7 @@ export const ReactSlate = forwardRef<any, ReactSlateProps>((props, ref) => {
|
|
|
965
972
|
)}
|
|
966
973
|
<Editable
|
|
967
974
|
className={classNames(
|
|
975
|
+
"border-gray-3 min-h-8 rounded-sm border",
|
|
968
976
|
readOnly ? "bg-gray-4" : "bg-gray-5",
|
|
969
977
|
"focus-shadow p-2",
|
|
970
978
|
"slate-editable",
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { RichTextEditorProfile } from
|
|
2
|
-
import { mapSitecoreProfileJsonToSlate } from
|
|
1
|
+
import { RichTextEditorProfile } from "../types";
|
|
2
|
+
import { mapSitecoreProfileJsonToSlate } from "./profileMapper";
|
|
3
3
|
|
|
4
4
|
// Single cache for parsed profiles
|
|
5
5
|
const parsedProfileCache = new Map<string, RichTextEditorProfile | null>();
|
|
6
6
|
|
|
7
7
|
// Cache for in-flight requests to prevent duplicate calls
|
|
8
|
-
const inflightRequests = new Map<
|
|
8
|
+
const inflightRequests = new Map<
|
|
9
|
+
string,
|
|
10
|
+
Promise<RichTextEditorProfile | null>
|
|
11
|
+
>();
|
|
9
12
|
|
|
10
13
|
// Cache statistics
|
|
11
14
|
interface ServiceCacheStats {
|
|
@@ -17,7 +20,7 @@ interface ServiceCacheStats {
|
|
|
17
20
|
let serviceStats: ServiceCacheStats = {
|
|
18
21
|
hits: 0,
|
|
19
22
|
misses: 0,
|
|
20
|
-
cacheSize: 0
|
|
23
|
+
cacheSize: 0,
|
|
21
24
|
};
|
|
22
25
|
|
|
23
26
|
/**
|
|
@@ -26,60 +29,67 @@ let serviceStats: ServiceCacheStats = {
|
|
|
26
29
|
*/
|
|
27
30
|
export async function getCachedParsedProfile(
|
|
28
31
|
profilePath: string,
|
|
29
|
-
serviceCall: (path: string) => Promise<any
|
|
32
|
+
serviceCall: (path: string) => Promise<any>,
|
|
30
33
|
): Promise<RichTextEditorProfile | null> {
|
|
31
|
-
console.log(`[ProfileServiceCache] Getting parsed profile for path: ${profilePath}`);
|
|
32
|
-
|
|
34
|
+
//console.log(`[ProfileServiceCache] Getting parsed profile for path: ${profilePath}`);
|
|
35
|
+
|
|
33
36
|
// Check if we already have a cached result
|
|
34
37
|
if (parsedProfileCache.has(profilePath)) {
|
|
35
38
|
serviceStats.hits++;
|
|
36
39
|
const cachedResult = parsedProfileCache.get(profilePath)!;
|
|
37
|
-
console.log(`[ProfileServiceCache] Cache hit for ${profilePath}`);
|
|
40
|
+
//console.log(`[ProfileServiceCache] Cache hit for ${profilePath}`);
|
|
38
41
|
return cachedResult;
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
// Check if there's already an in-flight request for this profile
|
|
42
45
|
if (inflightRequests.has(profilePath)) {
|
|
43
46
|
serviceStats.hits++;
|
|
44
|
-
console.log(
|
|
47
|
+
//console.log(
|
|
48
|
+
// `[ProfileServiceCache] In-flight request found for ${profilePath}`,
|
|
49
|
+
//);
|
|
45
50
|
return inflightRequests.get(profilePath)!;
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
// Make the service call, parse, and cache the result
|
|
49
54
|
serviceStats.misses++;
|
|
50
|
-
console.log(`[ProfileServiceCache] Making service call for ${profilePath}`);
|
|
51
|
-
|
|
55
|
+
// console.log(`[ProfileServiceCache] Making service call for ${profilePath}`);
|
|
56
|
+
|
|
52
57
|
const servicePromise = serviceCall(profilePath)
|
|
53
|
-
.then(response => {
|
|
54
|
-
console.log(`[ProfileServiceCache] Service call completed for ${profilePath}`, response);
|
|
55
|
-
|
|
58
|
+
.then((response) => {
|
|
59
|
+
//console.log(`[ProfileServiceCache] Service call completed for ${profilePath}`, response);
|
|
60
|
+
|
|
56
61
|
let parsedProfile: RichTextEditorProfile | null = null;
|
|
57
|
-
|
|
62
|
+
|
|
58
63
|
if (response?.data) {
|
|
59
64
|
const jsonString = JSON.stringify(response.data);
|
|
60
|
-
console.log(`[ProfileServiceCache] Parsing profile for ${profilePath}, JSON length: ${jsonString.length}`);
|
|
61
|
-
|
|
65
|
+
//console.log(`[ProfileServiceCache] Parsing profile for ${profilePath}, JSON length: ${jsonString.length}`);
|
|
66
|
+
|
|
62
67
|
parsedProfile = mapSitecoreProfileJsonToSlate(jsonString);
|
|
63
|
-
|
|
68
|
+
|
|
64
69
|
if (parsedProfile) {
|
|
65
|
-
console.log(`[ProfileServiceCache] Successfully parsed profile for ${profilePath}`);
|
|
70
|
+
//console.log(`[ProfileServiceCache] Successfully parsed profile for ${profilePath}`);
|
|
66
71
|
} else {
|
|
67
|
-
console.warn(
|
|
72
|
+
console.warn(
|
|
73
|
+
`[ProfileServiceCache] Failed to parse profile for ${profilePath}`,
|
|
74
|
+
);
|
|
68
75
|
}
|
|
69
76
|
}
|
|
70
|
-
|
|
77
|
+
|
|
71
78
|
// Cache the parsed result (even if null)
|
|
72
79
|
parsedProfileCache.set(profilePath, parsedProfile);
|
|
73
80
|
serviceStats.cacheSize = parsedProfileCache.size;
|
|
74
|
-
|
|
81
|
+
|
|
75
82
|
// Remove from in-flight requests
|
|
76
83
|
inflightRequests.delete(profilePath);
|
|
77
|
-
|
|
78
|
-
console.log(`[ProfileServiceCache] Cached parsed profile for ${profilePath}, cache size: ${serviceStats.cacheSize}
|
|
84
|
+
|
|
85
|
+
//console.log( `[ProfileServiceCache] Cached parsed profile for ${profilePath}, cache size: ${serviceStats.cacheSize}`, );
|
|
79
86
|
return parsedProfile;
|
|
80
87
|
})
|
|
81
|
-
.catch(error => {
|
|
82
|
-
console.error(
|
|
88
|
+
.catch((error) => {
|
|
89
|
+
console.error(
|
|
90
|
+
`[ProfileServiceCache] Service call failed for ${profilePath}:`,
|
|
91
|
+
error,
|
|
92
|
+
);
|
|
83
93
|
// Remove from in-flight requests on error
|
|
84
94
|
inflightRequests.delete(profilePath);
|
|
85
95
|
throw error;
|
|
@@ -97,7 +107,7 @@ export async function getCachedParsedProfile(
|
|
|
97
107
|
*/
|
|
98
108
|
export async function getCachedRichTextProfile(
|
|
99
109
|
profilePath: string,
|
|
100
|
-
serviceCall: (path: string) => Promise<any
|
|
110
|
+
serviceCall: (path: string) => Promise<any>,
|
|
101
111
|
): Promise<string | null> {
|
|
102
112
|
const parsedProfile = await getCachedParsedProfile(profilePath, serviceCall);
|
|
103
113
|
return parsedProfile ? JSON.stringify(parsedProfile) : null;
|
|
@@ -107,13 +117,13 @@ export async function getCachedRichTextProfile(
|
|
|
107
117
|
* Clear all caches
|
|
108
118
|
*/
|
|
109
119
|
export function clearServiceCache(): void {
|
|
110
|
-
console.log(
|
|
120
|
+
console.log("[ProfileServiceCache] Clearing all caches");
|
|
111
121
|
parsedProfileCache.clear();
|
|
112
122
|
inflightRequests.clear();
|
|
113
123
|
serviceStats = {
|
|
114
124
|
hits: 0,
|
|
115
125
|
misses: 0,
|
|
116
|
-
cacheSize: 0
|
|
126
|
+
cacheSize: 0,
|
|
117
127
|
};
|
|
118
128
|
}
|
|
119
129
|
|
|
@@ -124,11 +134,11 @@ export function evictProfileFromServiceCache(profilePath: string): boolean {
|
|
|
124
134
|
console.log(`[ProfileServiceCache] Evicting profile: ${profilePath}`);
|
|
125
135
|
const evicted = parsedProfileCache.delete(profilePath);
|
|
126
136
|
inflightRequests.delete(profilePath);
|
|
127
|
-
|
|
137
|
+
|
|
128
138
|
if (evicted) {
|
|
129
139
|
serviceStats.cacheSize = parsedProfileCache.size;
|
|
130
140
|
}
|
|
131
|
-
|
|
141
|
+
|
|
132
142
|
return evicted;
|
|
133
143
|
}
|
|
134
144
|
|
|
@@ -151,4 +161,4 @@ export function getCachedProfilePaths(): string[] {
|
|
|
151
161
|
*/
|
|
152
162
|
export function isProfilePathCached(profilePath: string): boolean {
|
|
153
163
|
return parsedProfileCache.has(profilePath);
|
|
154
|
-
}
|
|
164
|
+
}
|
|
@@ -43,6 +43,7 @@ export function EditControls({
|
|
|
43
43
|
icon={<EyeIcon className="h-6 w-6 p-1" strokeWidth={1} />}
|
|
44
44
|
label="Preview"
|
|
45
45
|
size="large"
|
|
46
|
+
data-testid="preview-mode-button"
|
|
46
47
|
selected={editContext.mode === "preview"}
|
|
47
48
|
onClick={() => editContext.setMode("preview")}
|
|
48
49
|
/>
|
|
@@ -516,7 +516,7 @@ export function InlineEditor({
|
|
|
516
516
|
// First, process modified fields directly (from updateFields functionality)
|
|
517
517
|
modifiedFieldsContext?.modifiedFields.forEach((field) => {
|
|
518
518
|
const elements = doc.querySelectorAll(
|
|
519
|
-
`[data-fieldid="${field.fieldId}"][data-itemid="${field.item.id}"][data-language="${field.item.language}"][data-version="${field.item.version}"`,
|
|
519
|
+
`[data-fieldid="${field.fieldId}"][data-itemid="${field.item.id}"][data-language="${field.item.language}"][data-version="${field.item.version}"]`,
|
|
520
520
|
);
|
|
521
521
|
|
|
522
522
|
elements?.forEach(async (element) => {
|
|
@@ -528,7 +528,10 @@ export function InlineEditor({
|
|
|
528
528
|
const fieldType = realField?.type;
|
|
529
529
|
|
|
530
530
|
if (fieldType && isTextFieldType(fieldType)) {
|
|
531
|
-
|
|
531
|
+
const next = field?.value ? (field.value as string) : "";
|
|
532
|
+
if (element.innerHTML !== next) {
|
|
533
|
+
element.innerHTML = next;
|
|
534
|
+
}
|
|
532
535
|
}
|
|
533
536
|
}
|
|
534
537
|
});
|
|
@@ -590,6 +593,18 @@ export function InlineEditor({
|
|
|
590
593
|
if (!fieldId || !itemId || !language || !versionStr) return;
|
|
591
594
|
const version = parseInt(versionStr, 10);
|
|
592
595
|
|
|
596
|
+
// Skip if this field currently has local modifications to avoid stomping fresh edits
|
|
597
|
+
const isModifiedNow = modifiedFieldsContext?.modifiedFields?.some(
|
|
598
|
+
(m: any) =>
|
|
599
|
+
m.fieldId === fieldId &&
|
|
600
|
+
m.item.id === itemId &&
|
|
601
|
+
m.item.language === language &&
|
|
602
|
+
m.item.version === version,
|
|
603
|
+
);
|
|
604
|
+
if (isModifiedNow) {
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
|
|
593
608
|
// Build an item descriptor.
|
|
594
609
|
const descriptor = { id: itemId, language, version };
|
|
595
610
|
|
|
@@ -619,7 +634,9 @@ export function InlineEditor({
|
|
|
619
634
|
|
|
620
635
|
// If showSuggestedEdits is false, update with the base value.
|
|
621
636
|
if (!context.showSuggestedEdits && context.mode !== "suggestions") {
|
|
622
|
-
fieldElement.innerHTML
|
|
637
|
+
if (fieldElement.innerHTML !== originalValue) {
|
|
638
|
+
fieldElement.innerHTML = originalValue;
|
|
639
|
+
}
|
|
623
640
|
|
|
624
641
|
return;
|
|
625
642
|
}
|
|
@@ -672,7 +689,9 @@ export function InlineEditor({
|
|
|
672
689
|
|
|
673
690
|
// If showSuggestedEditsDiff is false, show only the merged text
|
|
674
691
|
if (!context.showSuggestedEditsDiff) {
|
|
675
|
-
fieldElement.innerHTML
|
|
692
|
+
if (fieldElement.innerHTML !== mergedValue) {
|
|
693
|
+
fieldElement.innerHTML = mergedValue;
|
|
694
|
+
}
|
|
676
695
|
return;
|
|
677
696
|
}
|
|
678
697
|
|
|
@@ -695,7 +714,9 @@ export function InlineEditor({
|
|
|
695
714
|
});
|
|
696
715
|
|
|
697
716
|
// Update the element's innerHTML with the diff markup.
|
|
698
|
-
fieldElement.innerHTML
|
|
717
|
+
if (fieldElement.innerHTML !== diffHTML) {
|
|
718
|
+
fieldElement.innerHTML = diffHTML;
|
|
719
|
+
}
|
|
699
720
|
});
|
|
700
721
|
};
|
|
701
722
|
|
|
@@ -791,7 +812,9 @@ export function InlineEditor({
|
|
|
791
812
|
}
|
|
792
813
|
|
|
793
814
|
// write it in
|
|
794
|
-
el.innerHTML
|
|
815
|
+
if (el.innerHTML !== value) {
|
|
816
|
+
el.innerHTML = value;
|
|
817
|
+
}
|
|
795
818
|
});
|
|
796
819
|
}, [context.mode, context.showSuggestedEdits, context.suggestedEdits]);
|
|
797
820
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEditContext } from "../client/editContext";
|
|
1
|
+
import { useEditContext, useFieldsEditContext } from "../client/editContext";
|
|
2
2
|
import { getComponentById } from "../componentTreeHelper";
|
|
3
3
|
|
|
4
4
|
import { FieldList, ItemFields } from "../FieldList";
|
|
@@ -28,6 +28,7 @@ export function EditorForm({
|
|
|
28
28
|
initialActiveTab?: string | null;
|
|
29
29
|
}) {
|
|
30
30
|
const editContext = useEditContext()!;
|
|
31
|
+
const fieldsContext = useFieldsEditContext();
|
|
31
32
|
if (!pageViewContext) pageViewContext = editContext.pageView;
|
|
32
33
|
|
|
33
34
|
const insertMode =
|
|
@@ -337,7 +338,17 @@ export function EditorForm({
|
|
|
337
338
|
});
|
|
338
339
|
|
|
339
340
|
return (
|
|
340
|
-
<div
|
|
341
|
+
<div
|
|
342
|
+
className="flex h-full flex-col"
|
|
343
|
+
data-testid="editor-sidepanel"
|
|
344
|
+
onClickCapture={(e) => {
|
|
345
|
+
const target = e.target as HTMLElement;
|
|
346
|
+
const inField = target.closest("[data-field-id]");
|
|
347
|
+
if (!inField) {
|
|
348
|
+
fieldsContext?.setFocusedField(undefined, false);
|
|
349
|
+
}
|
|
350
|
+
}}
|
|
351
|
+
>
|
|
341
352
|
<h1 className="border-gray-3 flex h-12 items-center justify-center border-b p-2">
|
|
342
353
|
{component && component !== pageViewContext.page?.rootComponent && (
|
|
343
354
|
<SimpleIconButton
|
|
@@ -572,6 +572,10 @@ export function PageViewerFrame({
|
|
|
572
572
|
);
|
|
573
573
|
// Don't prevent default - we want the browser's natural cursor positioning
|
|
574
574
|
}
|
|
575
|
+
} else {
|
|
576
|
+
// Clicked inside iframe but not on a field: clear focused field
|
|
577
|
+
fieldsContextRef.current?.setFocusedField(undefined, false);
|
|
578
|
+
fieldsContextRef.current?.setInlineEditingFieldElement(undefined);
|
|
575
579
|
}
|
|
576
580
|
}
|
|
577
581
|
//Forward events so primereact overlays can close
|
|
@@ -818,8 +822,8 @@ export function PageViewerFrame({
|
|
|
818
822
|
() => {
|
|
819
823
|
// Block blur event if it was triggered by clicking on a field
|
|
820
824
|
if (blockBlurEventRef.current < Date.now()) {
|
|
821
|
-
//editContext.setFocusedField(undefined, false);
|
|
822
825
|
fieldsContextRef.current?.setInlineEditingFieldElement(undefined);
|
|
826
|
+
editContextRef.current?.operations.onFieldBlur?.();
|
|
823
827
|
} else {
|
|
824
828
|
blockBlurEventRef.current = 0;
|
|
825
829
|
}
|
package/src/editor/pageModel.ts
CHANGED
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
resolveComment,
|
|
16
16
|
unresolveComment,
|
|
17
17
|
createOrUpdateComment,
|
|
18
|
-
getAvailableCommentTags,
|
|
19
18
|
} from "../services/reviewsService";
|
|
20
19
|
import { CommentView } from "./CommentView";
|
|
21
20
|
import { CommentEditor } from "./CommentEditor";
|
|
@@ -38,29 +37,10 @@ export function CommentDisplayPopover({
|
|
|
38
37
|
const [isSaving, setIsSaving] = useState(false);
|
|
39
38
|
const [editText, setEditText] = useState(comment.text);
|
|
40
39
|
const [deleteConfirm, setDeleteConfirm] = useState(false);
|
|
41
|
-
const
|
|
42
|
-
{ label: string; color: string }[]
|
|
43
|
-
>([]);
|
|
40
|
+
const availableTags = editContext?.availableCommentTags || [];
|
|
44
41
|
const contentRef = React.useRef<HTMLDivElement | null>(null);
|
|
45
42
|
|
|
46
|
-
//
|
|
47
|
-
React.useEffect(() => {
|
|
48
|
-
let cancelled = false;
|
|
49
|
-
const loadTags = async () => {
|
|
50
|
-
try {
|
|
51
|
-
const descriptor = editContext?.contentEditorItem?.descriptor;
|
|
52
|
-
if (!descriptor) return;
|
|
53
|
-
const tags = await getAvailableCommentTags(descriptor);
|
|
54
|
-
if (!cancelled) setAvailableTags(tags || []);
|
|
55
|
-
} catch {
|
|
56
|
-
if (!cancelled) setAvailableTags([]);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
loadTags();
|
|
60
|
-
return () => {
|
|
61
|
-
cancelled = true;
|
|
62
|
-
};
|
|
63
|
-
}, [editContext?.contentEditorItem?.descriptor?.id]);
|
|
43
|
+
// Tags are now provided by editContext.availableCommentTags
|
|
64
44
|
|
|
65
45
|
const canDelete =
|
|
66
46
|
!comment.isNew && comment.author === editContext?.user?.name;
|