@alpaca-editor/core 1.0.4096 → 1.0.4098
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/button.d.ts +1 -1
- package/dist/components/ui/button.js +1 -0
- package/dist/components/ui/button.js.map +1 -1
- package/dist/components/ui/context-menu.d.ts +2 -2
- package/dist/components/ui/context-menu.js +16 -16
- package/dist/components/ui/context-menu.js.map +1 -1
- package/dist/editor/Editor.js +1 -1
- package/dist/editor/Editor.js.map +1 -1
- package/dist/editor/FieldActionsOverlay.js +40 -26
- package/dist/editor/FieldActionsOverlay.js.map +1 -1
- package/dist/editor/FieldListField.js +1 -1
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/LinkEditorDialog.js +1 -1
- package/dist/editor/ai/AgentTerminal.js +56 -5
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/Agents.js +23 -9
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/ContextInfoBar.js +15 -5
- package/dist/editor/ai/ContextInfoBar.js.map +1 -1
- package/dist/editor/ai/ToolCallDisplay.js +2 -0
- package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
- package/dist/editor/client/AboutDialog.js +7 -5
- package/dist/editor/client/AboutDialog.js.map +1 -1
- package/dist/editor/client/EditorShell.js +7 -2
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +2 -2
- package/dist/editor/client/hooks/useSocketMessageHandler.js +5 -0
- package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
- package/dist/editor/client/itemsRepository.js +1 -0
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/control-center/Setup.js +1 -1
- package/dist/editor/control-center/Setup.js.map +1 -1
- package/dist/editor/control-center/setup-steps/AiSetupStep/EmbeddingsModelSection.d.ts +2 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/EmbeddingsModelSection.js +195 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/EmbeddingsModelSection.js.map +1 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/index.d.ts +0 -1
- package/dist/editor/control-center/setup-steps/AiSetupStep/index.js +3 -506
- package/dist/editor/control-center/setup-steps/AiSetupStep/index.js.map +1 -1
- package/dist/editor/control-center/setup-steps/AiSetupStep/provider/ProviderSection.d.ts +1 -15
- package/dist/editor/control-center/setup-steps/AiSetupStep/provider/ProviderSection.js +226 -3
- package/dist/editor/control-center/setup-steps/AiSetupStep/provider/ProviderSection.js.map +1 -1
- package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersList.d.ts +1 -1
- package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.d.ts +1 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.js +94 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.js.map +1 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.d.ts +1 -7
- package/dist/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.js +323 -3
- package/dist/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.js.map +1 -1
- package/dist/editor/control-center/setup-steps/AiSetupStep/types.d.ts +1 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/types.js +2 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/types.js.map +1 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/utils.d.ts +4 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/utils.js +25 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/utils.js.map +1 -0
- package/dist/editor/control-center/setup-steps/IndexSetupStep.js +2 -1
- package/dist/editor/control-center/setup-steps/IndexSetupStep.js.map +1 -1
- package/dist/editor/field-types/SingleLineText.js.map +1 -1
- package/dist/editor/field-types/TreeListEditor.js +102 -16
- package/dist/editor/field-types/TreeListEditor.js.map +1 -1
- package/dist/editor/field-types/richtext/components/ReactSlate.js +1 -1
- package/dist/editor/field-types/richtext/components/SimpleRichTextEditor.js +1 -1
- package/dist/editor/menubar/toolbar-sections/EditControls.js +2 -2
- 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/page-editor-chrome/FrameMenu.js +25 -12
- package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
- package/dist/editor/page-editor-chrome/SuggestionHighlighting.js +2 -2
- package/dist/editor/page-editor-chrome/SuggestionHighlighting.js.map +1 -1
- package/dist/editor/page-viewer/PageViewer.js +2 -1
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/reviews/Comments.js +4 -4
- package/dist/editor/reviews/Comments.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +2 -0
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +1 -0
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/sidebar/ComponentTree.js +18 -6
- package/dist/editor/sidebar/ComponentTree.js.map +1 -1
- package/dist/editor/sidebar/Insert.js +1 -1
- package/dist/editor/ui/Splitter.js +2 -1
- package/dist/editor/ui/Splitter.js.map +1 -1
- package/dist/editor/utils.js +0 -1
- package/dist/editor/utils.js.map +1 -1
- package/dist/page-wizard/steps/MetaDataStep.js +43 -41
- package/dist/page-wizard/steps/MetaDataStep.js.map +1 -1
- package/dist/page-wizard/steps/SelectStep.js +19 -4
- package/dist/page-wizard/steps/SelectStep.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/styles.css +15 -18
- package/package.json +1 -1
- package/src/components/ui/button.tsx +1 -0
- package/src/components/ui/context-menu.tsx +44 -20
- package/src/editor/Editor.tsx +1 -1
- package/src/editor/FieldActionsOverlay.tsx +113 -91
- package/src/editor/FieldListField.tsx +4 -1
- package/src/editor/LinkEditorDialog.tsx +1 -1
- package/src/editor/ai/AgentTerminal.tsx +84 -4
- package/src/editor/ai/Agents.tsx +18 -5
- package/src/editor/ai/ContextInfoBar.tsx +22 -6
- package/src/editor/ai/ToolCallDisplay.tsx +2 -0
- package/src/editor/client/AboutDialog.tsx +22 -17
- package/src/editor/client/EditorShell.tsx +8 -2
- package/src/editor/client/editContext.ts +2 -2
- package/src/editor/client/hooks/useSocketMessageHandler.ts +8 -0
- package/src/editor/client/itemsRepository.ts +1 -0
- package/src/editor/control-center/Setup.tsx +1 -1
- package/src/editor/control-center/setup-steps/AiSetupStep/EmbeddingsModelSection.tsx +296 -0
- package/src/editor/control-center/setup-steps/AiSetupStep/index.tsx +7 -660
- package/src/editor/control-center/setup-steps/AiSetupStep/provider/ProviderSection.tsx +363 -24
- package/src/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersList.tsx +1 -1
- package/src/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.tsx +139 -0
- package/src/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.tsx +385 -8
- package/src/editor/control-center/setup-steps/AiSetupStep/types.ts +1 -0
- package/src/editor/control-center/setup-steps/AiSetupStep/utils.ts +43 -0
- package/src/editor/control-center/setup-steps/IndexSetupStep.tsx +2 -0
- package/src/editor/field-types/SingleLineText.tsx +0 -9
- package/src/editor/field-types/TreeListEditor.tsx +115 -16
- package/src/editor/field-types/richtext/components/ReactSlate.tsx +1 -1
- package/src/editor/field-types/richtext/components/SimpleRichTextEditor.tsx +1 -1
- package/src/editor/menubar/toolbar-sections/EditControls.tsx +2 -2
- package/src/editor/menubar/toolbar-sections/InsertControls.tsx +1 -1
- package/src/editor/page-editor-chrome/FrameMenu.tsx +81 -68
- package/src/editor/page-editor-chrome/SuggestionHighlighting.tsx +5 -5
- package/src/editor/page-viewer/PageViewer.tsx +3 -2
- package/src/editor/reviews/Comments.tsx +13 -8
- package/src/editor/services/agentService.ts +3 -0
- package/src/editor/services/aiService.ts +2 -0
- package/src/editor/sidebar/ComponentTree.tsx +20 -6
- package/src/editor/sidebar/Insert.tsx +1 -1
- package/src/editor/ui/Splitter.tsx +2 -2
- package/src/editor/utils.ts +0 -1
- package/src/page-wizard/steps/MetaDataStep.tsx +61 -50
- package/src/page-wizard/steps/SelectStep.tsx +37 -4
- package/src/revision.ts +2 -2
|
@@ -3,32 +3,318 @@ import { Select } from "../../../../../components/ui/select";
|
|
|
3
3
|
import { Input } from "../../../../../components/ui/input";
|
|
4
4
|
import { Button } from "../../../../../components/ui/button";
|
|
5
5
|
import { CheckCircle, RefreshCw } from "lucide-react";
|
|
6
|
+
import { useEditContext } from "../../../../client/editContext";
|
|
7
|
+
import type { ItemDescriptor } from "../../../../pageModel";
|
|
8
|
+
import { getChildren } from "../../../../services/contentService";
|
|
9
|
+
import type { ItemTreeNodeData } from "../../../../services/contentService";
|
|
10
|
+
import { findByName, resolvePathUnderContent } from "../utils";
|
|
6
11
|
|
|
7
12
|
interface ProviderOption {
|
|
8
13
|
value: string;
|
|
9
14
|
label: string;
|
|
10
15
|
}
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
export function ProviderSection() {
|
|
18
|
+
const editContext = useEditContext();
|
|
19
|
+
const [aiProvider, setAiProvider] = React.useState<string>("");
|
|
20
|
+
const [apiKey, setApiKey] = React.useState<string>("");
|
|
21
|
+
const [aiBusy, setAiBusy] = React.useState(false);
|
|
22
|
+
const [aiError, setAiError] = React.useState<string | null>(null);
|
|
23
|
+
const [endpoints, setEndpoints] = React.useState<ItemTreeNodeData[]>([]);
|
|
24
|
+
const [loadingEndpoints, setLoadingEndpoints] = React.useState(false);
|
|
25
|
+
const [endpointsError, setEndpointsError] = React.useState<string | null>(
|
|
26
|
+
null,
|
|
27
|
+
);
|
|
28
|
+
const lastLoadAtRef = React.useRef<number>(0);
|
|
29
|
+
const isLoadingRef = React.useRef<boolean>(false);
|
|
30
|
+
|
|
31
|
+
const userLang = editContext?.contentEditorItem?.language || "en";
|
|
32
|
+
const sessionId = editContext?.sessionId;
|
|
33
|
+
|
|
34
|
+
const aiOptions = React.useMemo<ProviderOption[]>(
|
|
35
|
+
() => [
|
|
36
|
+
{ value: "openai", label: "OpenAI" },
|
|
37
|
+
{ value: "azure-openai", label: "Azure OpenAI" },
|
|
38
|
+
{ value: "openrouter", label: "OpenRouter" },
|
|
39
|
+
],
|
|
40
|
+
[],
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const loadEndpoints = React.useCallback(
|
|
44
|
+
async (options?: { force?: boolean }) => {
|
|
45
|
+
if (!sessionId) return;
|
|
46
|
+
try {
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
const MIN_INTERVAL_MS = 2000;
|
|
49
|
+
if (!options?.force) {
|
|
50
|
+
if (isLoadingRef.current) return;
|
|
51
|
+
if (now - lastLoadAtRef.current < MIN_INTERVAL_MS) return;
|
|
52
|
+
}
|
|
53
|
+
isLoadingRef.current = true;
|
|
54
|
+
lastLoadAtRef.current = now;
|
|
55
|
+
setLoadingEndpoints(true);
|
|
56
|
+
setEndpointsError(null);
|
|
57
|
+
|
|
58
|
+
const settingsItem = await resolvePathUnderContent(
|
|
59
|
+
sessionId,
|
|
60
|
+
userLang,
|
|
61
|
+
["Settings", "Editor"],
|
|
62
|
+
);
|
|
63
|
+
if (!settingsItem) {
|
|
64
|
+
setEndpoints([]);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const children = (await getChildren(
|
|
69
|
+
settingsItem.id,
|
|
70
|
+
sessionId!,
|
|
71
|
+
[],
|
|
72
|
+
false,
|
|
73
|
+
userLang,
|
|
74
|
+
"path",
|
|
75
|
+
)) as unknown as ItemTreeNodeData[];
|
|
76
|
+
const aiEndpointsParent = findByName(children, "Ai Endpoints");
|
|
77
|
+
if (!aiEndpointsParent) {
|
|
78
|
+
setEndpoints([]);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const epChildren = (await getChildren(
|
|
83
|
+
aiEndpointsParent.id,
|
|
84
|
+
sessionId!,
|
|
85
|
+
[],
|
|
86
|
+
false,
|
|
87
|
+
userLang,
|
|
88
|
+
"path",
|
|
89
|
+
)) as unknown as ItemTreeNodeData[];
|
|
90
|
+
setEndpoints(epChildren || []);
|
|
91
|
+
} catch (e: any) {
|
|
92
|
+
setEndpointsError(e?.message || "Failed to load AI endpoints");
|
|
93
|
+
} finally {
|
|
94
|
+
isLoadingRef.current = false;
|
|
95
|
+
setLoadingEndpoints(false);
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
[sessionId, userLang],
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const createAiEndpoint = React.useCallback(async () => {
|
|
102
|
+
if (!aiProvider || !editContext) return;
|
|
103
|
+
try {
|
|
104
|
+
setAiBusy(true);
|
|
105
|
+
setAiError(null);
|
|
106
|
+
|
|
107
|
+
const settingsItem = await resolvePathUnderContent(
|
|
108
|
+
editContext.sessionId,
|
|
109
|
+
userLang,
|
|
110
|
+
["Settings", "Editor"],
|
|
111
|
+
);
|
|
112
|
+
if (!settingsItem)
|
|
113
|
+
throw new Error("Editor settings item not found. Create it first.");
|
|
114
|
+
|
|
115
|
+
const children = (await getChildren(
|
|
116
|
+
settingsItem.id,
|
|
117
|
+
editContext.sessionId!,
|
|
118
|
+
[],
|
|
119
|
+
false,
|
|
120
|
+
userLang,
|
|
121
|
+
"path",
|
|
122
|
+
)) as unknown as ItemTreeNodeData[];
|
|
123
|
+
let aiEndpointsParent = findByName(children, "Ai Endpoints");
|
|
124
|
+
if (!aiEndpointsParent) {
|
|
125
|
+
const aiEndpointsTemplateId = "4592f2e0-06e4-470e-9820-7fd4ea148e00";
|
|
126
|
+
const createdContainer = await editContext.operations.createItem(
|
|
127
|
+
settingsItem,
|
|
128
|
+
aiEndpointsTemplateId,
|
|
129
|
+
"Ai Endpoints",
|
|
130
|
+
);
|
|
131
|
+
if (!createdContainer)
|
|
132
|
+
throw new Error("Failed to create 'Ai Endpoints' container");
|
|
133
|
+
aiEndpointsParent = {
|
|
134
|
+
id: createdContainer.id,
|
|
135
|
+
name: "Ai Endpoints",
|
|
136
|
+
thumbUrl: "",
|
|
137
|
+
previewUrl: "",
|
|
138
|
+
templateId: "",
|
|
139
|
+
icon: "",
|
|
140
|
+
hasChildren: false,
|
|
141
|
+
isComponent: false,
|
|
142
|
+
displayName: "Ai Endpoints",
|
|
143
|
+
language: userLang,
|
|
144
|
+
version: 0,
|
|
145
|
+
hasLayout: false,
|
|
146
|
+
} as unknown as ItemTreeNodeData;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const endpointTemplateId = "a6358b92-a9ea-4dd8-b4e1-7d6d3a3b9c40";
|
|
150
|
+
const endpointName =
|
|
151
|
+
aiProvider === "openai"
|
|
152
|
+
? "OpenAI"
|
|
153
|
+
: aiProvider === "azure-openai"
|
|
154
|
+
? "Azure OpenAI"
|
|
155
|
+
: "Open Router";
|
|
156
|
+
|
|
157
|
+
// Find or create endpoint
|
|
158
|
+
let endpointItem: ItemDescriptor | null = null;
|
|
159
|
+
const epChildren = (await getChildren(
|
|
160
|
+
aiEndpointsParent!.id,
|
|
161
|
+
editContext.sessionId!,
|
|
162
|
+
[],
|
|
163
|
+
false,
|
|
164
|
+
userLang,
|
|
165
|
+
"path",
|
|
166
|
+
)) as unknown as ItemTreeNodeData[];
|
|
167
|
+
const existingEndpoint = findByName(epChildren, endpointName);
|
|
168
|
+
if (existingEndpoint) {
|
|
169
|
+
endpointItem = {
|
|
170
|
+
id: existingEndpoint.id,
|
|
171
|
+
language: userLang,
|
|
172
|
+
version: 0,
|
|
173
|
+
} as ItemDescriptor;
|
|
174
|
+
} else {
|
|
175
|
+
const created = await editContext.operations.createItem(
|
|
176
|
+
{
|
|
177
|
+
id: aiEndpointsParent!.id,
|
|
178
|
+
language: userLang,
|
|
179
|
+
version: 0,
|
|
180
|
+
} as ItemDescriptor,
|
|
181
|
+
endpointTemplateId,
|
|
182
|
+
endpointName,
|
|
183
|
+
);
|
|
184
|
+
if (!created) throw new Error("Failed to create AI endpoint");
|
|
185
|
+
endpointItem = created;
|
|
186
|
+
}
|
|
21
187
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
188
|
+
// Set API key if provided
|
|
189
|
+
if (apiKey && endpointItem) {
|
|
190
|
+
const apiKeyFieldId = "db07ae53-459a-4314-89b3-ab2721df2b4f";
|
|
191
|
+
await editContext.operations.editField({
|
|
192
|
+
field: {
|
|
193
|
+
fieldId: apiKeyFieldId,
|
|
194
|
+
item: {
|
|
195
|
+
id: endpointItem.id,
|
|
196
|
+
language: userLang,
|
|
197
|
+
version: 0,
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
value: apiKey,
|
|
201
|
+
rawValue: apiKey,
|
|
202
|
+
refresh: "delayed",
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Ensure Ai Models container exists
|
|
207
|
+
let aiModelsParent = findByName(children, "Ai Models");
|
|
208
|
+
if (!aiModelsParent) {
|
|
209
|
+
const aiModelsTemplateId = "e5f6a7b8-9c0d-1e2f-3a4b-5c6d7e8f9a0b";
|
|
210
|
+
const createdModels = await editContext.operations.createItem(
|
|
211
|
+
settingsItem,
|
|
212
|
+
aiModelsTemplateId,
|
|
213
|
+
"Ai Models",
|
|
214
|
+
);
|
|
215
|
+
if (!createdModels)
|
|
216
|
+
throw new Error("Failed to create 'Ai Models' container");
|
|
217
|
+
aiModelsParent = {
|
|
218
|
+
id: createdModels.id,
|
|
219
|
+
name: "Ai Models",
|
|
220
|
+
thumbUrl: "",
|
|
221
|
+
previewUrl: "",
|
|
222
|
+
templateId: "",
|
|
223
|
+
icon: "",
|
|
224
|
+
hasChildren: false,
|
|
225
|
+
isComponent: false,
|
|
226
|
+
displayName: "Ai Models",
|
|
227
|
+
language: userLang,
|
|
228
|
+
version: 0,
|
|
229
|
+
hasLayout: false,
|
|
230
|
+
} as unknown as ItemTreeNodeData;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Create default models if missing and link to endpoint
|
|
234
|
+
const modelsToEnsure = ["gpt-5", "gpt5-high", "image-1"];
|
|
235
|
+
const modelTemplateId = "d2e3f4a5-4b5c-5c6d-bf0b-3e4f5a6b7c8e";
|
|
236
|
+
const modelNameFieldId = "f1e2d3c4-5b6a-7980-8e9f-1a2b3c4d5e6f";
|
|
237
|
+
const modelEndpointFieldId = "cbade708-ce95-4f54-81b7-308ea61a76a6";
|
|
238
|
+
|
|
239
|
+
const modelChildren = (await getChildren(
|
|
240
|
+
aiModelsParent.id,
|
|
241
|
+
editContext.sessionId!,
|
|
242
|
+
[],
|
|
243
|
+
false,
|
|
244
|
+
userLang,
|
|
245
|
+
"path",
|
|
246
|
+
)) as unknown as ItemTreeNodeData[];
|
|
247
|
+
|
|
248
|
+
for (const modelName of modelsToEnsure) {
|
|
249
|
+
let modelNode = findByName(modelChildren, modelName);
|
|
250
|
+
if (!modelNode) {
|
|
251
|
+
const createdModel = await editContext.operations.createItem(
|
|
252
|
+
{
|
|
253
|
+
id: aiModelsParent.id,
|
|
254
|
+
language: userLang,
|
|
255
|
+
version: 0,
|
|
256
|
+
} as ItemDescriptor,
|
|
257
|
+
modelTemplateId,
|
|
258
|
+
modelName,
|
|
259
|
+
);
|
|
260
|
+
if (!createdModel)
|
|
261
|
+
throw new Error(`Failed to create model '${modelName}'`);
|
|
262
|
+
modelNode = {
|
|
263
|
+
id: createdModel.id,
|
|
264
|
+
name: modelName,
|
|
265
|
+
displayName: modelName,
|
|
266
|
+
thumbUrl: "",
|
|
267
|
+
previewUrl: "",
|
|
268
|
+
templateId: "",
|
|
269
|
+
icon: "",
|
|
270
|
+
hasChildren: false,
|
|
271
|
+
isComponent: false,
|
|
272
|
+
language: userLang,
|
|
273
|
+
version: 0,
|
|
274
|
+
hasLayout: false,
|
|
275
|
+
} as unknown as ItemTreeNodeData;
|
|
276
|
+
|
|
277
|
+
await editContext.operations.editField({
|
|
278
|
+
field: {
|
|
279
|
+
fieldId: modelNameFieldId,
|
|
280
|
+
item: { id: modelNode.id, language: userLang, version: 0 },
|
|
281
|
+
},
|
|
282
|
+
value: modelName,
|
|
283
|
+
rawValue: modelName,
|
|
284
|
+
refresh: "delayed",
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
const endpointGuid = `{${(endpointItem!.id as string)
|
|
288
|
+
.replace(/[{}()]/g, "")
|
|
289
|
+
.toUpperCase()}}`;
|
|
290
|
+
await editContext.operations.editField({
|
|
291
|
+
field: {
|
|
292
|
+
fieldId: modelEndpointFieldId,
|
|
293
|
+
item: { id: modelNode.id, language: userLang, version: 0 },
|
|
294
|
+
},
|
|
295
|
+
rawValue: endpointGuid,
|
|
296
|
+
refresh: "delayed",
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
editContext.requestRefresh?.("immediate");
|
|
302
|
+
editContext.showToast?.(
|
|
303
|
+
`Added '${endpointName}' AI endpoint successfully`,
|
|
304
|
+
);
|
|
305
|
+
await loadEndpoints({ force: true });
|
|
306
|
+
setAiProvider("");
|
|
307
|
+
setApiKey("");
|
|
308
|
+
} catch (e: any) {
|
|
309
|
+
setAiError(e?.message || "Failed to add AI endpoint");
|
|
310
|
+
} finally {
|
|
311
|
+
setAiBusy(false);
|
|
312
|
+
}
|
|
313
|
+
}, [aiProvider, editContext, userLang, apiKey, loadEndpoints]);
|
|
314
|
+
|
|
315
|
+
React.useEffect(() => {
|
|
316
|
+
loadEndpoints({ force: true });
|
|
317
|
+
}, [loadEndpoints]);
|
|
32
318
|
|
|
33
319
|
return (
|
|
34
320
|
<>
|
|
@@ -40,10 +326,58 @@ export function ProviderSection(props: ProviderSectionProps) {
|
|
|
40
326
|
</span>
|
|
41
327
|
</div>
|
|
42
328
|
</div>
|
|
43
|
-
<div className="mt-3
|
|
329
|
+
<div className="mt-3">
|
|
330
|
+
<div className="flex items-center justify-between gap-2">
|
|
331
|
+
<span className="text-xs text-gray-600">Existing endpoints</span>
|
|
332
|
+
<Button
|
|
333
|
+
size="sm"
|
|
334
|
+
disabled={loadingEndpoints}
|
|
335
|
+
onClick={() => loadEndpoints({ force: true })}
|
|
336
|
+
>
|
|
337
|
+
{loadingEndpoints ? (
|
|
338
|
+
<RefreshCw
|
|
339
|
+
strokeWidth={1}
|
|
340
|
+
className="h-4 w-4 animate-spin"
|
|
341
|
+
/>
|
|
342
|
+
) : (
|
|
343
|
+
<RefreshCw strokeWidth={1} className="h-4 w-4" />
|
|
344
|
+
)}
|
|
345
|
+
Refresh
|
|
346
|
+
</Button>
|
|
347
|
+
</div>
|
|
348
|
+
<div className="mt-2">
|
|
349
|
+
{endpointsError && (
|
|
350
|
+
<div className="rounded border border-red-200 bg-red-50 p-2 text-xs whitespace-pre-wrap text-red-700">
|
|
351
|
+
{endpointsError}
|
|
352
|
+
</div>
|
|
353
|
+
)}
|
|
354
|
+
{!endpointsError && (
|
|
355
|
+
<>
|
|
356
|
+
{loadingEndpoints ? (
|
|
357
|
+
<div className="text-xs text-gray-500 flex items-center gap-2">
|
|
358
|
+
<RefreshCw
|
|
359
|
+
strokeWidth={1}
|
|
360
|
+
className="h-4 w-4 animate-spin"
|
|
361
|
+
/>
|
|
362
|
+
Loading endpoints…
|
|
363
|
+
</div>
|
|
364
|
+
) : endpoints.length > 0 ? (
|
|
365
|
+
<ul className="list-disc pl-5 text-xs text-gray-700">
|
|
366
|
+
{endpoints.map((ep) => (
|
|
367
|
+
<li key={ep.id}>{ep.displayName || ep.name}</li>
|
|
368
|
+
))}
|
|
369
|
+
</ul>
|
|
370
|
+
) : (
|
|
371
|
+
<div className="text-xs text-gray-500">No AI endpoints found.</div>
|
|
372
|
+
)}
|
|
373
|
+
</>
|
|
374
|
+
)}
|
|
375
|
+
</div>
|
|
376
|
+
</div>
|
|
377
|
+
<div className="mt-3 flex flex-col items-stretch gap-2">
|
|
44
378
|
<Select
|
|
45
379
|
value={aiProvider}
|
|
46
|
-
onValueChange={
|
|
380
|
+
onValueChange={setAiProvider}
|
|
47
381
|
options={aiOptions}
|
|
48
382
|
placeholder="Choose provider"
|
|
49
383
|
/>
|
|
@@ -52,14 +386,14 @@ export function ProviderSection(props: ProviderSectionProps) {
|
|
|
52
386
|
type="password"
|
|
53
387
|
placeholder="API key"
|
|
54
388
|
value={apiKey}
|
|
55
|
-
onChange={(e) =>
|
|
56
|
-
|
|
389
|
+
onChange={(e) => setApiKey(e.target.value)}
|
|
390
|
+
|
|
57
391
|
/>
|
|
58
392
|
)}
|
|
59
393
|
<Button
|
|
60
394
|
size="sm"
|
|
61
395
|
disabled={!aiProvider || !apiKey || aiBusy}
|
|
62
|
-
onClick={
|
|
396
|
+
onClick={createAiEndpoint}
|
|
63
397
|
>
|
|
64
398
|
{aiBusy ? (
|
|
65
399
|
<RefreshCw strokeWidth={1} className="h-4 w-4 animate-spin" />
|
|
@@ -69,6 +403,11 @@ export function ProviderSection(props: ProviderSectionProps) {
|
|
|
69
403
|
Add endpoint
|
|
70
404
|
</Button>
|
|
71
405
|
</div>
|
|
406
|
+
{aiError && (
|
|
407
|
+
<div className="mt-2 rounded border border-red-200 bg-red-50 p-2 text-xs whitespace-pre-wrap text-red-700">
|
|
408
|
+
{aiError}
|
|
409
|
+
</div>
|
|
410
|
+
)}
|
|
72
411
|
</>
|
|
73
412
|
);
|
|
74
413
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Button } from "../../../../../components/ui/button";
|
|
3
3
|
import { AlertCircle, CheckCircle, RefreshCw } from "lucide-react";
|
|
4
|
-
import type { StepState } from "../
|
|
4
|
+
import type { StepState } from "../types";
|
|
5
5
|
|
|
6
6
|
interface RequiredContainerDef {
|
|
7
7
|
name: string;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useEditContext } from "../../../../client/editContext";
|
|
3
|
+
import type { ItemDescriptor } from "../../../../pageModel";
|
|
4
|
+
import { getChildren } from "../../../../services/contentService";
|
|
5
|
+
import type { ItemTreeNodeData } from "../../../../services/contentService";
|
|
6
|
+
import type { StepState } from "../types";
|
|
7
|
+
import { RequiredContainersList } from "./RequiredContainersList";
|
|
8
|
+
import { findByName, resolvePathUnderContent } from "../utils";
|
|
9
|
+
|
|
10
|
+
interface RequiredContainerDef {
|
|
11
|
+
name: string;
|
|
12
|
+
templateId: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function RequiredContainersSection() {
|
|
16
|
+
const editContext = useEditContext();
|
|
17
|
+
const userLang = editContext?.contentEditorItem?.language || "en";
|
|
18
|
+
|
|
19
|
+
const requiredContainers = React.useMemo<RequiredContainerDef[]>(
|
|
20
|
+
() => [
|
|
21
|
+
{ name: "Ai Models", templateId: "e5f6a7b8-9c0d-1e2f-3a4b-5c6d7e8f9a0b" },
|
|
22
|
+
{
|
|
23
|
+
name: "Ai Endpoints",
|
|
24
|
+
templateId: "4592f2e0-06e4-470e-9820-7fd4ea148e00",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: "Ai Profiles",
|
|
28
|
+
templateId: "4298ab78-2aaa-4d5f-bb3a-0bd321b86063",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: "Ai Schemas",
|
|
32
|
+
templateId: "5214a52b-6c45-4671-97f2-703fe24e3d09",
|
|
33
|
+
},
|
|
34
|
+
{ name: "Ai Quotas", templateId: "7a08ff28-70ed-459b-a7f2-db2a78d88f46" },
|
|
35
|
+
{
|
|
36
|
+
name: "AI Generators",
|
|
37
|
+
templateId: "9430a3e9-7397-4598-9a55-2b263c927497",
|
|
38
|
+
},
|
|
39
|
+
{ name: "AI Tools", templateId: "7e3f9d9d-8e84-4e57-b12e-6e9c99d4fd11" },
|
|
40
|
+
],
|
|
41
|
+
[],
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const [editorSettingsItem, setEditorSettingsItem] =
|
|
45
|
+
React.useState<ItemDescriptor | null>(null);
|
|
46
|
+
const [containersError, setContainersError] = React.useState<string | null>(
|
|
47
|
+
null,
|
|
48
|
+
);
|
|
49
|
+
const [containerStates, setContainerStates] = React.useState<
|
|
50
|
+
Record<string, StepState>
|
|
51
|
+
>({});
|
|
52
|
+
const [busyMap, setBusyMap] = React.useState<Record<string, boolean>>({});
|
|
53
|
+
|
|
54
|
+
const checkRequiredContainers = React.useCallback(async () => {
|
|
55
|
+
try {
|
|
56
|
+
setContainersError(null);
|
|
57
|
+
const initialStates: Record<string, StepState> = {};
|
|
58
|
+
for (const rc of requiredContainers) initialStates[rc.name] = "checking";
|
|
59
|
+
setContainerStates(initialStates);
|
|
60
|
+
|
|
61
|
+
const settingsItem = await resolvePathUnderContent(
|
|
62
|
+
editContext?.sessionId,
|
|
63
|
+
userLang,
|
|
64
|
+
["Settings", "Editor"],
|
|
65
|
+
);
|
|
66
|
+
if (!settingsItem) {
|
|
67
|
+
setEditorSettingsItem(null);
|
|
68
|
+
const errorMsg = "Editor settings item not found. Create it first.";
|
|
69
|
+
setContainersError(errorMsg);
|
|
70
|
+
const errorStates: Record<string, StepState> = {};
|
|
71
|
+
for (const rc of requiredContainers) errorStates[rc.name] = "error";
|
|
72
|
+
setContainerStates(errorStates);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
setEditorSettingsItem(settingsItem);
|
|
77
|
+
const children = (await getChildren(
|
|
78
|
+
settingsItem.id,
|
|
79
|
+
editContext!.sessionId!,
|
|
80
|
+
[],
|
|
81
|
+
false,
|
|
82
|
+
userLang,
|
|
83
|
+
"path",
|
|
84
|
+
)) as unknown as ItemTreeNodeData[];
|
|
85
|
+
|
|
86
|
+
const states: Record<string, StepState> = {};
|
|
87
|
+
for (const rc of requiredContainers) {
|
|
88
|
+
const exists = !!findByName(children, rc.name);
|
|
89
|
+
states[rc.name] = exists ? "success" : "error";
|
|
90
|
+
}
|
|
91
|
+
setContainerStates(states);
|
|
92
|
+
} catch (e: any) {
|
|
93
|
+
setContainersError(e?.message || "Failed to check AI containers");
|
|
94
|
+
const errorStates: Record<string, StepState> = {};
|
|
95
|
+
for (const rc of requiredContainers) errorStates[rc.name] = "error";
|
|
96
|
+
setContainerStates(errorStates);
|
|
97
|
+
}
|
|
98
|
+
}, [editContext, requiredContainers, userLang]);
|
|
99
|
+
|
|
100
|
+
React.useEffect(() => {
|
|
101
|
+
checkRequiredContainers();
|
|
102
|
+
}, [checkRequiredContainers]);
|
|
103
|
+
|
|
104
|
+
const createContainer = React.useCallback(
|
|
105
|
+
async (name: string, templateId: string) => {
|
|
106
|
+
if (!editorSettingsItem || !editContext) return;
|
|
107
|
+
try {
|
|
108
|
+
setBusyMap((s) => ({ ...s, [name]: true }));
|
|
109
|
+
setContainersError(null);
|
|
110
|
+
|
|
111
|
+
const created = await editContext.operations.createItem(
|
|
112
|
+
editorSettingsItem,
|
|
113
|
+
templateId,
|
|
114
|
+
name,
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
if (!created) throw new Error(`Failed to create '${name}'`);
|
|
118
|
+
setContainerStates((s) => ({ ...s, [name]: "success" }));
|
|
119
|
+
} catch (e: any) {
|
|
120
|
+
setContainersError(e?.message || `Failed to create '${name}'`);
|
|
121
|
+
setContainerStates((s) => ({ ...s, [name]: "error" }));
|
|
122
|
+
} finally {
|
|
123
|
+
setBusyMap((s) => ({ ...s, [name]: false }));
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
[editorSettingsItem, editContext],
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<RequiredContainersList
|
|
131
|
+
requiredContainers={requiredContainers}
|
|
132
|
+
containerStates={containerStates}
|
|
133
|
+
busyMap={busyMap}
|
|
134
|
+
canFix={!!editorSettingsItem}
|
|
135
|
+
containersError={containersError}
|
|
136
|
+
onFixContainer={createContainer}
|
|
137
|
+
/>
|
|
138
|
+
);
|
|
139
|
+
}
|