@alpaca-editor/core 1.0.3993 → 1.0.3995

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 (123) hide show
  1. package/dist/components/ui/copy-button.d.ts +10 -0
  2. package/dist/components/ui/copy-button.js +33 -0
  3. package/dist/components/ui/copy-button.js.map +1 -0
  4. package/dist/components/ui/sonner.d.ts +3 -0
  5. package/dist/components/ui/sonner.js +14 -0
  6. package/dist/components/ui/sonner.js.map +1 -0
  7. package/dist/config/config.js +4 -4
  8. package/dist/config/config.js.map +1 -1
  9. package/dist/editor/ComponentInfo.js +2 -2
  10. package/dist/editor/ComponentInfo.js.map +1 -1
  11. package/dist/editor/ContentTree.js +2 -6
  12. package/dist/editor/ContentTree.js.map +1 -1
  13. package/dist/editor/FieldListField.js +3 -10
  14. package/dist/editor/FieldListField.js.map +1 -1
  15. package/dist/editor/ItemInfo.js +3 -3
  16. package/dist/editor/ItemInfo.js.map +1 -1
  17. package/dist/editor/ai/Agents.d.ts +6 -0
  18. package/dist/editor/ai/Agents.js +48 -0
  19. package/dist/editor/ai/Agents.js.map +1 -0
  20. package/dist/editor/ai/AiTerminal.js +4 -2
  21. package/dist/editor/ai/AiTerminal.js.map +1 -1
  22. package/dist/editor/client/EditorClient.js +48 -91
  23. package/dist/editor/client/EditorClient.js.map +1 -1
  24. package/dist/editor/client/editContext.d.ts +3 -2
  25. package/dist/editor/client/editContext.js.map +1 -1
  26. package/dist/editor/commands/itemCommands.js +5 -24
  27. package/dist/editor/commands/itemCommands.js.map +1 -1
  28. package/dist/editor/component-designer/ComponentEditor.js +3 -5
  29. package/dist/editor/component-designer/ComponentEditor.js.map +1 -1
  30. package/dist/editor/field-types/RichTextEditor.d.ts +2 -1
  31. package/dist/editor/field-types/RichTextEditor.js +2 -2
  32. package/dist/editor/field-types/RichTextEditor.js.map +1 -1
  33. package/dist/editor/field-types/RichTextEditorComponent.d.ts +2 -1
  34. package/dist/editor/field-types/RichTextEditorComponent.js +25 -15
  35. package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
  36. package/dist/editor/field-types/richtext/components/EditorDropdown.d.ts +2 -2
  37. package/dist/editor/field-types/richtext/components/EditorDropdown.js +40 -53
  38. package/dist/editor/field-types/richtext/components/EditorDropdown.js.map +1 -1
  39. package/dist/editor/field-types/richtext/components/ReactSlate.js +8 -4
  40. package/dist/editor/field-types/richtext/components/ReactSlate.js.map +1 -1
  41. package/dist/editor/field-types/richtext/utils/profileMapper.js +4 -4
  42. package/dist/editor/field-types/richtext/utils/profileMapper.js.map +1 -1
  43. package/dist/editor/media-selector/AiImageSearch.js +2 -5
  44. package/dist/editor/media-selector/AiImageSearch.js.map +1 -1
  45. package/dist/editor/media-selector/MediaFolderBrowser.js +2 -5
  46. package/dist/editor/media-selector/MediaFolderBrowser.js.map +1 -1
  47. package/dist/editor/media-selector/TreeSelector.js +2 -5
  48. package/dist/editor/media-selector/TreeSelector.js.map +1 -1
  49. package/dist/editor/menubar/FavoritesControls.d.ts +8 -0
  50. package/dist/editor/menubar/FavoritesControls.js +124 -0
  51. package/dist/editor/menubar/FavoritesControls.js.map +1 -0
  52. package/dist/editor/menubar/ItemLanguageVersion.js +2 -1
  53. package/dist/editor/menubar/ItemLanguageVersion.js.map +1 -1
  54. package/dist/editor/menubar/PageSelector.js +3 -6
  55. package/dist/editor/menubar/PageSelector.js.map +1 -1
  56. package/dist/editor/reviews/reviewCommands.js +3 -8
  57. package/dist/editor/reviews/reviewCommands.js.map +1 -1
  58. package/dist/editor/services/favouritesService.d.ts +33 -0
  59. package/dist/editor/services/favouritesService.js +22 -0
  60. package/dist/editor/services/favouritesService.js.map +1 -0
  61. package/dist/editor/sidebar/Debug.js +2 -2
  62. package/dist/editor/sidebar/Debug.js.map +1 -1
  63. package/dist/editor/sidebar/SEOInfo.js +4 -15
  64. package/dist/editor/sidebar/SEOInfo.js.map +1 -1
  65. package/dist/editor/ui/ItemSearch.js +2 -5
  66. package/dist/editor/ui/ItemSearch.js.map +1 -1
  67. package/dist/editor/ui/Section.js +1 -1
  68. package/dist/editor/utils/keyboardNavigation.d.ts +32 -0
  69. package/dist/editor/utils/keyboardNavigation.js +156 -0
  70. package/dist/editor/utils/keyboardNavigation.js.map +1 -0
  71. package/dist/page-wizard/PageWizard.d.ts +2 -2
  72. package/dist/page-wizard/steps/ContentStep.js +6 -4
  73. package/dist/page-wizard/steps/ContentStep.js.map +1 -1
  74. package/dist/page-wizard/steps/schema.js +4 -2
  75. package/dist/page-wizard/steps/schema.js.map +1 -1
  76. package/dist/revision.d.ts +2 -2
  77. package/dist/revision.js +2 -2
  78. package/dist/splash-screen/NewPage.js +7 -10
  79. package/dist/splash-screen/NewPage.js.map +1 -1
  80. package/dist/styles.css +25 -5
  81. package/package.json +3 -1
  82. package/src/components/ui/copy-button.tsx +75 -0
  83. package/src/components/ui/sonner.tsx +25 -0
  84. package/src/config/config.tsx +5 -7
  85. package/src/editor/ComponentInfo.tsx +5 -4
  86. package/src/editor/ContentTree.tsx +2 -6
  87. package/src/editor/FieldListField.tsx +4 -25
  88. package/src/editor/ItemInfo.tsx +4 -4
  89. package/src/editor/ai/Agents.tsx +125 -0
  90. package/src/editor/ai/AiTerminal.tsx +4 -0
  91. package/src/editor/client/EditorClient.tsx +58 -119
  92. package/src/editor/client/editContext.ts +3 -2
  93. package/src/editor/commands/itemCommands.tsx +10 -25
  94. package/src/editor/component-designer/ComponentEditor.tsx +8 -10
  95. package/src/editor/field-types/RichTextEditor.tsx +10 -1
  96. package/src/editor/field-types/RichTextEditorComponent.tsx +25 -11
  97. package/src/editor/field-types/richtext/components/EditorDropdown.css +81 -0
  98. package/src/editor/field-types/richtext/components/EditorDropdown.tsx +57 -72
  99. package/src/editor/field-types/richtext/components/ReactSlate.css +1 -3
  100. package/src/editor/field-types/richtext/components/ReactSlate.tsx +13 -4
  101. package/src/editor/field-types/richtext/utils/profileMapper.ts +4 -4
  102. package/src/editor/media-selector/AiImageSearch.tsx +2 -5
  103. package/src/editor/media-selector/MediaFolderBrowser.tsx +2 -5
  104. package/src/editor/media-selector/TreeSelector.tsx +2 -5
  105. package/src/editor/menubar/FavoritesControls.tsx +250 -0
  106. package/src/editor/menubar/ItemLanguageVersion.tsx +3 -1
  107. package/src/editor/menubar/PageSelector.tsx +76 -75
  108. package/src/editor/reviews/reviewCommands.tsx +3 -8
  109. package/src/editor/services/favouritesService.ts +60 -0
  110. package/src/editor/sidebar/Debug.tsx +4 -3
  111. package/src/editor/sidebar/SEOInfo.tsx +6 -16
  112. package/src/editor/ui/ItemSearch.tsx +2 -5
  113. package/src/editor/ui/Section.tsx +1 -1
  114. package/src/editor/utils/keyboardNavigation.ts +234 -0
  115. package/src/page-wizard/PageWizard.tsx +2 -2
  116. package/src/page-wizard/steps/ContentStep.tsx +6 -6
  117. package/src/page-wizard/steps/schema.ts +10 -7
  118. package/src/revision.ts +2 -2
  119. package/src/splash-screen/NewPage.tsx +28 -24
  120. package/dist/editor/ui/CopyToClipboardButton.d.ts +0 -3
  121. package/dist/editor/ui/CopyToClipboardButton.js +0 -16
  122. package/dist/editor/ui/CopyToClipboardButton.js.map +0 -1
  123. package/src/editor/ui/CopyToClipboardButton.tsx +0 -24
@@ -78,6 +78,7 @@ export function AiTerminal({
78
78
  const [profiles, setProfiles] = useState<AiProfile[]>([]);
79
79
  const [activeProfile, setActiveProfile] = useState<AiProfile>();
80
80
  const [initialPromptExecuted, setInitialPromptExecuted] = useState(false);
81
+ const [agentId] = useState<string>(() => crypto.randomUUID());
81
82
  const selection = editContext.selection;
82
83
  const terminalRef = useRef<{ submit: () => void }>(null);
83
84
  const [responseMessages, setResponseMessages] = useState<Message[]>([]);
@@ -229,6 +230,7 @@ export function AiTerminal({
229
230
  model,
230
231
  selectedText,
231
232
  context,
233
+ agentId,
232
234
  (response: any) => {
233
235
  setResponse(response);
234
236
  handleResponse(response, callback, false);
@@ -455,6 +457,7 @@ async function executePrompt(
455
457
  model: string | null,
456
458
  selectedText: string | null,
457
459
  context: AiContext,
460
+ agentId: string,
458
461
  callback: (response: any) => void,
459
462
  ): Promise<Response | null> {
460
463
  const response = await fetch(context.endpoint, {
@@ -468,6 +471,7 @@ async function executePrompt(
468
471
  selectedText,
469
472
  model,
470
473
  sessionId: session,
474
+ agentId,
471
475
  }),
472
476
  credentials: "include",
473
477
  headers: {
@@ -10,7 +10,7 @@ import React, {
10
10
  ReactNode,
11
11
  } from "react";
12
12
 
13
- import { Toast, ToastMessage } from "primereact/toast";
13
+ import { toast } from "sonner";
14
14
 
15
15
  import {
16
16
  EditContextProvider,
@@ -113,6 +113,8 @@ import { Spinner } from "../ui/Spinner";
113
113
  import { cleanId } from "../utils/id-helper";
114
114
 
115
115
  import { useDebouncedCallback } from "use-debounce";
116
+ import { useKeyboardNavigation } from "../utils/keyboardNavigation";
117
+ import { Toaster } from "../../components/ui/sonner";
116
118
 
117
119
  import { Tour } from "../../tour/Tour";
118
120
  import { usePageViewContext } from "../page-viewer/pageViewContext";
@@ -344,6 +346,8 @@ export function EditorClient({
344
346
  WebSocketMessage[]
345
347
  >([]);
346
348
 
349
+ const [favorites, setFavorites] = useState<any[]>([]);
350
+
347
351
  useEffect(() => {
348
352
  const queryMode = searchParams.get("mode");
349
353
  if (queryMode) setMode(queryMode as EditorMode);
@@ -959,6 +963,19 @@ export function EditorClient({
959
963
  setSuggestedEdits(result.data || []);
960
964
  }, [contentEditorItem]);
961
965
 
966
+ const loadFavorites = useCallback(async () => {
967
+ try {
968
+ const { getAllFavorites } = await import("../services/favouritesService");
969
+ const result = await getAllFavorites();
970
+ if (result.data) {
971
+ setFavorites(result.data);
972
+ }
973
+ } catch (error) {
974
+ console.error("Failed to load favorites:", error);
975
+ setFavorites([]);
976
+ }
977
+ }, []);
978
+
962
979
  const page = pageViewContext.page;
963
980
 
964
981
  useEffect(() => {
@@ -1043,6 +1060,11 @@ export function EditorClient({
1043
1060
  loadSuggestedEdits();
1044
1061
  }, [currentItemDescriptor]);
1045
1062
 
1063
+ // Load favorites on mount
1064
+ useEffect(() => {
1065
+ loadFavorites();
1066
+ }, [loadFavorites]);
1067
+
1046
1068
  // Automatically focus the first rendered field when a new component is added
1047
1069
  useEffect(() => {
1048
1070
  async function setFocus() {
@@ -1273,107 +1295,6 @@ export function EditorClient({
1273
1295
  [editContextRef, pathname, router, searchParams],
1274
1296
  );
1275
1297
 
1276
- const handleKeyDownDebounced = useDebouncedCallback(
1277
- async (event: KeyboardEvent) => {
1278
- if (!event.key) return;
1279
-
1280
- if (event.ctrlKey && event.key === "z") {
1281
- await operations.undo();
1282
- }
1283
- if (event.ctrlKey && event.key === "y") {
1284
- await operations.redo();
1285
- }
1286
- if (event.ctrlKey && event.key === "F11") {
1287
- event.preventDefault();
1288
- pageViewContext.setFullscreen(false);
1289
- }
1290
-
1291
- const command = configuration.commands.allItemCommands.find(
1292
- (x) => x.keyBinding === event.key,
1293
- );
1294
-
1295
- if (command) {
1296
- event.preventDefault();
1297
- const contentEditorItem = editContextRef.current?.contentEditorItem;
1298
- if (!contentEditorItem) return;
1299
-
1300
- const items =
1301
- editContextRef.current?.selection?.map((x) => ({
1302
- id: x,
1303
- language: contentEditorItem.language,
1304
- version: 0,
1305
- })) || [];
1306
-
1307
- if (!items.length) items.push(contentEditorItem.descriptor);
1308
-
1309
- if (items.length > 0) {
1310
- const fullItems =
1311
- await editContextRef.current?.itemsRepository.getItems(items);
1312
- executeCommand({
1313
- command,
1314
- data: {
1315
- items: fullItems,
1316
- },
1317
- });
1318
- }
1319
- }
1320
- },
1321
- 50,
1322
- );
1323
-
1324
- const handleKeyDown = useCallback(
1325
- async (event: KeyboardEvent) => {
1326
- if (event.key === "Insert") {
1327
- event.preventDefault();
1328
- event.stopPropagation();
1329
- editContext.setInsertMode((x) => !x);
1330
- }
1331
-
1332
- if (event.ctrlKey && event.key === "s") {
1333
- event.preventDefault();
1334
- event.stopPropagation();
1335
- return;
1336
- }
1337
-
1338
- const target = event.target as HTMLElement;
1339
- const isTyping =
1340
- target instanceof HTMLInputElement ||
1341
- target instanceof HTMLTextAreaElement ||
1342
- target.isContentEditable;
1343
-
1344
- if (
1345
- (event.ctrlKey && event.key === "z") ||
1346
- (event.ctrlKey && event.key === "y")
1347
- ) {
1348
- if (!isTyping) {
1349
- event.preventDefault();
1350
- event.stopPropagation();
1351
- handleKeyDownDebounced(event);
1352
- }
1353
- return;
1354
- }
1355
- handleKeyDownDebounced(event);
1356
- },
1357
- [
1358
- configuration.commands.allItemCommands,
1359
- executeCommand,
1360
- editContextRef.current,
1361
- ],
1362
- );
1363
-
1364
- if (typeof window !== "undefined")
1365
- if (typeof window !== "undefined")
1366
- useEventListenerExt("keydown", handleKeyDown, window, true);
1367
-
1368
- if (typeof window !== "undefined")
1369
- useEventListenerExt(
1370
- "click",
1371
- () => {
1372
- contextMenuRef.current?.close({});
1373
- },
1374
- window,
1375
- true,
1376
- );
1377
1298
  const state = useMemo(
1378
1299
  () => ({
1379
1300
  page,
@@ -1425,8 +1346,6 @@ export function EditorClient({
1425
1346
  if (currentOverlay !== "context-menu") contextMenuRef.current?.close({});
1426
1347
  }, [currentOverlay]);
1427
1348
 
1428
- const toast = useRef<Toast | null>(null);
1429
-
1430
1349
  useEffect(() => {
1431
1350
  loadItemVersions();
1432
1351
  }, [currentItemDescriptor]);
@@ -1458,24 +1377,14 @@ export function EditorClient({
1458
1377
 
1459
1378
  const showErrorToast = useCallback(
1460
1379
  ({ summary, details }: { summary?: string; details?: string }) => {
1461
- toast.current?.show({
1462
- severity: "error",
1463
- summary: summary || "Error",
1464
- detail: details || "An error occurred",
1465
- life: 3000,
1466
- });
1380
+ toast.error(details || summary || "An error occurred");
1467
1381
  },
1468
1382
  [],
1469
1383
  );
1470
1384
 
1471
1385
  const showInfoToast = useCallback(
1472
1386
  ({ summary, details }: { summary?: string; details?: string }) => {
1473
- toast.current?.show({
1474
- severity: "info",
1475
- summary: summary || "Info",
1476
- detail: details || "Information",
1477
- life: 3000,
1478
- });
1387
+ toast.info(details || summary || "Information");
1479
1388
  },
1480
1389
  [],
1481
1390
  );
@@ -1648,6 +1557,32 @@ export function EditorClient({
1648
1557
 
1649
1558
  const operations = operationsContext.ops;
1650
1559
 
1560
+ const { handleKeyDown } = useKeyboardNavigation({
1561
+ editContextRef,
1562
+ operations,
1563
+ pageViewContext,
1564
+ configuration,
1565
+ contentEditorItem,
1566
+ browseHistory,
1567
+ loadItem,
1568
+ showInfoToast,
1569
+ showErrorToast,
1570
+ executeCommand,
1571
+ });
1572
+
1573
+ if (typeof window !== "undefined")
1574
+ useEventListenerExt("keydown", handleKeyDown, window, true);
1575
+
1576
+ if (typeof window !== "undefined")
1577
+ useEventListenerExt(
1578
+ "click",
1579
+ () => {
1580
+ contextMenuRef.current?.close({});
1581
+ },
1582
+ window,
1583
+ true,
1584
+ );
1585
+
1651
1586
  useEffect(() => {
1652
1587
  if (mode === "suggestions") {
1653
1588
  setShowSuggestedEdits(true);
@@ -1772,8 +1707,8 @@ export function EditorClient({
1772
1707
  router.push(newUrl, { scroll: false });
1773
1708
  },
1774
1709
  selectMedia,
1775
- showToast: (message: ToastMessage | ToastMessage[]) => {
1776
- toast.current?.show(message);
1710
+ showToast: (message: string) => {
1711
+ toast(message);
1777
1712
  },
1778
1713
  scrollIntoView,
1779
1714
  setScrollIntoView,
@@ -2153,6 +2088,8 @@ export function EditorClient({
2153
2088
  userInfo: userInfo,
2154
2089
  userPreferences,
2155
2090
  setUserPreferences,
2091
+ favorites,
2092
+ loadFavorites,
2156
2093
  };
2157
2094
  }, [
2158
2095
  operations,
@@ -2238,6 +2175,8 @@ export function EditorClient({
2238
2175
  openDialog,
2239
2176
  pageWizard,
2240
2177
  webSocketMessages,
2178
+ favorites,
2179
+ loadFavorites,
2241
2180
  ]);
2242
2181
 
2243
2182
  const modifiedFieldsContext = useMemo(() => {
@@ -2302,7 +2241,6 @@ export function EditorClient({
2302
2241
  </>
2303
2242
  ) : (
2304
2243
  <>
2305
- <Toast ref={toast} />
2306
2244
  <ConfirmationDialog ref={confirmationDialogRef} />
2307
2245
  <EditContextMenu ref={contextMenuRef} />
2308
2246
  <MainLayout
@@ -2352,6 +2290,7 @@ export function EditorClient({
2352
2290
  </div>
2353
2291
  )}
2354
2292
  {dialog}
2293
+ <Toaster position="top-center" />
2355
2294
  </EditContextProvider>
2356
2295
  </ModifiedFieldsContextProvider>
2357
2296
  </OperationsContextProvider>
@@ -7,7 +7,6 @@ import React, {
7
7
  useEffect,
8
8
  useRef,
9
9
  } from "react";
10
- import { ToastMessage } from "primereact/toast";
11
10
 
12
11
  import { EditorConfiguration, EditorView } from "../../config/types";
13
12
  import {
@@ -171,7 +170,7 @@ export type EditContextType = {
171
170
  setShowSuggestedEditsDiff: React.Dispatch<React.SetStateAction<boolean>>;
172
171
  setCenterPanelView: (view: ReactNode) => void;
173
172
  configuration: EditorConfiguration;
174
- showToast: (message: ToastMessage | ToastMessage[]) => void;
173
+ showToast: (message: string) => void;
175
174
  sessionId: string;
176
175
  openSplashScreen: () => void;
177
176
  getComponentCommands: (component: Component[]) => Promise<ComponentCommand[]>;
@@ -369,6 +368,8 @@ export type EditContextType = {
369
368
  clearWebSocketMessages: () => void;
370
369
  userInfo: UserInfo;
371
370
  setUserPreferences: (preferences: Partial<UserPreferences>) => void;
371
+ favorites: any[];
372
+ loadFavorites: () => Promise<void>;
372
373
  };
373
374
 
374
375
  const EditContext = React.createContext<EditContextType | undefined>(undefined);
@@ -1,5 +1,6 @@
1
1
  import { Command, CommandContext, CommandData } from "./commands";
2
2
  import { FullItem, ItemDescriptor } from "../pageModel";
3
+ import { toast } from "sonner";
3
4
 
4
5
  import { ItemNameDialog, ItemNameDialogProps } from "../ui/ItemNameDialogNew";
5
6
  import {
@@ -357,6 +358,7 @@ export const publishItemCommand: ItemCommand = {
357
358
  },
358
359
  };
359
360
 
361
+
360
362
  export const exportItemsCommand: ItemCommand = {
361
363
  id: "exportItem",
362
364
  label: "Export",
@@ -428,12 +430,9 @@ export const exportItemsCommand: ItemCommand = {
428
430
  try {
429
431
  await navigator.clipboard.writeText(result.yaml);
430
432
 
431
- context.editContext.showToast({
432
- severity: "success",
433
- summary: "Export Complete",
434
- detail: `Successfully exported ${result.itemCount} item(s) to clipboard`,
435
- life: 3000,
436
- });
433
+ toast.success(
434
+ `Successfully exported ${result.itemCount} item(s) to clipboard`,
435
+ );
437
436
 
438
437
  return true;
439
438
  } catch (clipboardError) {
@@ -469,13 +468,9 @@ export const exportItemsCommand: ItemCommand = {
469
468
  }
470
469
  }
471
470
  } catch (error) {
472
- context.editContext.showToast({
473
- severity: "error",
474
- summary: "Export Failed",
475
- detail:
476
- error instanceof Error ? error.message : "Unknown error occurred",
477
- life: 5000,
478
- });
471
+ toast.error(
472
+ error instanceof Error ? error.message : "Unknown error occurred",
473
+ );
479
474
  }
480
475
 
481
476
  return false;
@@ -583,12 +578,7 @@ export const importItemsCommand: ItemCommand = {
583
578
  const result = response.data;
584
579
 
585
580
  if (result && result.createdItems) {
586
- context.editContext.showToast({
587
- severity: "success",
588
- summary: "Import Complete",
589
- detail: `Successfully imported ${result.itemCount} item(s)`,
590
- life: 3000,
591
- });
581
+ toast.success(`Successfully imported ${result.itemCount} item(s)`);
592
582
 
593
583
  // Refresh by requesting a refresh
594
584
  context.editContext.requestRefresh("immediate");
@@ -599,12 +589,7 @@ export const importItemsCommand: ItemCommand = {
599
589
  const errorMessage =
600
590
  error instanceof Error ? error.message : "Unknown error occurred";
601
591
 
602
- context.editContext.showToast({
603
- severity: "error",
604
- summary: "Import Failed",
605
- detail: errorMessage,
606
- life: 5000,
607
- });
592
+ toast.error(errorMessage);
608
593
 
609
594
  // Handle specific error cases
610
595
  if (errorMessage.includes("already exist")) {
@@ -4,6 +4,7 @@ import { ComponentsDropdown } from "./ComponentsDropdown";
4
4
  import { TemplateEditor } from "./TemplateEditor";
5
5
  import { getSelectedComponents } from "../componentTreeHelper";
6
6
  import { useEditContext } from "../client/editContext";
7
+ import { toast } from "sonner";
7
8
  import {
8
9
  Component,
9
10
  loadComponentDetails,
@@ -34,8 +35,8 @@ export function ComponentEditor({
34
35
  context.setComponentDesignerComponent(
35
36
  await loadComponentDetails(
36
37
  component.datasourceItem!.templateId,
37
- context!.page!.item.id
38
- )
38
+ context!.page!.item.id,
39
+ ),
39
40
  );
40
41
  // if (component.rendering) {
41
42
  // context.setComponentDesignerRendering(
@@ -57,8 +58,8 @@ export function ComponentEditor({
57
58
  context.setComponentDesignerComponent(
58
59
  await loadComponentDetails(
59
60
  context.componentDesignerComponent.templateId,
60
- context!.page!.item.id
61
- )
61
+ context!.page!.item.id,
62
+ ),
62
63
  );
63
64
  }
64
65
  };
@@ -66,7 +67,7 @@ export function ComponentEditor({
66
67
  }, [context.refreshCompletedFlag]);
67
68
 
68
69
  return (
69
- <div className="flex flex-col h-full">
70
+ <div className="flex h-full flex-col">
70
71
  <Toolbar>
71
72
  <ComponentsDropdown allComponents={allComponents} />{" "}
72
73
  <Button
@@ -78,12 +79,9 @@ export function ComponentEditor({
78
79
  const save = async () => {
79
80
  await saveComponentDetails(
80
81
  context.componentDesignerComponent!,
81
- context.page!.item
82
+ context.page!.item,
82
83
  );
83
- context.showToast({
84
- content: "Component saved",
85
- severity: "success",
86
- });
84
+ toast.success("Component saved");
87
85
  };
88
86
  save();
89
87
  }}
@@ -16,9 +16,11 @@ const RichTextEditorComponent = dynamic(
16
16
  export function RichTextEditor({
17
17
  field,
18
18
  readOnly,
19
+ updateFieldValue,
19
20
  }: {
20
21
  field: RichTextField;
21
22
  readOnly?: boolean;
23
+ updateFieldValue?: (value: string) => void;
22
24
  }) {
23
25
  const profilePath = field.customProperties?.profile;
24
26
 
@@ -46,5 +48,12 @@ export function RichTextEditor({
46
48
  );
47
49
  }
48
50
 
49
- return <RichTextEditorComponent field={field} readOnly={readOnly} profile={profile} />;
51
+ return (
52
+ <RichTextEditorComponent
53
+ field={field}
54
+ readOnly={readOnly}
55
+ profile={profile}
56
+ updateFieldValue={updateFieldValue}
57
+ />
58
+ );
50
59
  }
@@ -41,15 +41,18 @@ export function RichTextEditorComponent({
41
41
  field,
42
42
  readOnly,
43
43
  profile,
44
+ updateFieldValue,
44
45
  }: {
45
46
  field: RichTextField;
46
47
  readOnly?: boolean;
47
48
  profile: RichTextEditorProfile | null;
49
+ updateFieldValue?: (value: string) => void;
48
50
  }) {
49
51
  const editContextRef = useEditContextRef();
50
52
  const modifiedFieldsContext = useModifiedFieldsContext();
51
53
  const [focused, setFocused] = useState(false);
52
54
  const [value, setValue] = useState(field.value as string);
55
+ const editorRef = useRef<HTMLDivElement>(null);
53
56
 
54
57
  if (!editContextRef.current) return null;
55
58
 
@@ -65,24 +68,35 @@ export function RichTextEditorComponent({
65
68
  );
66
69
 
67
70
  useEffect(() => {
68
- if (!focused || !document.activeElement?.closest('.slate-editor')) {
69
- if (field.isHistoric) setValue(field.value as string);
70
- else setValue(modifiedField?.value ?? (field.value as string));
71
+ // Only update if the editor is not currently being edited by the user
72
+ const isEditorActive = focused && editorRef.current?.contains(document.activeElement);
73
+ const newValue = field.isHistoric
74
+ ? field.value as string
75
+ : modifiedField?.value ?? (field.value as string);
76
+
77
+ if (!isEditorActive && newValue !== value) {
78
+ setValue(newValue);
71
79
  }
72
- }, [field.value, modifiedFieldsContext?.modifiedFields]);
80
+ }, [field.value, modifiedFieldsContext?.modifiedFields, focused]);
73
81
 
74
82
  const debouncedSetFieldvalue = useThrottledCallback((value) => {
75
- editContextRef.current?.operations.editField({
76
- field: field.descriptor,
77
- value: value,
78
- refresh: "none",
79
- });
83
+ if (updateFieldValue) {
84
+ // Use the updateFieldValue prop when provided (WYSIWYG mode)
85
+ updateFieldValue(value);
86
+ } else {
87
+ // Use direct field editing when updateFieldValue is not provided (Editor mode)
88
+ editContextRef.current?.operations.editField({
89
+ field: field.descriptor,
90
+ value: value,
91
+ refresh: "none",
92
+ });
93
+ }
80
94
  }, editContextRef.current?.configuration.debounceFieldEditsInterval);
81
95
 
82
96
  const handleChange = (newValue: string) => {
83
97
  // Skip empty content patterns for both paragraph and no-tag blocks
84
98
  if ((newValue === "<p><br></p>" || newValue === "<br>" || newValue === "") && !field.value) return;
85
- if (newValue !== value && focused && !readOnly) {
99
+ if (newValue !== value && !readOnly) {
86
100
  setValue(newValue);
87
101
  debouncedSetFieldvalue(newValue);
88
102
  }
@@ -95,7 +109,7 @@ export function RichTextEditorComponent({
95
109
  // console.log(editorProfile);
96
110
 
97
111
  return (
98
- <div>
112
+ <div ref={editorRef}>
99
113
  <ReactSlate
100
114
  value={value}
101
115
  onChange={handleChange}
@@ -0,0 +1,81 @@
1
+ .editor-dropdown {
2
+ position: relative;
3
+ display: inline-block;
4
+ }
5
+
6
+ .editor-dropdown-button {
7
+ padding: 5px 8px;
8
+ background: transparent;
9
+ border: 1px solid #ddd;
10
+ border-radius: 3px;
11
+ cursor: pointer;
12
+ display: flex;
13
+ align-items: center;
14
+ min-width: 60px;
15
+ justify-content: space-between;
16
+ font-size: 13px;
17
+ }
18
+
19
+ .editor-dropdown-button-content {
20
+ display: flex;
21
+ align-items: center;
22
+ overflow: hidden;
23
+ text-overflow: ellipsis;
24
+ white-space: nowrap;
25
+ }
26
+
27
+ .editor-dropdown-button-icon {
28
+ margin-right: 8px;
29
+ flex-shrink: 0;
30
+ }
31
+
32
+ .editor-dropdown-button-text {
33
+ overflow: hidden;
34
+ text-overflow: ellipsis;
35
+ white-space: nowrap;
36
+ }
37
+
38
+ .editor-dropdown-button-arrow {
39
+ margin-left: 5px;
40
+ flex-shrink: 0;
41
+ }
42
+
43
+ .editor-dropdown-menu {
44
+ position: absolute;
45
+ top: 100%;
46
+ left: 0;
47
+ z-index: 1000;
48
+ background: white;
49
+ border: 1px solid #ddd;
50
+ border-radius: 3px;
51
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
52
+ }
53
+
54
+ .editor-dropdown-option {
55
+ padding: 8px 12px;
56
+ cursor: pointer;
57
+ background-color: transparent;
58
+ font-weight: normal;
59
+ display: flex;
60
+ align-items: center;
61
+ }
62
+
63
+ .editor-dropdown-option:hover {
64
+ background-color: #f5f5f5;
65
+ }
66
+
67
+ .editor-dropdown-option.active {
68
+ background-color: #f0f0f0;
69
+ font-weight: bold;
70
+ }
71
+
72
+ .editor-dropdown-option-icon {
73
+ margin-right: 8px;
74
+ flex-shrink: 0;
75
+ }
76
+
77
+ .editor-dropdown-option-text {
78
+ overflow: hidden;
79
+ text-overflow: ellipsis;
80
+ white-space: nowrap;
81
+ }