@alpaca-editor/core 1.0.4026 → 1.0.4030
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/ActionButton.js +2 -2
- package/dist/components/ActionButton.js.map +1 -1
- package/dist/components/SimpleLanguageSelector.js +3 -1
- package/dist/components/SimpleLanguageSelector.js.map +1 -1
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/config/config.js +1 -1
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +1 -1
- package/dist/editor/ContextMenu.js +0 -1
- package/dist/editor/ContextMenu.js.map +1 -1
- package/dist/editor/Editor.js +9 -3
- package/dist/editor/Editor.js.map +1 -1
- package/dist/editor/FieldListField.js +16 -24
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/ImageEditButton.js +1 -1
- package/dist/editor/ImageEditButton.js.map +1 -1
- package/dist/editor/ImageEditor.js +1 -1
- package/dist/editor/ImageEditor.js.map +1 -1
- package/dist/editor/MainLayout.js +2 -2
- package/dist/editor/MainLayout.js.map +1 -1
- package/dist/editor/Terminal.js +1 -1
- package/dist/editor/Terminal.js.map +1 -1
- package/dist/editor/Titlebar.js +0 -1
- package/dist/editor/Titlebar.js.map +1 -1
- package/dist/editor/ai/AgentCostDisplay.d.ts +26 -0
- package/dist/editor/ai/AgentCostDisplay.js +65 -0
- package/dist/editor/ai/AgentCostDisplay.js.map +1 -0
- package/dist/editor/ai/Agents.js +83 -29
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiPromptPopover.d.ts +7 -0
- package/dist/editor/ai/AiPromptPopover.js +111 -0
- package/dist/editor/ai/AiPromptPopover.js.map +1 -0
- package/dist/editor/ai/AiResponseMessage.d.ts +1 -0
- package/dist/editor/ai/AiResponseMessage.js +104 -18
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/AiTerminal.d.ts +18 -2
- package/dist/editor/ai/AiTerminal.js +423 -63
- package/dist/editor/ai/AiTerminal.js.map +1 -1
- package/dist/editor/ai/EditorAiTerminal.d.ts +2 -1
- package/dist/editor/ai/EditorAiTerminal.js +2 -2
- package/dist/editor/ai/EditorAiTerminal.js.map +1 -1
- package/dist/editor/ai/editorAiContext.d.ts +0 -1
- package/dist/editor/ai/editorAiContext.js +0 -2
- package/dist/editor/ai/editorAiContext.js.map +1 -1
- package/dist/editor/client/EditorClient.d.ts +3 -2
- package/dist/editor/client/EditorClient.js +326 -68
- package/dist/editor/client/EditorClient.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +6 -4
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/client/fieldModificationStore.d.ts +19 -0
- package/dist/editor/client/fieldModificationStore.js +125 -0
- package/dist/editor/client/fieldModificationStore.js.map +1 -0
- package/dist/editor/client/itemsRepository.d.ts +1 -1
- package/dist/editor/client/itemsRepository.js +38 -28
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/client/operations.d.ts +1 -0
- package/dist/editor/client/operations.js +39 -31
- package/dist/editor/client/operations.js.map +1 -1
- package/dist/editor/commands/componentCommands.js +5 -3
- package/dist/editor/commands/componentCommands.js.map +1 -1
- package/dist/editor/commands/itemCommands.js.map +1 -1
- package/dist/editor/component-designer/aiContext.js +0 -2
- package/dist/editor/component-designer/aiContext.js.map +1 -1
- package/dist/editor/field-types/DropLinkEditor.js +1 -1
- package/dist/editor/field-types/DropLinkEditor.js.map +1 -1
- package/dist/editor/field-types/MultiLineText.js +5 -7
- package/dist/editor/field-types/MultiLineText.js.map +1 -1
- package/dist/editor/field-types/RichTextEditorComponent.js +5 -7
- package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
- package/dist/editor/field-types/SingleLineText.js +5 -7
- package/dist/editor/field-types/SingleLineText.js.map +1 -1
- package/dist/editor/hooks/useEditorSettings.d.ts +17 -0
- package/dist/editor/hooks/useEditorSettings.js +61 -0
- package/dist/editor/hooks/useEditorSettings.js.map +1 -0
- package/dist/editor/menubar/ItemActionsMenu.js +2 -2
- package/dist/editor/menubar/ItemActionsMenu.js.map +1 -1
- package/dist/editor/menubar/PageSelector.js +1 -1
- package/dist/editor/menubar/PageSelector.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/InsertControls.js +1 -1
- package/dist/editor/menubar/toolbar-sections/InsertControls.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js +1 -1
- package/dist/editor/menubar/toolbar-sections/UtilityControls.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/FieldEditedIndicators.js +4 -3
- package/dist/editor/page-editor-chrome/FieldEditedIndicators.js.map +1 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js +9 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js +0 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
- package/dist/editor/page-viewer/EditorForm.js +1 -1
- package/dist/editor/page-viewer/EditorForm.js.map +1 -1
- package/dist/editor/page-viewer/PageViewer.js +9 -8
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +7 -1
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/page-viewer/pageViewContext.js +40 -6
- package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
- package/dist/editor/reviews/Comment.js +7 -6
- package/dist/editor/reviews/Comment.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +84 -12
- package/dist/editor/services/agentService.js +256 -15
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +18 -3
- package/dist/editor/services/aiService.js +5 -3
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/services/contextService.js +0 -1
- package/dist/editor/services/contextService.js.map +1 -1
- package/dist/editor/services/systemService.d.ts +2 -1
- package/dist/editor/services/systemService.js +3 -0
- package/dist/editor/services/systemService.js.map +1 -1
- package/dist/editor/sidebar/ComponentPalette.js +1 -1
- package/dist/editor/sidebar/ComponentPalette.js.map +1 -1
- package/dist/editor/sidebar/EditHistory.js +2 -2
- package/dist/editor/sidebar/EditHistory.js.map +1 -1
- package/dist/editor/sidebar/GraphQL.d.ts +1 -0
- package/dist/editor/sidebar/GraphQL.js +8 -2
- package/dist/editor/sidebar/GraphQL.js.map +1 -1
- package/dist/editor/sidebar/MainContentTree.js +1 -1
- package/dist/editor/sidebar/MainContentTree.js.map +1 -1
- package/dist/editor/sidebar/SEOInfo.js +1 -1
- package/dist/editor/sidebar/SEOInfo.js.map +1 -1
- package/dist/editor/sidebar/ViewSelector.d.ts +4 -1
- package/dist/editor/sidebar/ViewSelector.js +64 -48
- package/dist/editor/sidebar/ViewSelector.js.map +1 -1
- package/dist/editor/ui/PerfectTree.js +2 -11
- package/dist/editor/ui/PerfectTree.js.map +1 -1
- package/dist/editor/ui/SimpleIconButton.d.ts +2 -0
- package/dist/editor/ui/SimpleIconButton.js +8 -4
- package/dist/editor/ui/SimpleIconButton.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/page-wizard/steps/CollectStep.js +1 -1
- package/dist/page-wizard/steps/CollectStep.js.map +1 -1
- package/dist/page-wizard/steps/StructureStep.js +1 -1
- package/dist/page-wizard/steps/StructureStep.js.map +1 -1
- package/dist/page-wizard/steps/TranslateStep.js +233 -18
- package/dist/page-wizard/steps/TranslateStep.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/splash-screen/RecentPages.js +1 -13
- package/dist/splash-screen/RecentPages.js.map +1 -1
- package/dist/splash-screen/SplashScreen.js +1 -1
- package/dist/splash-screen/SplashScreen.js.map +1 -1
- package/dist/styles.css +88 -3
- package/dist/types.d.ts +6 -0
- package/package.json +2 -2
- package/src/components/ActionButton.tsx +3 -2
- package/src/components/SimpleLanguageSelector.tsx +6 -1
- package/src/config/config.tsx +1 -1
- package/src/config/types.ts +1 -1
- package/src/editor/ContextMenu.tsx +0 -3
- package/src/editor/Editor.tsx +11 -3
- package/src/editor/FieldListField.tsx +22 -31
- package/src/editor/ImageEditButton.tsx +1 -0
- package/src/editor/ImageEditor.tsx +1 -0
- package/src/editor/MainLayout.tsx +2 -2
- package/src/editor/Terminal.tsx +1 -1
- package/src/editor/Titlebar.tsx +0 -2
- package/src/editor/ai/AgentCostDisplay.tsx +237 -0
- package/src/editor/ai/Agents.tsx +147 -65
- package/src/editor/ai/AiPromptPopover.tsx +209 -0
- package/src/editor/ai/AiResponseMessage.tsx +232 -45
- package/src/editor/ai/AiTerminal.tsx +559 -88
- package/src/editor/ai/EditorAiTerminal.tsx +3 -0
- package/src/editor/ai/editorAiContext.ts +0 -3
- package/src/editor/client/EditorClient.tsx +409 -117
- package/src/editor/client/editContext.ts +7 -5
- package/src/editor/client/fieldModificationStore.ts +196 -0
- package/src/editor/client/itemsRepository.ts +41 -31
- package/src/editor/client/operations.ts +95 -76
- package/src/editor/commands/componentCommands.tsx +9 -3
- package/src/editor/commands/itemCommands.tsx +0 -1
- package/src/editor/component-designer/aiContext.ts +0 -2
- package/src/editor/field-types/DropLinkEditor.tsx +1 -1
- package/src/editor/field-types/MultiLineText.tsx +9 -9
- package/src/editor/field-types/RichTextEditorComponent.tsx +8 -8
- package/src/editor/field-types/SingleLineText.tsx +9 -9
- package/src/editor/hooks/useEditorSettings.ts +68 -0
- package/src/editor/menubar/ItemActionsMenu.tsx +3 -2
- package/src/editor/menubar/PageSelector.tsx +1 -1
- package/src/editor/menubar/toolbar-sections/EditControls.tsx +1 -0
- package/src/editor/menubar/toolbar-sections/InsertControls.tsx +1 -0
- package/src/editor/menubar/toolbar-sections/UtilityControls.tsx +2 -0
- package/src/editor/menubar/toolbar-sections/ViewportControls.tsx +2 -0
- package/src/editor/page-editor-chrome/FieldEditedIndicators.tsx +4 -3
- package/src/editor/page-editor-chrome/FrameMenu.tsx +10 -1
- package/src/editor/page-editor-chrome/useInlineAICompletion.tsx +0 -1
- package/src/editor/page-viewer/EditorForm.tsx +1 -0
- package/src/editor/page-viewer/PageViewer.tsx +9 -8
- package/src/editor/page-viewer/PageViewerFrame.tsx +7 -1
- package/src/editor/page-viewer/pageViewContext.ts +40 -5
- package/src/editor/reviews/Comment.tsx +7 -7
- package/src/editor/services/agentService.ts +405 -31
- package/src/editor/services/aiService.ts +24 -5
- package/src/editor/services/contextService.ts +0 -1
- package/src/editor/services/systemService.ts +7 -1
- package/src/editor/sidebar/ComponentPalette.tsx +4 -1
- package/src/editor/sidebar/EditHistory.tsx +2 -0
- package/src/editor/sidebar/GraphQL.tsx +19 -7
- package/src/editor/sidebar/MainContentTree.tsx +1 -1
- package/src/editor/sidebar/SEOInfo.tsx +1 -1
- package/src/editor/sidebar/ViewSelector.tsx +80 -64
- package/src/editor/ui/PerfectTree.tsx +2 -18
- package/src/editor/ui/SimpleIconButton.tsx +56 -38
- package/src/index.ts +2 -0
- package/src/page-wizard/steps/CollectStep.tsx +0 -2
- package/src/page-wizard/steps/StructureStep.tsx +3 -0
- package/src/page-wizard/steps/TranslateStep.tsx +473 -62
- package/src/revision.ts +2 -2
- package/src/splash-screen/RecentPages.tsx +0 -14
- package/src/splash-screen/SplashScreen.tsx +3 -2
- package/src/types.ts +7 -0
- package/dist/editor/ai/AiPopup.d.ts +0 -10
- package/dist/editor/ai/AiPopup.js +0 -23
- package/dist/editor/ai/AiPopup.js.map +0 -1
- package/dist/editor/ai/AiToolCall.d.ts +0 -5
- package/dist/editor/ai/AiToolCall.js +0 -28
- package/dist/editor/ai/AiToolCall.js.map +0 -1
- package/src/editor/ai/AiPopup.tsx +0 -59
- package/src/editor/ai/AiToolCall.tsx +0 -71
|
@@ -64,6 +64,7 @@ export function getOperationsContext(
|
|
|
64
64
|
setSuggestedEdits: (suggestedEdits: SuggestedEdit[]) => void;
|
|
65
65
|
mode: EditorMode;
|
|
66
66
|
setFocusFieldComponentId: (componentId: string) => void;
|
|
67
|
+
setInsertMode: (insertMode: boolean) => void;
|
|
67
68
|
},
|
|
68
69
|
ui: {
|
|
69
70
|
showErrorToast: (error: { summary?: string; details?: string }) => void;
|
|
@@ -87,25 +88,31 @@ export function getOperationsContext(
|
|
|
87
88
|
stateRef.current = state;
|
|
88
89
|
}, [state]);
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
// Extract values to avoid state object reference issues
|
|
92
|
+
const itemsRepository = state.itemsRepository;
|
|
93
|
+
|
|
94
|
+
const executeOp = useCallback(
|
|
95
|
+
async (
|
|
96
|
+
op: EditOperation,
|
|
97
|
+
options: ExecuteEditOptions = {
|
|
98
|
+
refresh: "immediate",
|
|
99
|
+
},
|
|
100
|
+
): Promise<EditOperation> => {
|
|
101
|
+
console.log("Executing op", op);
|
|
102
|
+
setExecutingEditOperations((ops) => (ops ? [...ops, op] : [op]));
|
|
103
|
+
const result = await executeEditOperation(op, state.sessionId);
|
|
104
|
+
// console.log("Result", result);
|
|
105
|
+
handleResult(result, options);
|
|
106
|
+
await state.refreshHistory(op.mainItem);
|
|
107
|
+
//state.addToEditHistory(result.data as EditOperation);
|
|
108
|
+
setEditOperationExecuted(result.data as EditOperation);
|
|
109
|
+
setExecutingEditOperations((ops) =>
|
|
110
|
+
ops ? ops.filter((x: EditOperation) => x.id !== op.id) : [],
|
|
111
|
+
);
|
|
112
|
+
return result.data as EditOperation;
|
|
94
113
|
},
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
setExecutingEditOperations((ops) => (ops ? [...ops, op] : [op]));
|
|
98
|
-
const result = await executeEditOperation(op, state.sessionId);
|
|
99
|
-
// console.log("Result", result);
|
|
100
|
-
handleResult(result, options);
|
|
101
|
-
await state.refreshHistory(op.mainItem);
|
|
102
|
-
//state.addToEditHistory(result.data as EditOperation);
|
|
103
|
-
setEditOperationExecuted(result.data as EditOperation);
|
|
104
|
-
setExecutingEditOperations((ops) =>
|
|
105
|
-
ops ? ops.filter((x: EditOperation) => x.id !== op.id) : [],
|
|
106
|
-
);
|
|
107
|
-
return result.data as EditOperation;
|
|
108
|
-
};
|
|
114
|
+
[state.sessionId, state.refreshHistory],
|
|
115
|
+
);
|
|
109
116
|
|
|
110
117
|
const duplicateComponents = useCallback(
|
|
111
118
|
({ componentIds }: { componentIds: string[] }) => {
|
|
@@ -123,37 +130,40 @@ export function getOperationsContext(
|
|
|
123
130
|
[state.page],
|
|
124
131
|
);
|
|
125
132
|
|
|
126
|
-
const addComponent =
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
133
|
+
const addComponent = useCallback(
|
|
134
|
+
async (
|
|
135
|
+
componentTypeId: string,
|
|
136
|
+
placeholderKey: string,
|
|
137
|
+
index: number,
|
|
138
|
+
pageItem: ItemDescriptor,
|
|
139
|
+
) => {
|
|
140
|
+
// if (!page) {
|
|
141
|
+
// console.log("Add component: No page found");
|
|
142
|
+
// return;
|
|
143
|
+
// }
|
|
144
|
+
|
|
145
|
+
const op: AddComponentOperation = {
|
|
146
|
+
type: "add-component",
|
|
147
|
+
mainItem: pageItem,
|
|
148
|
+
// parent: parentId
|
|
149
|
+
// ? {
|
|
150
|
+
// id: parentId,
|
|
151
|
+
// language: page.item.language,
|
|
152
|
+
// version: page.item.version,
|
|
153
|
+
// }
|
|
154
|
+
// : undefined,
|
|
155
|
+
placeholderKey,
|
|
156
|
+
placeholderIndex: index,
|
|
157
|
+
componentTypeId,
|
|
158
|
+
date: new Date().toISOString(),
|
|
159
|
+
id: uuid(),
|
|
160
|
+
description: "Add component",
|
|
161
|
+
};
|
|
162
|
+
const result = await executeOp(op);
|
|
163
|
+
return result as AddComponentOperation;
|
|
164
|
+
},
|
|
165
|
+
[executeOp],
|
|
166
|
+
);
|
|
157
167
|
|
|
158
168
|
const handleResult = (
|
|
159
169
|
result: ExecutionResult<unknown>,
|
|
@@ -177,6 +187,11 @@ export function getOperationsContext(
|
|
|
177
187
|
if (executedOp.focus) {
|
|
178
188
|
state.setSelection([executedOp.focus]);
|
|
179
189
|
state.setFocusFieldComponentId(executedOp.focus);
|
|
190
|
+
|
|
191
|
+
// Clear insert mode immediately after selecting the newly added component
|
|
192
|
+
if (executedOp.type === "add-component") {
|
|
193
|
+
state.setInsertMode(false);
|
|
194
|
+
}
|
|
180
195
|
}
|
|
181
196
|
}
|
|
182
197
|
|
|
@@ -272,9 +287,9 @@ export function getOperationsContext(
|
|
|
272
287
|
description: "Rename item",
|
|
273
288
|
} as RenameItemOperation);
|
|
274
289
|
state.requestRefresh("immediate");
|
|
275
|
-
|
|
290
|
+
itemsRepository.refreshItems([item]);
|
|
276
291
|
},
|
|
277
|
-
[
|
|
292
|
+
[itemsRepository, executeOp],
|
|
278
293
|
);
|
|
279
294
|
|
|
280
295
|
const lastEditField = useRef<FieldDescriptor | undefined>(undefined);
|
|
@@ -314,7 +329,7 @@ export function getOperationsContext(
|
|
|
314
329
|
const op = await getOrMergeSuggestedEditOp(field, rawValue, value, {
|
|
315
330
|
page: state.page,
|
|
316
331
|
user: state.user,
|
|
317
|
-
itemsRepository:
|
|
332
|
+
itemsRepository: itemsRepository,
|
|
318
333
|
suggestedEdits: state.suggestedEdits,
|
|
319
334
|
setSuggestedEdits: state.setSuggestedEdits,
|
|
320
335
|
});
|
|
@@ -344,7 +359,7 @@ export function getOperationsContext(
|
|
|
344
359
|
return;
|
|
345
360
|
}
|
|
346
361
|
|
|
347
|
-
const saveSequence = await
|
|
362
|
+
const saveSequence = await itemsRepository.updateFieldValue(
|
|
348
363
|
field,
|
|
349
364
|
state.user ?? { name: "unknown", ai: false },
|
|
350
365
|
true,
|
|
@@ -373,7 +388,7 @@ export function getOperationsContext(
|
|
|
373
388
|
return executeEditFieldDebounced({ field, value, rawValue, refresh });
|
|
374
389
|
},
|
|
375
390
|
[
|
|
376
|
-
|
|
391
|
+
itemsRepository,
|
|
377
392
|
getOrMergeSuggestedEditOp,
|
|
378
393
|
state.suggestedEdits,
|
|
379
394
|
state.page,
|
|
@@ -396,7 +411,7 @@ export function getOperationsContext(
|
|
|
396
411
|
refresh?: "none" | "immediate" | "delayed" | "waitForQuietPeriod";
|
|
397
412
|
saveSequence?: number;
|
|
398
413
|
}): Promise<void> => {
|
|
399
|
-
//
|
|
414
|
+
// itemsRepository.updateFieldValue(
|
|
400
415
|
// field,
|
|
401
416
|
// state.user ?? { name: "unknown", ai: false },
|
|
402
417
|
// true,
|
|
@@ -411,11 +426,11 @@ export function getOperationsContext(
|
|
|
411
426
|
|
|
412
427
|
if (op) {
|
|
413
428
|
await executeOp(op, { refresh });
|
|
414
|
-
await
|
|
415
|
-
|
|
429
|
+
await itemsRepository.refreshItems([field.item]);
|
|
430
|
+
itemsRepository.onFieldSaved(field, val, saveSequence || 0);
|
|
416
431
|
}
|
|
417
432
|
},
|
|
418
|
-
[
|
|
433
|
+
[itemsRepository, executeOp],
|
|
419
434
|
);
|
|
420
435
|
|
|
421
436
|
const executeEditFieldDebounced = useDebouncedCallback(
|
|
@@ -430,7 +445,7 @@ export function getOperationsContext(
|
|
|
430
445
|
rawValue?: string | null;
|
|
431
446
|
refresh?: "none" | "immediate" | "delayed" | "waitForQuietPeriod";
|
|
432
447
|
}) => {
|
|
433
|
-
const saveSequence = await
|
|
448
|
+
const saveSequence = await itemsRepository.updateFieldValue(
|
|
434
449
|
field,
|
|
435
450
|
state.user ?? { name: "unknown", ai: false },
|
|
436
451
|
true,
|
|
@@ -454,10 +469,10 @@ export function getOperationsContext(
|
|
|
454
469
|
rawValue: string | null | undefined,
|
|
455
470
|
value: string | undefined,
|
|
456
471
|
) {
|
|
457
|
-
const item = await
|
|
472
|
+
const item = await itemsRepository.getItem(field.item);
|
|
458
473
|
if (!item) return;
|
|
459
474
|
|
|
460
|
-
const fieldItem = await
|
|
475
|
+
const fieldItem = await itemsRepository.getItem({
|
|
461
476
|
id: field.fieldId,
|
|
462
477
|
language: "en",
|
|
463
478
|
version: 0,
|
|
@@ -557,7 +572,7 @@ export function getOperationsContext(
|
|
|
557
572
|
(x: ItemDescriptor | undefined): x is ItemDescriptor => x != null,
|
|
558
573
|
);
|
|
559
574
|
|
|
560
|
-
await
|
|
575
|
+
await itemsRepository.refreshItems(itemsToRefresh);
|
|
561
576
|
await state.refreshHistory(item);
|
|
562
577
|
return true;
|
|
563
578
|
} else {
|
|
@@ -613,7 +628,7 @@ export function getOperationsContext(
|
|
|
613
628
|
(x: ItemDescriptor | undefined): x is ItemDescriptor => x != null,
|
|
614
629
|
);
|
|
615
630
|
|
|
616
|
-
await
|
|
631
|
+
await itemsRepository.refreshItems(itemsToRefresh);
|
|
617
632
|
await state.refreshHistory(item);
|
|
618
633
|
return true;
|
|
619
634
|
} else {
|
|
@@ -628,9 +643,9 @@ export function getOperationsContext(
|
|
|
628
643
|
|
|
629
644
|
const deleteItems = useCallback(
|
|
630
645
|
async (items: ItemDescriptor[]) => {
|
|
631
|
-
const itemsToDelete = await
|
|
646
|
+
const itemsToDelete = await itemsRepository.getItems(items);
|
|
632
647
|
await executeDeleteItems(items);
|
|
633
|
-
|
|
648
|
+
itemsRepository.onItemsDeleted(
|
|
634
649
|
itemsToDelete.map((x) => ({
|
|
635
650
|
item: x.descriptor,
|
|
636
651
|
parentId: x.parentId,
|
|
@@ -638,14 +653,14 @@ export function getOperationsContext(
|
|
|
638
653
|
);
|
|
639
654
|
state.requestRefresh("immediate");
|
|
640
655
|
},
|
|
641
|
-
[
|
|
656
|
+
[itemsRepository],
|
|
642
657
|
);
|
|
643
658
|
|
|
644
659
|
const moveItems = useCallback(
|
|
645
660
|
async (items: ItemDescriptor[], target: ItemDescriptor, index: number) => {
|
|
646
661
|
await executeMoveItems(items, target, index);
|
|
647
662
|
},
|
|
648
|
-
[
|
|
663
|
+
[itemsRepository],
|
|
649
664
|
);
|
|
650
665
|
|
|
651
666
|
const createItem = useCallback(
|
|
@@ -653,10 +668,10 @@ export function getOperationsContext(
|
|
|
653
668
|
const result = await executeCreateItem(parent, templateId, name);
|
|
654
669
|
if (handleErrorResult(result as ExecutionResult<unknown>, ui, state))
|
|
655
670
|
return;
|
|
656
|
-
|
|
671
|
+
itemsRepository.refreshItems([parent]);
|
|
657
672
|
return result.data as ItemDescriptor;
|
|
658
673
|
},
|
|
659
|
-
[
|
|
674
|
+
[itemsRepository],
|
|
660
675
|
);
|
|
661
676
|
|
|
662
677
|
const createVersion = useCallback(
|
|
@@ -671,7 +686,7 @@ export function getOperationsContext(
|
|
|
671
686
|
const result = await executeCopyItems(items, target, index);
|
|
672
687
|
return result.data as ItemDescriptor[];
|
|
673
688
|
},
|
|
674
|
-
[
|
|
689
|
+
[itemsRepository],
|
|
675
690
|
);
|
|
676
691
|
|
|
677
692
|
const duplicateItem = useCallback(
|
|
@@ -679,10 +694,10 @@ export function getOperationsContext(
|
|
|
679
694
|
const result = await executeDuplicateItem(item, target, name);
|
|
680
695
|
if (handleErrorResult(result as ExecutionResult<unknown>, ui, state))
|
|
681
696
|
return;
|
|
682
|
-
|
|
697
|
+
itemsRepository.refreshItems([target]);
|
|
683
698
|
return result.data as ItemDescriptor;
|
|
684
699
|
},
|
|
685
|
-
[
|
|
700
|
+
[itemsRepository],
|
|
686
701
|
);
|
|
687
702
|
|
|
688
703
|
const onFieldBlur = useCallback(() => {
|
|
@@ -691,6 +706,10 @@ export function getOperationsContext(
|
|
|
691
706
|
lastOp.current = undefined;
|
|
692
707
|
}, []);
|
|
693
708
|
|
|
709
|
+
// Extract state values to avoid object reference changes
|
|
710
|
+
const mode = state.mode;
|
|
711
|
+
const page = state.page;
|
|
712
|
+
|
|
694
713
|
const ops = useMemo(
|
|
695
714
|
() => ({
|
|
696
715
|
addComponent,
|
|
@@ -728,10 +747,10 @@ export function getOperationsContext(
|
|
|
728
747
|
createItem,
|
|
729
748
|
createVersion,
|
|
730
749
|
duplicateComponents,
|
|
731
|
-
|
|
750
|
+
|
|
732
751
|
moveItems,
|
|
733
|
-
|
|
734
|
-
|
|
752
|
+
mode,
|
|
753
|
+
page,
|
|
735
754
|
copyItems,
|
|
736
755
|
duplicateItem,
|
|
737
756
|
onFieldBlur,
|
|
@@ -746,7 +765,7 @@ export function getOperationsContext(
|
|
|
746
765
|
executingEditOperations,
|
|
747
766
|
},
|
|
748
767
|
}),
|
|
749
|
-
[executingEditOperations, editOperationExecuted, ops
|
|
768
|
+
[executingEditOperations, editOperationExecuted, ops],
|
|
750
769
|
);
|
|
751
770
|
}
|
|
752
771
|
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
Trash2,
|
|
25
25
|
TriangleAlert,
|
|
26
26
|
} from "lucide-react";
|
|
27
|
+
import { AiPromptPopover } from "../ai/AiPromptPopover";
|
|
27
28
|
|
|
28
29
|
export type ComponentCommandData = CommandData & {
|
|
29
30
|
components: Component[];
|
|
@@ -134,13 +135,18 @@ function getAiCommand(
|
|
|
134
135
|
): ComponentCommand {
|
|
135
136
|
return {
|
|
136
137
|
id: "ai",
|
|
137
|
-
icon:
|
|
138
|
+
icon: (
|
|
139
|
+
<AiPromptPopover components={components}>
|
|
140
|
+
<Sparkles size={defaultIconSize} />
|
|
141
|
+
</AiPromptPopover>
|
|
142
|
+
),
|
|
138
143
|
label: "AI",
|
|
139
|
-
disabled: (c) => components.length
|
|
144
|
+
disabled: (c) => components.length === 0,
|
|
140
145
|
|
|
141
146
|
execute: async (context: CommandContext<any>) => {
|
|
147
|
+
// The AiPromptPopover handles the click interaction directly
|
|
148
|
+
// No need to execute anything here as the popover trigger handles it
|
|
142
149
|
const event = context.event!;
|
|
143
|
-
editContext.showAiPopup(event as any);
|
|
144
150
|
event.preventDefault();
|
|
145
151
|
event.stopPropagation();
|
|
146
152
|
},
|
|
@@ -90,7 +90,6 @@ export const renameItemCommand: ItemCommand = {
|
|
|
90
90
|
false,
|
|
91
91
|
execute: async (context: ItemCommandContext) => {
|
|
92
92
|
const item = context.data?.items[0];
|
|
93
|
-
|
|
94
93
|
if (!item) return;
|
|
95
94
|
const newName = await context.openDialog<string, ItemNameDialogProps>(
|
|
96
95
|
ItemNameDialog,
|
|
@@ -6,8 +6,9 @@ import {
|
|
|
6
6
|
useEditContextRef,
|
|
7
7
|
useModifiedFieldsContext,
|
|
8
8
|
} from "../client/editContext";
|
|
9
|
+
import { useFieldModification } from "../client/fieldModificationStore";
|
|
9
10
|
|
|
10
|
-
import { useEffect, useRef, useState } from "react";
|
|
11
|
+
import { useEffect, useRef, useState, useMemo } from "react";
|
|
11
12
|
|
|
12
13
|
import { Field } from "../pageModel";
|
|
13
14
|
|
|
@@ -27,13 +28,12 @@ export function MultiLineText({
|
|
|
27
28
|
const fieldItem = field.descriptor.item;
|
|
28
29
|
const [value, setValue] = useState(field.value as string);
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
const modifiedField =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
x.item.version === fieldItem.version,
|
|
31
|
+
// Field-specific subscription - only rerenders when THIS field changes
|
|
32
|
+
const { modifiedField } = useFieldModification(
|
|
33
|
+
field.id,
|
|
34
|
+
fieldItem.id,
|
|
35
|
+
fieldItem.language,
|
|
36
|
+
fieldItem.version
|
|
37
37
|
);
|
|
38
38
|
|
|
39
39
|
useEffect(() => {
|
|
@@ -41,7 +41,7 @@ export function MultiLineText({
|
|
|
41
41
|
if (field.isHistoric) setValue(field.value as string);
|
|
42
42
|
else setValue(modifiedField?.value ?? (field.value as string));
|
|
43
43
|
}
|
|
44
|
-
}, [field.value,
|
|
44
|
+
}, [field.value, field.isHistoric, modifiedField?.value]);
|
|
45
45
|
|
|
46
46
|
if (!editContextRef.current) return;
|
|
47
47
|
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
useEditContextRef,
|
|
5
5
|
useModifiedFieldsContext,
|
|
6
6
|
} from "../client/editContext";
|
|
7
|
+
import { useFieldModification } from "../client/fieldModificationStore";
|
|
7
8
|
|
|
8
9
|
import { useThrottledCallback } from "use-debounce";
|
|
9
10
|
import { useEffect, useRef, useState, useMemo } from "react";
|
|
@@ -46,7 +47,6 @@ export function RichTextEditorComponent({
|
|
|
46
47
|
updateFieldValue?: (value: string) => void;
|
|
47
48
|
}) {
|
|
48
49
|
const editContextRef = useEditContextRef();
|
|
49
|
-
const modifiedFieldsContext = useModifiedFieldsContext();
|
|
50
50
|
const [focused, setFocused] = useState(false);
|
|
51
51
|
const [value, setValue] = useState(field.value as string);
|
|
52
52
|
const editorRef = useRef<HTMLDivElement>(null);
|
|
@@ -56,12 +56,12 @@ export function RichTextEditorComponent({
|
|
|
56
56
|
const fieldItem = field.descriptor.item;
|
|
57
57
|
if (!fieldItem) return null;
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
// Field-specific subscription - only rerenders when THIS field changes
|
|
60
|
+
const { modifiedField } = useFieldModification(
|
|
61
|
+
field.id,
|
|
62
|
+
fieldItem.id,
|
|
63
|
+
fieldItem.language,
|
|
64
|
+
fieldItem.version
|
|
65
65
|
);
|
|
66
66
|
|
|
67
67
|
useEffect(() => {
|
|
@@ -75,7 +75,7 @@ export function RichTextEditorComponent({
|
|
|
75
75
|
if (!isEditorActive && newValue !== value) {
|
|
76
76
|
setValue(newValue);
|
|
77
77
|
}
|
|
78
|
-
}, [field.value,
|
|
78
|
+
}, [field.value, field.isHistoric, modifiedField?.value, focused, value]);
|
|
79
79
|
|
|
80
80
|
const debouncedSetFieldvalue = useThrottledCallback((value) => {
|
|
81
81
|
if (updateFieldValue) {
|
|
@@ -7,8 +7,9 @@ import {
|
|
|
7
7
|
useModifiedFieldsContext,
|
|
8
8
|
SelectionRange,
|
|
9
9
|
} from "../client/editContext";
|
|
10
|
+
import { useFieldModification } from "../client/fieldModificationStore";
|
|
10
11
|
|
|
11
|
-
import { useEffect, useRef, useState } from "react";
|
|
12
|
+
import { useEffect, useRef, useState, useMemo } from "react";
|
|
12
13
|
|
|
13
14
|
import { Field } from "../pageModel";
|
|
14
15
|
|
|
@@ -23,7 +24,6 @@ export function SingleLineText({
|
|
|
23
24
|
}) {
|
|
24
25
|
const editContextRef = useEditContextRef();
|
|
25
26
|
const editContext = useEditContext();
|
|
26
|
-
const modifiedFieldsContext = useModifiedFieldsContext();
|
|
27
27
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
28
28
|
// const [selectionStart, setSelectionStart] = useState(0);
|
|
29
29
|
// const [selectionEnd, setSelectionEnd] = useState(0);
|
|
@@ -32,12 +32,12 @@ export function SingleLineText({
|
|
|
32
32
|
const fieldItem = field.descriptor.item;
|
|
33
33
|
const [value, setValue] = useState<string | undefined>(undefined);
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
// Field-specific subscription - only rerenders when THIS field changes
|
|
36
|
+
const { modifiedField } = useFieldModification(
|
|
37
|
+
field.id,
|
|
38
|
+
fieldItem.id,
|
|
39
|
+
fieldItem.language,
|
|
40
|
+
fieldItem.version
|
|
41
41
|
);
|
|
42
42
|
|
|
43
43
|
useEffect(() => {
|
|
@@ -47,7 +47,7 @@ export function SingleLineText({
|
|
|
47
47
|
setValue(modifiedField?.value ?? (field.value as string));
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
-
}, [field.value,
|
|
50
|
+
}, [field.value, field.isHistoric, modifiedField?.value]);
|
|
51
51
|
|
|
52
52
|
if (!fieldItem) return;
|
|
53
53
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { useEditContext } from "../client/editContext";
|
|
2
|
+
import { EditorSettings } from "../../types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Hook to access general editor settings like Sitecore name validation
|
|
6
|
+
* @returns EditorSettings containing validation patterns and character restrictions
|
|
7
|
+
*/
|
|
8
|
+
export function useEditorSettings(): EditorSettings | undefined {
|
|
9
|
+
const editContext = useEditContext();
|
|
10
|
+
return editContext?.editorSettings;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Hook to validate item names against Sitecore settings
|
|
15
|
+
* @returns Validation functions for item names
|
|
16
|
+
*/
|
|
17
|
+
export function useItemNameValidation() {
|
|
18
|
+
const editorSettings = useEditorSettings();
|
|
19
|
+
|
|
20
|
+
const validateItemName = (name: string): { isValid: boolean; message?: string } => {
|
|
21
|
+
if (!editorSettings) {
|
|
22
|
+
return { isValid: true }; // No settings loaded, assume valid
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Check invalid characters
|
|
26
|
+
if (editorSettings.invalidItemNameChars) {
|
|
27
|
+
const invalidChars = editorSettings.invalidItemNameChars.replace(/\\/g, '');
|
|
28
|
+
for (const char of invalidChars) {
|
|
29
|
+
if (name.includes(char)) {
|
|
30
|
+
return {
|
|
31
|
+
isValid: false,
|
|
32
|
+
message: `Item name cannot contain the character: ${char}`
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Check name validation regex
|
|
39
|
+
if (editorSettings.itemNameValidation) {
|
|
40
|
+
try {
|
|
41
|
+
const regex = new RegExp(editorSettings.itemNameValidation);
|
|
42
|
+
if (!regex.test(name)) {
|
|
43
|
+
return {
|
|
44
|
+
isValid: false,
|
|
45
|
+
message: `Item name must satisfy the pattern: ${editorSettings.itemNameValidation}`
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.warn('Invalid regex pattern for item name validation:', error);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Check maximum length
|
|
54
|
+
if (editorSettings.maxItemNameLength && name.length > editorSettings.maxItemNameLength) {
|
|
55
|
+
return {
|
|
56
|
+
isValid: false,
|
|
57
|
+
message: `Item name cannot exceed ${editorSettings.maxItemNameLength} characters`
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return { isValid: true };
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
validateItemName,
|
|
66
|
+
settings: editorSettings
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -53,11 +53,11 @@ export function ItemActionsMenu({ isMobile }: { isMobile: boolean }) {
|
|
|
53
53
|
return (
|
|
54
54
|
<DropdownMenu>
|
|
55
55
|
<DropdownMenuTrigger asChild className="cursor-pointer">
|
|
56
|
-
<div className="p-1">
|
|
56
|
+
<div className="p-1" data-testid="item-actions-menu-trigger">
|
|
57
57
|
<VerticalDotsIcon />
|
|
58
58
|
</div>
|
|
59
59
|
</DropdownMenuTrigger>
|
|
60
|
-
<DropdownMenuContent className="w-64" align="start">
|
|
60
|
+
<DropdownMenuContent className="w-64" align="start" data-testid="item-actions-menu-content">
|
|
61
61
|
{itemGroups.map((group, groupIndex) => (
|
|
62
62
|
<div key={`group-${group.id || groupIndex}`}>
|
|
63
63
|
{group.items
|
|
@@ -69,6 +69,7 @@ export function ItemActionsMenu({ isMobile }: { isMobile: boolean }) {
|
|
|
69
69
|
item.command?.({});
|
|
70
70
|
}}
|
|
71
71
|
className="flex items-center gap-2"
|
|
72
|
+
data-testid={`item-action-${item.id}`}
|
|
72
73
|
>
|
|
73
74
|
{typeof item.icon === "string" ? (
|
|
74
75
|
<i className={item.icon} style={{ fontSize: "1rem" }} />
|
|
@@ -185,7 +185,7 @@ export function PageSelector({
|
|
|
185
185
|
data-testid="page-selector-button"
|
|
186
186
|
>
|
|
187
187
|
<div className="flex flex-col">
|
|
188
|
-
<div className="text-sm font-medium">
|
|
188
|
+
<div className="text-sm font-medium" data-testid="item-name">
|
|
189
189
|
{item?.name || "unknown"}
|
|
190
190
|
</div>
|
|
191
191
|
<div className="text-xs text-gray-500">{item?.path}</div>
|
|
@@ -17,12 +17,14 @@ export function UtilityControls() {
|
|
|
17
17
|
icon={<EnterFullScreenIcon className="h-6 w-6 p-1" />}
|
|
18
18
|
label="Fullscreen"
|
|
19
19
|
size="large"
|
|
20
|
+
data-testid="fullscreen-button"
|
|
20
21
|
onClick={() => pageViewContext.setFullscreen(true)}
|
|
21
22
|
/>
|
|
22
23
|
<SimpleIconButton
|
|
23
24
|
icon={<Layers className="h-6 w-6 p-1" strokeWidth={1} />}
|
|
24
25
|
label="Component Navigator"
|
|
25
26
|
size="large"
|
|
27
|
+
data-testid="component-navigator-button"
|
|
26
28
|
selected={editContext.showRightSidebar}
|
|
27
29
|
onClick={() =>
|
|
28
30
|
editContext.setShowRightSidebar(!editContext.showRightSidebar)
|