@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.
Files changed (136) 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 +56 -5
  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.js +15 -5
  19. package/dist/editor/ai/ContextInfoBar.js.map +1 -1
  20. package/dist/editor/ai/ToolCallDisplay.js +2 -0
  21. package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
  22. package/dist/editor/client/AboutDialog.js +7 -5
  23. package/dist/editor/client/AboutDialog.js.map +1 -1
  24. package/dist/editor/client/EditorShell.js +7 -2
  25. package/dist/editor/client/EditorShell.js.map +1 -1
  26. package/dist/editor/client/editContext.d.ts +2 -2
  27. package/dist/editor/client/hooks/useSocketMessageHandler.js +5 -0
  28. package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
  29. package/dist/editor/client/itemsRepository.js +1 -0
  30. package/dist/editor/client/itemsRepository.js.map +1 -1
  31. package/dist/editor/control-center/Setup.js +1 -1
  32. package/dist/editor/control-center/Setup.js.map +1 -1
  33. package/dist/editor/control-center/setup-steps/AiSetupStep/EmbeddingsModelSection.d.ts +2 -0
  34. package/dist/editor/control-center/setup-steps/AiSetupStep/EmbeddingsModelSection.js +195 -0
  35. package/dist/editor/control-center/setup-steps/AiSetupStep/EmbeddingsModelSection.js.map +1 -0
  36. package/dist/editor/control-center/setup-steps/AiSetupStep/index.d.ts +0 -1
  37. package/dist/editor/control-center/setup-steps/AiSetupStep/index.js +3 -506
  38. package/dist/editor/control-center/setup-steps/AiSetupStep/index.js.map +1 -1
  39. package/dist/editor/control-center/setup-steps/AiSetupStep/provider/ProviderSection.d.ts +1 -15
  40. package/dist/editor/control-center/setup-steps/AiSetupStep/provider/ProviderSection.js +226 -3
  41. package/dist/editor/control-center/setup-steps/AiSetupStep/provider/ProviderSection.js.map +1 -1
  42. package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersList.d.ts +1 -1
  43. package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.d.ts +1 -0
  44. package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.js +94 -0
  45. package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.js.map +1 -0
  46. package/dist/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.d.ts +1 -7
  47. package/dist/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.js +323 -3
  48. package/dist/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.js.map +1 -1
  49. package/dist/editor/control-center/setup-steps/AiSetupStep/types.d.ts +1 -0
  50. package/dist/editor/control-center/setup-steps/AiSetupStep/types.js +2 -0
  51. package/dist/editor/control-center/setup-steps/AiSetupStep/types.js.map +1 -0
  52. package/dist/editor/control-center/setup-steps/AiSetupStep/utils.d.ts +4 -0
  53. package/dist/editor/control-center/setup-steps/AiSetupStep/utils.js +25 -0
  54. package/dist/editor/control-center/setup-steps/AiSetupStep/utils.js.map +1 -0
  55. package/dist/editor/control-center/setup-steps/IndexSetupStep.js +2 -1
  56. package/dist/editor/control-center/setup-steps/IndexSetupStep.js.map +1 -1
  57. package/dist/editor/field-types/SingleLineText.js.map +1 -1
  58. package/dist/editor/field-types/TreeListEditor.js +102 -16
  59. package/dist/editor/field-types/TreeListEditor.js.map +1 -1
  60. package/dist/editor/field-types/richtext/components/ReactSlate.js +1 -1
  61. package/dist/editor/field-types/richtext/components/SimpleRichTextEditor.js +1 -1
  62. package/dist/editor/menubar/toolbar-sections/EditControls.js +2 -2
  63. package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
  64. package/dist/editor/menubar/toolbar-sections/InsertControls.js +1 -1
  65. package/dist/editor/menubar/toolbar-sections/InsertControls.js.map +1 -1
  66. package/dist/editor/page-editor-chrome/FrameMenu.js +25 -12
  67. package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
  68. package/dist/editor/page-editor-chrome/SuggestionHighlighting.js +2 -2
  69. package/dist/editor/page-editor-chrome/SuggestionHighlighting.js.map +1 -1
  70. package/dist/editor/page-viewer/PageViewer.js +2 -1
  71. package/dist/editor/page-viewer/PageViewer.js.map +1 -1
  72. package/dist/editor/reviews/Comments.js +4 -4
  73. package/dist/editor/reviews/Comments.js.map +1 -1
  74. package/dist/editor/services/agentService.d.ts +2 -0
  75. package/dist/editor/services/agentService.js.map +1 -1
  76. package/dist/editor/services/aiService.d.ts +1 -0
  77. package/dist/editor/services/aiService.js.map +1 -1
  78. package/dist/editor/sidebar/ComponentTree.js +18 -6
  79. package/dist/editor/sidebar/ComponentTree.js.map +1 -1
  80. package/dist/editor/sidebar/Insert.js +1 -1
  81. package/dist/editor/ui/Splitter.js +2 -1
  82. package/dist/editor/ui/Splitter.js.map +1 -1
  83. package/dist/editor/utils.js +0 -1
  84. package/dist/editor/utils.js.map +1 -1
  85. package/dist/page-wizard/steps/MetaDataStep.js +43 -41
  86. package/dist/page-wizard/steps/MetaDataStep.js.map +1 -1
  87. package/dist/page-wizard/steps/SelectStep.js +19 -4
  88. package/dist/page-wizard/steps/SelectStep.js.map +1 -1
  89. package/dist/revision.d.ts +2 -2
  90. package/dist/revision.js +2 -2
  91. package/dist/styles.css +15 -18
  92. package/package.json +1 -1
  93. package/src/components/ui/button.tsx +1 -0
  94. package/src/components/ui/context-menu.tsx +44 -20
  95. package/src/editor/Editor.tsx +1 -1
  96. package/src/editor/FieldActionsOverlay.tsx +113 -91
  97. package/src/editor/FieldListField.tsx +4 -1
  98. package/src/editor/LinkEditorDialog.tsx +1 -1
  99. package/src/editor/ai/AgentTerminal.tsx +84 -4
  100. package/src/editor/ai/Agents.tsx +18 -5
  101. package/src/editor/ai/ContextInfoBar.tsx +22 -6
  102. package/src/editor/ai/ToolCallDisplay.tsx +2 -0
  103. package/src/editor/client/AboutDialog.tsx +22 -17
  104. package/src/editor/client/EditorShell.tsx +8 -2
  105. package/src/editor/client/editContext.ts +2 -2
  106. package/src/editor/client/hooks/useSocketMessageHandler.ts +8 -0
  107. package/src/editor/client/itemsRepository.ts +1 -0
  108. package/src/editor/control-center/Setup.tsx +1 -1
  109. package/src/editor/control-center/setup-steps/AiSetupStep/EmbeddingsModelSection.tsx +296 -0
  110. package/src/editor/control-center/setup-steps/AiSetupStep/index.tsx +7 -660
  111. package/src/editor/control-center/setup-steps/AiSetupStep/provider/ProviderSection.tsx +363 -24
  112. package/src/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersList.tsx +1 -1
  113. package/src/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.tsx +139 -0
  114. package/src/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.tsx +385 -8
  115. package/src/editor/control-center/setup-steps/AiSetupStep/types.ts +1 -0
  116. package/src/editor/control-center/setup-steps/AiSetupStep/utils.ts +43 -0
  117. package/src/editor/control-center/setup-steps/IndexSetupStep.tsx +2 -0
  118. package/src/editor/field-types/SingleLineText.tsx +0 -9
  119. package/src/editor/field-types/TreeListEditor.tsx +115 -16
  120. package/src/editor/field-types/richtext/components/ReactSlate.tsx +1 -1
  121. package/src/editor/field-types/richtext/components/SimpleRichTextEditor.tsx +1 -1
  122. package/src/editor/menubar/toolbar-sections/EditControls.tsx +2 -2
  123. package/src/editor/menubar/toolbar-sections/InsertControls.tsx +1 -1
  124. package/src/editor/page-editor-chrome/FrameMenu.tsx +81 -68
  125. package/src/editor/page-editor-chrome/SuggestionHighlighting.tsx +5 -5
  126. package/src/editor/page-viewer/PageViewer.tsx +3 -2
  127. package/src/editor/reviews/Comments.tsx +13 -8
  128. package/src/editor/services/agentService.ts +3 -0
  129. package/src/editor/services/aiService.ts +2 -0
  130. package/src/editor/sidebar/ComponentTree.tsx +20 -6
  131. package/src/editor/sidebar/Insert.tsx +1 -1
  132. package/src/editor/ui/Splitter.tsx +2 -2
  133. package/src/editor/utils.ts +0 -1
  134. package/src/page-wizard/steps/MetaDataStep.tsx +61 -50
  135. package/src/page-wizard/steps/SelectStep.tsx +37 -4
  136. package/src/revision.ts +2 -2
@@ -290,11 +290,19 @@ export function ContextInfoBar({
290
290
  try {
291
291
  const dragObj = editContext?.dragObject;
292
292
  if (dragObj?.type === "component") {
293
- const idFromContext = dragObj.component?.id;
293
+ const idsFromComponents = (dragObj.components || [])
294
+ .map((c) => c.id)
295
+ .filter((x) => !!x);
294
296
  const idFromData = e.dataTransfer.getData("componentId");
295
- const compId = idFromContext || idFromData;
296
- if (compId) {
297
- await addComponentIdsToContext([compId]);
297
+ const allIds = Array.from(
298
+ new Set(
299
+ [...(idsFromComponents as string[]), idFromData].filter(
300
+ (x): x is string => !!x,
301
+ ),
302
+ ),
303
+ );
304
+ if (allIds.length) {
305
+ await addComponentIdsToContext(allIds);
298
306
  if (isCollapsed) setIsCollapsed(false);
299
307
  return;
300
308
  }
@@ -306,6 +314,14 @@ export function ContextInfoBar({
306
314
  return;
307
315
  }
308
316
 
317
+ // Fallbacks when no dragObj or different source
318
+ const fallbackComponentId = e.dataTransfer.getData("componentId");
319
+ if (fallbackComponentId) {
320
+ await addComponentIdsToContext([fallbackComponentId]);
321
+ if (isCollapsed) setIsCollapsed(false);
322
+ return;
323
+ }
324
+
309
325
  try {
310
326
  const textData = e.dataTransfer.getData("text/plain");
311
327
  if (textData) {
@@ -525,14 +541,14 @@ export function ContextInfoBar({
525
541
  <div className="flex flex-wrap items-center gap-2">{chips}</div>
526
542
  <div className="ml-2 flex flex-col items-end gap-1">
527
543
  {canAddPage && (
528
- <Button size="sm" variant="outline" onClick={addPagesToContext}>
544
+ <Button size="xs" variant="outline" onClick={addPagesToContext}>
529
545
  <Plus className="mr-1 h-3 w-3" strokeWidth={1} /> Add current
530
546
  item
531
547
  </Button>
532
548
  )}
533
549
  {canAddSelection && (
534
550
  <Button
535
- size="sm"
551
+ size="xs"
536
552
  variant="outline"
537
553
  onClick={addSelectedComponentsToContext}
538
554
  >
@@ -61,6 +61,8 @@ const getToolIcon = (toolName: string) => {
61
61
  // Comments and suggestions
62
62
  "add-comment": <CheckSquare strokeWidth={1} size={14} />,
63
63
  "get-comments": <CheckSquare strokeWidth={1} size={14} />,
64
+ "get-reviews": <CheckSquare strokeWidth={1} size={14} />,
65
+ "invite-reviewer": <CheckSquare strokeWidth={1} size={14} />,
64
66
  "get-suggestions": <Brain strokeWidth={1} size={14} />,
65
67
 
66
68
  // Default fallback
@@ -1,4 +1,4 @@
1
- import { Dialog } from "primereact/dialog";
1
+ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../../components/ui/dialog";
2
2
  import { DialogProps } from "./editContext";
3
3
  import { useImperativeHandle, useState, forwardRef, useEffect } from "react";
4
4
  import { buildDate, version } from "../../revision";
@@ -20,25 +20,30 @@ export const AboutDialog = forwardRef<DialogProps<void>, DialogProps<void>>(
20
20
 
21
21
  return (
22
22
  <Dialog
23
- visible={visible}
24
- onHide={() => {
25
- setVisible(false);
26
- props.onClose(null);
23
+ open={visible}
24
+ onOpenChange={(open: boolean) => {
25
+ if (!open) {
26
+ setVisible(false);
27
+ props.onClose(null);
28
+ }
27
29
  }}
28
- header="About SiteMagician"
29
- style={{ width: "400px" }}
30
30
  >
31
- <div className="flex flex-col gap-4 p-2">
32
- <div className="bg-wizard-demo mt-3 aspect-[1.75] w-full rounded-lg"></div>
33
- <div>
34
- Build: <span className="font-bold"> {version}</span> from{" "}
35
- <span className="font-bold">{buildDate}</span>
31
+ <DialogContent style={{ width: 400 }}>
32
+ <DialogHeader>
33
+ <DialogTitle>About parhelion</DialogTitle>
34
+ </DialogHeader>
35
+ <div className="flex flex-col gap-4 p-2">
36
+ <div className="bg-wizard-demo mt-3 aspect-[1.75] w-full rounded-lg"></div>
37
+ <div>
38
+ Build: <span className="font-bold"> {version}</span> from{" "}
39
+ <span className="font-bold">{buildDate}</span>
40
+ </div>
41
+ <div className="mt-4 text-sm text-gray-600">
42
+ &copy; {new Date().getFullYear()} canvas Reply GmbH. All rights
43
+ reserved.
44
+ </div>
36
45
  </div>
37
- <div className="mt-4 text-sm text-gray-600">
38
- &copy; {new Date().getFullYear()} canvas Reply GmbH. All rights
39
- reserved.
40
- </div>
41
- </div>
46
+ </DialogContent>
42
47
  </Dialog>
43
48
  );
44
49
  },
@@ -1644,6 +1644,9 @@ export function EditorShell({
1644
1644
  readonly: isReadOnly,
1645
1645
  selection,
1646
1646
  select: (ids: string[]) => {
1647
+ try {
1648
+ console.log("[DND][EditContext] select", ids);
1649
+ } catch {}
1647
1650
  setSelection(ids);
1648
1651
  },
1649
1652
  selectedForInsertion,
@@ -1691,6 +1694,7 @@ export function EditorShell({
1691
1694
  ) => {
1692
1695
  if ((!dragObject && !insertOption) || !page) return;
1693
1696
  setDragObject(undefined);
1697
+
1694
1698
 
1695
1699
  if (spotPositionElement && spotPositionAnchor) {
1696
1700
  setInserting({
@@ -1761,7 +1765,8 @@ export function EditorShell({
1761
1765
  } as LinkComponentOperation;
1762
1766
  console.log("op", op);
1763
1767
  } else {
1764
- if (!dragObject.component) return;
1768
+ const componentIds = dragObject.components?.map((c) => c.id) ?? [];
1769
+ if (componentIds.length === 0) return;
1765
1770
  op = {
1766
1771
  type: "move-component",
1767
1772
  mainItem: page!.item.descriptor,
@@ -1772,11 +1777,12 @@ export function EditorShell({
1772
1777
  },
1773
1778
  placeholderKey,
1774
1779
  placeholderIndex: index,
1775
- componentIds: [dragObject.component.id],
1780
+ componentIds,
1776
1781
  date: new Date().toISOString(),
1777
1782
  id: uuid(),
1778
1783
  description: "Move component",
1779
1784
  } as MoveComponentOperation;
1785
+
1780
1786
  }
1781
1787
  }
1782
1788
 
@@ -58,7 +58,7 @@ export type DragObject = {
58
58
  typeId: string;
59
59
  templateId?: string;
60
60
  name?: string;
61
- component?: ItemDescriptor;
61
+ components?: ItemDescriptor[];
62
62
  items?: ItemDescriptor[];
63
63
  };
64
64
 
@@ -176,7 +176,7 @@ export type EditContextType = {
176
176
  showToast: (message: string) => void;
177
177
  sessionId: string;
178
178
  openSplashScreen: () => void;
179
- getComponentCommands: (component: Component[]) => Promise<ComponentCommand[]>;
179
+ getComponentCommands: (components: Component[]) => Promise<ComponentCommand[]>;
180
180
  selectMedia: ({
181
181
  selectedIdPath,
182
182
  mode,
@@ -244,6 +244,14 @@ 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 (
249
+ op.sessionId &&
250
+ op.sessionId === sessionId &&
251
+ (op as any).user?.ai !== true
252
+ )
253
+ return;
254
+
247
255
  const editFieldOperation = op as any;
248
256
  const field = await itemsRepository.getField({
249
257
  item: {
@@ -516,6 +516,7 @@ export function useItemsRepository(
516
516
 
517
517
  // Avoid clobbering in-flight edits: only sync to server value if not dirty
518
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);
519
520
  return { ...modifiedField, value: serverValue };
520
521
  }
521
522
 
@@ -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
+