@alpaca-editor/core 1.0.4095 → 1.0.4097

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/dist/components/ui/button.d.ts +1 -1
  2. package/dist/components/ui/button.js +1 -0
  3. package/dist/components/ui/button.js.map +1 -1
  4. package/dist/components/ui/context-menu.d.ts +2 -2
  5. package/dist/components/ui/context-menu.js +16 -16
  6. package/dist/components/ui/context-menu.js.map +1 -1
  7. package/dist/editor/Editor.js +1 -1
  8. package/dist/editor/Editor.js.map +1 -1
  9. package/dist/editor/FieldActionsOverlay.js +40 -26
  10. package/dist/editor/FieldActionsOverlay.js.map +1 -1
  11. package/dist/editor/FieldListField.js +1 -1
  12. package/dist/editor/FieldListField.js.map +1 -1
  13. package/dist/editor/LinkEditorDialog.js +1 -1
  14. package/dist/editor/ai/AgentTerminal.js +203 -59
  15. package/dist/editor/ai/AgentTerminal.js.map +1 -1
  16. package/dist/editor/ai/Agents.js +23 -9
  17. package/dist/editor/ai/Agents.js.map +1 -1
  18. package/dist/editor/ai/ContextInfoBar.d.ts +11 -0
  19. package/dist/editor/ai/ContextInfoBar.js +357 -0
  20. package/dist/editor/ai/ContextInfoBar.js.map +1 -0
  21. package/dist/editor/ai/DancingDots.js +1 -1
  22. package/dist/editor/ai/DancingDots.js.map +1 -1
  23. package/dist/editor/ai/ToolCallDisplay.js +2 -0
  24. package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
  25. package/dist/editor/client/AboutDialog.js +7 -5
  26. package/dist/editor/client/AboutDialog.js.map +1 -1
  27. package/dist/editor/client/EditorShell.js +7 -2
  28. package/dist/editor/client/EditorShell.js.map +1 -1
  29. package/dist/editor/client/editContext.d.ts +2 -2
  30. package/dist/editor/client/hooks/useSocketMessageHandler.js +3 -0
  31. package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
  32. package/dist/editor/client/itemsRepository.js +6 -2
  33. package/dist/editor/client/itemsRepository.js.map +1 -1
  34. package/dist/editor/control-center/Setup.js +1 -1
  35. package/dist/editor/control-center/Setup.js.map +1 -1
  36. package/dist/editor/control-center/setup-steps/AiSetupStep/EmbeddingsModelSection.d.ts +2 -0
  37. package/dist/editor/control-center/setup-steps/AiSetupStep/EmbeddingsModelSection.js +195 -0
  38. package/dist/editor/control-center/setup-steps/AiSetupStep/EmbeddingsModelSection.js.map +1 -0
  39. package/dist/editor/control-center/setup-steps/AiSetupStep/index.js +22 -0
  40. package/dist/editor/control-center/setup-steps/AiSetupStep/index.js.map +1 -0
  41. package/dist/editor/control-center/setup-steps/AiSetupStep/provider/ProviderSection.d.ts +1 -0
  42. package/dist/editor/control-center/setup-steps/AiSetupStep/provider/ProviderSection.js +233 -0
  43. package/dist/editor/control-center/setup-steps/AiSetupStep/provider/ProviderSection.js.map +1 -0
  44. package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersList.d.ts +15 -0
  45. package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersList.js +14 -0
  46. package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersList.js.map +1 -0
  47. package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.d.ts +1 -0
  48. package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.js +94 -0
  49. package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.js.map +1 -0
  50. package/dist/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.d.ts +1 -0
  51. package/dist/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.js +328 -0
  52. package/dist/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.js.map +1 -0
  53. package/dist/editor/control-center/setup-steps/AiSetupStep/types.d.ts +1 -0
  54. package/dist/editor/control-center/setup-steps/AiSetupStep/types.js +2 -0
  55. package/dist/editor/control-center/setup-steps/AiSetupStep/types.js.map +1 -0
  56. package/dist/editor/control-center/setup-steps/AiSetupStep/utils.d.ts +4 -0
  57. package/dist/editor/control-center/setup-steps/AiSetupStep/utils.js +25 -0
  58. package/dist/editor/control-center/setup-steps/AiSetupStep/utils.js.map +1 -0
  59. package/dist/editor/control-center/setup-steps/IndexSetupStep.js +2 -1
  60. package/dist/editor/control-center/setup-steps/IndexSetupStep.js.map +1 -1
  61. package/dist/editor/field-types/SingleLineText.js.map +1 -1
  62. package/dist/editor/field-types/TreeListEditor.js +102 -16
  63. package/dist/editor/field-types/TreeListEditor.js.map +1 -1
  64. package/dist/editor/field-types/richtext/components/ReactSlate.js +1 -1
  65. package/dist/editor/field-types/richtext/components/SimpleRichTextEditor.js +1 -1
  66. package/dist/editor/menubar/toolbar-sections/EditControls.js +2 -2
  67. package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
  68. package/dist/editor/menubar/toolbar-sections/InsertControls.js +1 -1
  69. package/dist/editor/menubar/toolbar-sections/InsertControls.js.map +1 -1
  70. package/dist/editor/page-editor-chrome/FrameMenu.js +25 -12
  71. package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
  72. package/dist/editor/page-editor-chrome/InlineEditor.js +102 -7
  73. package/dist/editor/page-editor-chrome/InlineEditor.js.map +1 -1
  74. package/dist/editor/page-editor-chrome/SuggestionHighlighting.js +2 -2
  75. package/dist/editor/page-editor-chrome/SuggestionHighlighting.js.map +1 -1
  76. package/dist/editor/reviews/Comments.js +4 -4
  77. package/dist/editor/reviews/Comments.js.map +1 -1
  78. package/dist/editor/services/agentService.d.ts +2 -0
  79. package/dist/editor/services/agentService.js.map +1 -1
  80. package/dist/editor/services/aiService.d.ts +1 -0
  81. package/dist/editor/services/aiService.js.map +1 -1
  82. package/dist/editor/sidebar/ComponentTree.js +18 -6
  83. package/dist/editor/sidebar/ComponentTree.js.map +1 -1
  84. package/dist/editor/sidebar/Insert.js +1 -1
  85. package/dist/editor/ui/Splitter.js +2 -1
  86. package/dist/editor/ui/Splitter.js.map +1 -1
  87. package/dist/editor/utils.js +0 -1
  88. package/dist/editor/utils.js.map +1 -1
  89. package/dist/page-wizard/steps/ContentStep.js +5 -2
  90. package/dist/page-wizard/steps/ContentStep.js.map +1 -1
  91. package/dist/revision.d.ts +2 -2
  92. package/dist/revision.js +2 -2
  93. package/dist/styles.css +15 -18
  94. package/package.json +1 -1
  95. package/src/components/ui/button.tsx +1 -0
  96. package/src/components/ui/context-menu.tsx +44 -20
  97. package/src/editor/Editor.tsx +1 -1
  98. package/src/editor/FieldActionsOverlay.tsx +113 -91
  99. package/src/editor/FieldListField.tsx +4 -1
  100. package/src/editor/LinkEditorDialog.tsx +1 -1
  101. package/src/editor/ai/AgentTerminal.tsx +261 -157
  102. package/src/editor/ai/Agents.tsx +18 -5
  103. package/src/editor/ai/ContextInfoBar.tsx +567 -0
  104. package/src/editor/ai/DancingDots.tsx +1 -1
  105. package/src/editor/ai/ToolCallDisplay.tsx +2 -0
  106. package/src/editor/client/AboutDialog.tsx +22 -17
  107. package/src/editor/client/EditorShell.tsx +8 -2
  108. package/src/editor/client/editContext.ts +2 -2
  109. package/src/editor/client/hooks/useSocketMessageHandler.ts +3 -0
  110. package/src/editor/client/itemsRepository.ts +6 -5
  111. package/src/editor/control-center/Setup.tsx +1 -1
  112. package/src/editor/control-center/setup-steps/AiSetupStep/EmbeddingsModelSection.tsx +296 -0
  113. package/src/editor/control-center/setup-steps/AiSetupStep/index.tsx +38 -0
  114. package/src/editor/control-center/setup-steps/AiSetupStep/provider/ProviderSection.tsx +413 -0
  115. package/src/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersList.tsx +92 -0
  116. package/src/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.tsx +139 -0
  117. package/src/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.tsx +409 -0
  118. package/src/editor/control-center/setup-steps/AiSetupStep/types.ts +1 -0
  119. package/src/editor/control-center/setup-steps/AiSetupStep/utils.ts +43 -0
  120. package/src/editor/control-center/setup-steps/IndexSetupStep.tsx +2 -0
  121. package/src/editor/field-types/SingleLineText.tsx +0 -9
  122. package/src/editor/field-types/TreeListEditor.tsx +115 -16
  123. package/src/editor/field-types/richtext/components/ReactSlate.tsx +1 -1
  124. package/src/editor/field-types/richtext/components/SimpleRichTextEditor.tsx +1 -1
  125. package/src/editor/menubar/toolbar-sections/EditControls.tsx +3 -2
  126. package/src/editor/menubar/toolbar-sections/InsertControls.tsx +1 -1
  127. package/src/editor/page-editor-chrome/FrameMenu.tsx +81 -68
  128. package/src/editor/page-editor-chrome/InlineEditor.tsx +129 -7
  129. package/src/editor/page-editor-chrome/SuggestionHighlighting.tsx +5 -5
  130. package/src/editor/reviews/Comments.tsx +13 -8
  131. package/src/editor/services/agentService.ts +3 -0
  132. package/src/editor/services/aiService.ts +2 -0
  133. package/src/editor/sidebar/ComponentTree.tsx +20 -6
  134. package/src/editor/sidebar/Insert.tsx +1 -1
  135. package/src/editor/ui/Splitter.tsx +2 -2
  136. package/src/editor/utils.ts +0 -1
  137. package/src/page-wizard/steps/ContentStep.tsx +46 -0
  138. package/src/revision.ts +2 -2
  139. package/dist/editor/control-center/setup-steps/AiSetupStep.js +0 -531
  140. package/dist/editor/control-center/setup-steps/AiSetupStep.js.map +0 -1
  141. package/src/editor/control-center/setup-steps/AiSetupStep.tsx +0 -784
  142. /package/dist/editor/control-center/setup-steps/{AiSetupStep.d.ts → AiSetupStep/index.d.ts} +0 -0
@@ -244,6 +244,9 @@ export function useSocketMessageHandler(deps: {
244
244
  if (message.type === "edit-operation") {
245
245
  const op = message.payload as EditOperation;
246
246
  if (op.type === "edit-field") {
247
+ // Ignore our own edit-field operations to avoid redundant updates
248
+ if (op.sessionId && op.sessionId === sessionId) return;
249
+
247
250
  const editFieldOperation = op as any;
248
251
  const field = await itemsRepository.getField({
249
252
  item: {
@@ -68,9 +68,7 @@ const TEMPLATE_SECTION_TEMPLATE_ID = "E269FBB5-3750-427A-9149-7AA950B49301"; //
68
68
  /**
69
69
  * Checks if an item change represents a template-related modification that should trigger cache clearing
70
70
  */
71
- const isTemplateChange = async (
72
- change: ItemChange
73
- ): Promise<boolean> => {
71
+ const isTemplateChange = async (change: ItemChange): Promise<boolean> => {
74
72
  try {
75
73
  // Check if the item is under the /sitecore/templates path
76
74
  if (
@@ -483,6 +481,7 @@ export function useItemsRepository(
483
481
 
484
482
  if (itemsToFetch.length !== 0) {
485
483
  const time = Date.now();
484
+ const startSequence = saveSequenceCounter.current;
486
485
  await fetchItemsFromServer(itemsToFetch);
487
486
 
488
487
  const getFieldValue = (
@@ -515,8 +514,9 @@ export function useItemsRepository(
515
514
  modifiedField.fieldId,
516
515
  );
517
516
 
518
- // If our cached modified value is different from the server, update the cached value
519
- if (isOutdated(modifiedField)) {
517
+ // Avoid clobbering in-flight edits: only sync to server value if not dirty
518
+ if (isOutdated(modifiedField) && !modifiedField.isDirty) {
519
+ console.log("Syncing to server value", modifiedField.fieldId, modifiedField.item.id, modifiedField.item.language, modifiedField.item.version, serverValue);
520
520
  return { ...modifiedField, value: serverValue };
521
521
  }
522
522
 
@@ -532,6 +532,7 @@ export function useItemsRepository(
532
532
  };
533
533
 
534
534
  const isOutdated = (x: ModifiedField) => {
535
+ if (x.saveSequence > startSequence) return false;
535
536
  if (x.timestamp > time) return false;
536
537
  const item = itemsMap.current.get(generateKey(x.item));
537
538
  if (!item) return true;
@@ -16,7 +16,7 @@ export function Setup() {
16
16
  return (
17
17
  <div className="h-full overflow-auto p-4">
18
18
  <div className="mb-4 text-lg font-semibold text-gray-800">Setup</div>
19
- <div className="grid grid-cols-1 gap-4">
19
+ <div className="grid grid-cols-1 gap-4 max-w-xl">
20
20
  {steps.map((Step, idx) => (
21
21
  <Step key={idx} />
22
22
  ))}
@@ -0,0 +1,296 @@
1
+ import React from "react";
2
+ import { Button } from "../../../../components/ui/button";
3
+ import { AlertCircle, CheckCircle, RefreshCw } from "lucide-react";
4
+ import { useEditContext } from "../../../client/editContext";
5
+ import type { ItemDescriptor } from "../../../pageModel";
6
+ import { getChildren } from "../../../services/contentService";
7
+ import type { ItemTreeNodeData } from "../../../services/contentService";
8
+ import { findByName, resolvePathUnderContent } from "./utils";
9
+ import type { StepState } from "./types";
10
+
11
+ // Sitecore IDs and constants used by AI models/endpoints
12
+ const AI_MODELS_CONTAINER_TEMPLATE_ID = "e5f6a7b8-9c0d-1e2f-3a4b-5c6d7e8f9a0b";
13
+ const AI_MODEL_TEMPLATE_ID = "d2e3f4a5-4b5c-5c6d-bf0b-3e4f5a6b7c8e";
14
+ const MODEL_NAME_FIELD_ID = "f1e2d3c4-5b6a-7980-8e9f-1a2b3c4d5e6f";
15
+ const MODEL_ENDPOINT_FIELD_ID = "cbade708-ce95-4f54-81b7-308ea61a76a6";
16
+ const EDITOR_SETTINGS_EMBEDDINGS_MODEL_FIELD_ID = "08d0579d-3bf2-4777-981f-3f0856da948e"; // Embeddings Model (Droptree)
17
+
18
+ const DEFAULT_EMBEDDINGS_MODEL_NAME = "text-embedding-3-large";
19
+
20
+ export function EmbeddingsModelSection() {
21
+ const editContext = useEditContext();
22
+ const [state, setState] = React.useState<StepState>("checking");
23
+ const [error, setError] = React.useState<string | null>(null);
24
+ const [isBusy, setIsBusy] = React.useState(false);
25
+ const [currentModelName, setCurrentModelName] = React.useState<string | null>(
26
+ null,
27
+ );
28
+
29
+ const userLang = editContext?.contentEditorItem?.language || "en";
30
+
31
+ const statusIcon = React.useCallback((s: StepState) => {
32
+ if (s === "success")
33
+ return <CheckCircle className="h-4 w-4 text-green-600" strokeWidth={1} />;
34
+ if (s === "error")
35
+ return <AlertCircle className="h-4 w-4 text-red-600" strokeWidth={1} />;
36
+ return (
37
+ <RefreshCw className="h-4 w-4 animate-spin text-amber-600" strokeWidth={1} />
38
+ );
39
+ }, []);
40
+
41
+ const checkStatus = React.useCallback(async () => {
42
+ try {
43
+ setState("checking");
44
+ setError(null);
45
+ setCurrentModelName(null);
46
+
47
+ const settingsItem = await resolvePathUnderContent(
48
+ editContext?.sessionId,
49
+ userLang,
50
+ ["Settings", "Editor"],
51
+ );
52
+ if (!settingsItem) {
53
+ setState("error");
54
+ setError("Editor settings item not found. Create it first.");
55
+ return;
56
+ }
57
+
58
+ // Read the Embeddings Model droptree field
59
+ const field = await editContext!.itemsRepository.getField({
60
+ item: { id: settingsItem.id, language: userLang, version: 0 },
61
+ fieldId: EDITOR_SETTINGS_EMBEDDINGS_MODEL_FIELD_ID,
62
+ });
63
+
64
+ const raw = (field?.rawValue || "").trim();
65
+ if (!raw) {
66
+ setState("error");
67
+ return;
68
+ }
69
+
70
+ // Normalize ID braces and load the model item to get its display name
71
+ const modelId = raw.replace(/^[{(]+|[})]+$/g, "");
72
+ const model = await editContext!.itemsRepository.getItem({
73
+ id: `{${modelId}}`,
74
+ language: userLang,
75
+ version: 0,
76
+ });
77
+ if (!model) {
78
+ setState("error");
79
+ setError(
80
+ "Embeddings Model points to a missing item. Please fix to re-create and assign.",
81
+ );
82
+ return;
83
+ }
84
+
85
+ setCurrentModelName(model.name);
86
+ setState("success");
87
+ } catch (e: any) {
88
+ setState("error");
89
+ setError(e?.message || "Failed to check embeddings model");
90
+ }
91
+ }, [editContext, userLang]);
92
+
93
+ React.useEffect(() => {
94
+ checkStatus();
95
+ }, [checkStatus]);
96
+
97
+ const ensureEmbeddingsModel = React.useCallback(async () => {
98
+ if (!editContext) return;
99
+ try {
100
+ setIsBusy(true);
101
+ setError(null);
102
+
103
+ const settingsItem = await resolvePathUnderContent(
104
+ editContext.sessionId,
105
+ userLang,
106
+ ["Settings", "Editor"],
107
+ );
108
+ if (!settingsItem)
109
+ throw new Error("Editor settings item not found. Create it first.");
110
+
111
+ // Load children under /Settings/Editor
112
+ const editorChildren = (await getChildren(
113
+ settingsItem.id,
114
+ editContext.sessionId!,
115
+ [],
116
+ false,
117
+ userLang,
118
+ "path",
119
+ )) as unknown as ItemTreeNodeData[];
120
+
121
+ // Find endpoints container and choose OpenAI or Azure OpenAI endpoint
122
+ const endpointsContainer = findByName(editorChildren, "Ai Endpoints");
123
+ if (!endpointsContainer) {
124
+ throw new Error(
125
+ "No AI endpoints found. Please add an OpenAI or Azure OpenAI endpoint first.",
126
+ );
127
+ }
128
+ const endpointChildren = (await getChildren(
129
+ endpointsContainer.id,
130
+ editContext.sessionId!,
131
+ [],
132
+ false,
133
+ userLang,
134
+ "path",
135
+ )) as unknown as ItemTreeNodeData[];
136
+ const openAi = findByName(endpointChildren, "OpenAI");
137
+ const azureOpenAi = findByName(endpointChildren, "Azure OpenAI");
138
+ const selectedEndpoint = openAi || azureOpenAi;
139
+ if (!selectedEndpoint) {
140
+ throw new Error(
141
+ "No OpenAI or Azure OpenAI endpoint exists. Add one in 'Provider' section.",
142
+ );
143
+ }
144
+
145
+ // Ensure Ai Models container exists
146
+ let modelsContainer = findByName(editorChildren, "Ai Models");
147
+ if (!modelsContainer) {
148
+ const createdModels = await editContext.operations.createItem(
149
+ settingsItem,
150
+ AI_MODELS_CONTAINER_TEMPLATE_ID,
151
+ "Ai Models",
152
+ );
153
+ if (!createdModels)
154
+ throw new Error("Failed to create 'Ai Models' container");
155
+ modelsContainer = {
156
+ id: createdModels.id,
157
+ name: "Ai Models",
158
+ displayName: "Ai Models",
159
+ thumbUrl: "",
160
+ previewUrl: "",
161
+ templateId: "",
162
+ icon: "",
163
+ hasChildren: false,
164
+ isComponent: false,
165
+ language: userLang,
166
+ version: 0,
167
+ hasLayout: false,
168
+ } as unknown as ItemTreeNodeData;
169
+ }
170
+
171
+ // Find or create the "text-embedding-3-large" model
172
+ const modelChildren = (await getChildren(
173
+ modelsContainer.id,
174
+ editContext.sessionId!,
175
+ [],
176
+ false,
177
+ userLang,
178
+ "path",
179
+ )) as unknown as ItemTreeNodeData[];
180
+
181
+ let modelNode = findByName(modelChildren, DEFAULT_EMBEDDINGS_MODEL_NAME);
182
+ let modelDescriptor: ItemDescriptor | null = null;
183
+
184
+ if (modelNode) {
185
+ modelDescriptor = {
186
+ id: modelNode.id,
187
+ language: userLang,
188
+ version: 0,
189
+ } as ItemDescriptor;
190
+ } else {
191
+ const createdModel = await editContext.operations.createItem(
192
+ {
193
+ id: modelsContainer.id,
194
+ language: userLang,
195
+ version: 0,
196
+ } as ItemDescriptor,
197
+ AI_MODEL_TEMPLATE_ID,
198
+ DEFAULT_EMBEDDINGS_MODEL_NAME,
199
+ );
200
+ if (!createdModel)
201
+ throw new Error(
202
+ `Failed to create model '${DEFAULT_EMBEDDINGS_MODEL_NAME}'`,
203
+ );
204
+ modelDescriptor = createdModel;
205
+
206
+ // Set Model Name field
207
+ await editContext.operations.editField({
208
+ field: {
209
+ fieldId: MODEL_NAME_FIELD_ID,
210
+ item: {
211
+ id: modelDescriptor.id,
212
+ language: userLang,
213
+ version: 0,
214
+ },
215
+ },
216
+ value: DEFAULT_EMBEDDINGS_MODEL_NAME,
217
+ rawValue: DEFAULT_EMBEDDINGS_MODEL_NAME,
218
+ refresh: "delayed",
219
+ });
220
+ }
221
+
222
+ // Link model to the selected endpoint
223
+ const endpointGuid = `{${(selectedEndpoint.id as string)
224
+ .replace(/[{}()]/g, "")
225
+ .toUpperCase()}}`;
226
+ await editContext.operations.editField({
227
+ field: {
228
+ fieldId: MODEL_ENDPOINT_FIELD_ID,
229
+ item: { id: modelDescriptor!.id, language: userLang, version: 0 },
230
+ },
231
+ rawValue: endpointGuid,
232
+ refresh: "delayed",
233
+ });
234
+
235
+ // Assign model to Editor settings' Embeddings Model field
236
+ const modelGuid = `{${(modelDescriptor!.id as string)
237
+ .replace(/[{}()]/g, "")
238
+ .toUpperCase()}}`;
239
+ await editContext.operations.editField({
240
+ field: {
241
+ fieldId: EDITOR_SETTINGS_EMBEDDINGS_MODEL_FIELD_ID,
242
+ item: { id: settingsItem.id, language: userLang, version: 0 },
243
+ },
244
+ rawValue: modelGuid,
245
+ refresh: "immediate",
246
+ });
247
+
248
+ editContext.requestRefresh?.("immediate");
249
+ editContext.showToast?.(
250
+ `Embeddings model set to '${DEFAULT_EMBEDDINGS_MODEL_NAME}'`,
251
+ );
252
+ await checkStatus();
253
+ } catch (e: any) {
254
+ setError(e?.message || "Failed to ensure embeddings model");
255
+ setState("error");
256
+ } finally {
257
+ setIsBusy(false);
258
+ }
259
+ }, [editContext, userLang, checkStatus]);
260
+
261
+ return (
262
+ <div className="mt-3">
263
+ <div className="flex items-center justify-between gap-2">
264
+ <div className="flex items-center gap-2">
265
+ {statusIcon(state)}
266
+ <span className="text-sm text-gray-700">
267
+ {state === "success"
268
+ ? `Embeddings model: ${currentModelName}`
269
+ : state === "error"
270
+ ? "Embeddings model not assigned"
271
+ : "Checking embeddings model..."}
272
+ </span>
273
+ </div>
274
+ {state !== "success" && (
275
+ <Button size="sm" onClick={ensureEmbeddingsModel} disabled={isBusy}>
276
+ {isBusy ? (
277
+ <RefreshCw strokeWidth={1} className="h-4 w-4 animate-spin" />
278
+ ) : (
279
+ <CheckCircle strokeWidth={1} className="h-4 w-4" />
280
+ )}
281
+ Fix
282
+ </Button>
283
+ )}
284
+ </div>
285
+ {error && (
286
+ <div className="mt-2 rounded border border-yellow-200 bg-yellow-50 p-2 text-xs whitespace-pre-wrap text-yellow-800">
287
+ {error}
288
+ </div>
289
+ )}
290
+ </div>
291
+ );
292
+ }
293
+
294
+ export default EmbeddingsModelSection;
295
+
296
+
@@ -0,0 +1,38 @@
1
+ import { useState } from "react";
2
+ import { Card } from "../../../../components/ui/card";
3
+ import { useEditContext } from "../../../client/editContext";
4
+ import { SparklesIcon } from "lucide-react";
5
+ import { RequiredContainersSection } from "./required-containers/RequiredContainersSection";
6
+ import { ProviderSection } from "./provider/ProviderSection";
7
+ import { GenerateToolsSection } from "./tools/GenerateToolsSection";
8
+ import type { StepState } from "./types";
9
+
10
+ export function AiSetupStep() {
11
+ const editContext = useEditContext();
12
+ const [aiState, setAiState] = useState<StepState>("idle");
13
+ const userLang = editContext?.contentEditorItem?.language || "en";
14
+
15
+ // IDs for tool templates/fields
16
+ const AI_TOOLS_TEMPLATE_ID = "7e3f9d9d-8e84-4e57-b12e-6e9c99d4fd11"; // container
17
+ const AI_TOOL_TEMPLATE_ID = "a2e8a0d9-7b1f-4d1a-a2d8-8f3ea6d6f0b1"; // item template
18
+ const FIELD_FUNCTION_NAME_ID = "3d9f2b1a-9d44-4d26-8a1b-92f0fb4d3a2c";
19
+ const FIELD_LABEL_ID = "5a19f5a5-1d21-4b6a-9f83-6f3fc0b34b2e";
20
+ const FIELD_DESCRIPTION_ID = "6b2e4b8e-8a9f-4d8f-9f6e-1a2b3c4d5e6f";
21
+
22
+ return (
23
+ <Card
24
+ className="w-full"
25
+ icon={<SparklesIcon strokeWidth={1} className="h-5 w-5" />}
26
+ title="AI setup"
27
+ description="Add an AI endpoint (OpenAI, Azure OpenAI, or OpenRouter)."
28
+ >
29
+ <RequiredContainersSection />
30
+
31
+ <ProviderSection />
32
+
33
+ <GenerateToolsSection />
34
+ </Card>
35
+ );
36
+ }
37
+
38
+ export default AiSetupStep;