easy-email-pro-theme 1.59.10 → 1.60.1

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/lib/index.js CHANGED
@@ -58,17 +58,17 @@ import { classnames, useEditorActions, useDragNodePath, useStandaloneElementEdit
58
58
  import { useSlate, ReactEditor, useSlateStatic, useSelected } from "slate-react";
59
59
  import * as React$2 from "react";
60
60
  import React__default, { useRef, useState, useEffect, useCallback, useMemo, createContext, useContext, useLayoutEffect, memo, useReducer, cloneElement, forwardRef, createElement, Suspense, Component } from "react";
61
- import { NodeUtils, BlockManager, ElementType, EditorCore, t, ElementCategory, classnames as classnames$1, StandardType, PluginManager, EditorAuth, mjmlToJsonCore, ConditionOperator, ConditionOperatorSymbol, EmailRenderProvider, components, HtmlNodeAdapter, assertAiAgentFeatureEnabled, imageReplacementTarget, imageDecisionChoices, canAutoReplaceImageTarget, imageTargetAttributeKey, normalizeToolCallsPayload, freezeSelectedTargets, selectedTargetFromContext, resolveAiChatResult, choiceImageAttributeKey, I18nManager } from "easy-email-pro-core";
61
+ import { NodeUtils, BlockManager, ElementType, EditorCore, t, StandardType, ElementCategory, classnames as classnames$1, PluginManager, EditorAuth, mjmlToJsonCore, ConditionOperator, ConditionOperatorSymbol, EmailRenderProvider, components, HtmlNodeAdapter, assertAiAgentFeatureEnabled, imageReplacementTarget, imageDecisionChoices, canAutoReplaceImageTarget, imageTargetAttributeKey, normalizeToolCallsPayload, freezeSelectedTargets, selectedTargetFromContext, resolveAiChatResult, choiceImageAttributeKey, I18nManager } from "easy-email-pro-core";
62
62
  import { parseEasyEmailAiStream, resolveAiChatResult as resolveAiChatResult2 } from "easy-email-pro-core";
63
- import { cloneDeep, get, isEqual, set, omit as omit$2, merge as merge$1, debounce as debounce$2, isUndefined as isUndefined$1, uniqueId, isFunction as isFunction$5, isString as isString$2, isNumber as isNumber$1, upperFirst, sum, flatMap } from "lodash";
63
+ import { cloneDeep, omit as omit$2, get, set, isEqual, merge as merge$1, debounce as debounce$2, isUndefined as isUndefined$1, uniqueId, isFunction as isFunction$5, isString as isString$2, isNumber as isNumber$1, upperFirst, sum, flatMap } from "lodash";
64
64
  import { Editor, Range, Node, Transforms, Path, Text as Text$4, createEditor } from "slate";
65
65
  import { nanoid } from "nanoid";
66
66
  import { Form, Input, Modal, Collapse, Space, Empty, Card, Grid, Button as Button$2, Drawer, Switch, Typography as Typography$1, Tabs, Radio, Tooltip, Divider as Divider$2, Alert, Popconfirm, Message, PageHeader as PageHeader$1, Spin, Layout as Layout$2, ConfigProvider, Popover, ColorPicker as ColorPicker$2, Select as Select$1, Slider, InputNumber, Tag, Link as Link$3, List as List$1, Skeleton, Trigger } from "@arco-design/web-react";
67
+ import { HistoryEditor } from "slate-history";
67
68
  import { IconPlus, IconCopy, IconDelete, IconEdit, IconLeft, IconDownload, IconUndo, IconRedo, IconMinus, IconEye, IconSubscribeAdd, IconClose, IconCheckCircleFill, IconDragArrow, IconPalette, IconLock, IconUnlock, IconImage, IconQuestionCircle, IconLink, IconCloud, IconDragDotVertical, IconSend, IconDown, IconCheck, IconSearch } from "@arco-design/web-react/icon";
68
69
  import { unstable_batchedUpdates, createPortal } from "react-dom";
69
70
  import mjml from "mjml-browser";
70
71
  import { getImageUrlsForAmp, mjml2amp } from "mjml2amp";
71
- import { HistoryEditor } from "slate-history";
72
72
  import { renderToStaticMarkup } from "react-dom/server";
73
73
  const styleText$h = `@font-face {
74
74
  font-family: "iconfont"; /* Project id 4003152 */
@@ -1366,6 +1366,49 @@ const DraggingProvider = ({
1366
1366
  }, [dragHandle, isDragging]);
1367
1367
  return /* @__PURE__ */ React__default.createElement(DraggingProviderContext.Provider, { value }, children);
1368
1368
  };
1369
+ function setEditorFieldValue(editor, path2, name, value) {
1370
+ const node = Node.get(editor, path2);
1371
+ if (NodeUtils.isTextNode(node)) {
1372
+ Transforms.insertText(editor, String(value != null ? value : ""), { at: path2 });
1373
+ return;
1374
+ }
1375
+ if (name === "children") {
1376
+ if (!Array.isArray(value)) {
1377
+ throw new Error("children must be an array");
1378
+ }
1379
+ const children = node.children || [];
1380
+ Editor.withoutNormalizing(editor, () => {
1381
+ for (let index2 = children.length - 1; index2 >= 0; index2 -= 1) {
1382
+ Transforms.removeNodes(editor, { at: [...path2, index2] });
1383
+ }
1384
+ if (value.length) {
1385
+ Transforms.insertNodes(editor, cloneDeep(value), {
1386
+ at: [...path2, 0]
1387
+ });
1388
+ }
1389
+ });
1390
+ return;
1391
+ }
1392
+ if (name === "") {
1393
+ editor.replaceNode({ path: path2, node: value });
1394
+ return;
1395
+ }
1396
+ const cloneNode = cloneDeep(omit$2(node, "children"));
1397
+ if (value === void 0) {
1398
+ const dotIndex = name.lastIndexOf(".");
1399
+ if (dotIndex > -1) {
1400
+ const parent2 = get(cloneNode, name.substring(0, dotIndex));
1401
+ if (parent2 && typeof parent2 === "object") {
1402
+ delete parent2[name.substring(dotIndex + 1)];
1403
+ }
1404
+ } else {
1405
+ delete cloneNode[name];
1406
+ }
1407
+ } else {
1408
+ set(cloneNode, name, value);
1409
+ }
1410
+ Transforms.setNodes(editor, cloneNode, { at: path2 });
1411
+ }
1369
1412
  const loop$1 = () => {
1370
1413
  };
1371
1414
  const getNodePathName = (path2, name) => {
@@ -1432,58 +1475,30 @@ const EditorContextProvider = (props) => {
1432
1475
  getFieldValue(path2, name)
1433
1476
  );
1434
1477
  });
1435
- const setFieldValue = useEventCallback(
1478
+ const setFieldValueTransactional = useEventCallback(
1436
1479
  (path2, name, val) => {
1437
1480
  if (!path2) {
1438
1481
  form.setFieldValue(name, val);
1439
1482
  return;
1440
1483
  }
1484
+ setEditorFieldValue(editor, path2, name, val);
1485
+ form.setFieldValue(getNodePathName(path2, name), val);
1486
+ }
1487
+ );
1488
+ const setFieldValue = useEventCallback(
1489
+ (path2, name, val) => {
1441
1490
  try {
1442
- const node = Node.get(editor, path2);
1443
- if (NodeUtils.isTextNode(node)) {
1444
- Transforms.insertText(editor, val, {
1445
- at: path2
1446
- });
1447
- } else {
1448
- if (name === "children") {
1449
- const cloneNode = cloneDeep(node);
1450
- set(cloneNode, name, val);
1451
- editor.replaceNode({
1452
- path: path2,
1453
- node: cloneNode
1454
- });
1455
- } else if (name === "") {
1456
- editor.replaceNode({
1457
- path: path2,
1458
- node: val
1459
- });
1460
- } else {
1461
- const cloneNode = cloneDeep(omit$2(node, "children"));
1462
- if (val === void 0) {
1463
- const dotIndex = name.lastIndexOf(".");
1464
- if (dotIndex > -1) {
1465
- const parentName = name.substring(0, dotIndex);
1466
- const parent2 = get(cloneNode, parentName);
1467
- if (parent2) {
1468
- parent2[name.substring(dotIndex + 1)] = void 0;
1469
- }
1470
- } else {
1471
- cloneNode[name] = void 0;
1472
- }
1473
- } else {
1474
- set(cloneNode, name, val);
1475
- }
1476
- Transforms.setNodes(editor, cloneNode, {
1477
- at: path2
1478
- });
1479
- }
1480
- }
1481
- form.setFieldValue(getNodePathName(path2, name), val);
1491
+ setFieldValueTransactional(path2, name, val);
1482
1492
  } catch (error2) {
1483
1493
  console.log(error2);
1484
1494
  }
1485
1495
  }
1486
1496
  );
1497
+ const syncValues = useEventCallback(
1498
+ (newValues) => {
1499
+ form.setFieldsValue(newValues);
1500
+ }
1501
+ );
1487
1502
  const reset = useEventCallback((newValues) => {
1488
1503
  if (isEqual(newValues, values))
1489
1504
  return;
@@ -1551,6 +1566,8 @@ const EditorContextProvider = (props) => {
1551
1566
  inited,
1552
1567
  pageDataVariables,
1553
1568
  setFieldValue,
1569
+ setFieldValueTransactional,
1570
+ syncValues,
1554
1571
  getFieldDirty,
1555
1572
  valid,
1556
1573
  reset,
@@ -1564,6 +1581,8 @@ const EditorContextProvider = (props) => {
1564
1581
  inited,
1565
1582
  pageDataVariables,
1566
1583
  setFieldValue,
1584
+ setFieldValueTransactional,
1585
+ syncValues,
1567
1586
  getFieldDirty,
1568
1587
  valid,
1569
1588
  reset,
@@ -2333,6 +2352,335 @@ const useBlocksDrawer = () => {
2333
2352
  parentCategory: context.blocksDrawerParentCategory
2334
2353
  };
2335
2354
  };
2355
+ const MAX_AI_BATCH_TARGETS = 200;
2356
+ const STANDARD_TYPES = new Set(Object.values(StandardType));
2357
+ const CHANGE_FIELDS = [
2358
+ "text",
2359
+ "attributes",
2360
+ "mobileAttributes",
2361
+ "data",
2362
+ "visibility"
2363
+ ];
2364
+ function isObject$c(value) {
2365
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
2366
+ }
2367
+ function isSafeFieldKey$1(key2) {
2368
+ return typeof key2 === "string" && key2.length > 0 && key2.length <= 80 && !key2.includes("__proto__") && !key2.includes("prototype") && !key2.includes("constructor") && /^[a-zA-Z0-9_.-]+$/.test(key2);
2369
+ }
2370
+ function validateRecordKeys(value) {
2371
+ if (!isObject$c(value))
2372
+ throw new Error("batch changes must be objects");
2373
+ Object.keys(value).forEach((key2) => {
2374
+ if (!isSafeFieldKey$1(key2))
2375
+ throw new Error(`unsafe field key: ${key2}`);
2376
+ });
2377
+ }
2378
+ function validateFilter(filter2) {
2379
+ if (filter2.field !== "text" && filter2.field !== "attribute" && filter2.field !== "mobileAttribute" && filter2.field !== "data") {
2380
+ throw new Error(`unsupported batch filter field: ${String(filter2.field)}`);
2381
+ }
2382
+ if (filter2.operator !== "equals" && filter2.operator !== "contains" && filter2.operator !== "exists") {
2383
+ throw new Error(
2384
+ `unsupported batch filter operator: ${String(filter2.operator)}`
2385
+ );
2386
+ }
2387
+ if (filter2.field !== "text" && !isSafeFieldKey$1(filter2.key)) {
2388
+ throw new Error("batch filter key is required and must be safe");
2389
+ }
2390
+ if (filter2.operator === "contains" && typeof filter2.value !== "string") {
2391
+ throw new Error("batch contains filter requires a string value");
2392
+ }
2393
+ }
2394
+ function validateBatchUpdateCall(call) {
2395
+ var _a;
2396
+ if (call.name !== "batch_update") {
2397
+ throw new Error("batch_update tool call is required");
2398
+ }
2399
+ if (call.selector.scope !== "template" && call.selector.scope !== "descendants") {
2400
+ throw new Error("invalid batch selector scope");
2401
+ }
2402
+ if (!Array.isArray(call.selector.types) || call.selector.types.length === 0) {
2403
+ throw new Error("at least one standard block type is required");
2404
+ }
2405
+ call.selector.types.forEach((type) => {
2406
+ if (!STANDARD_TYPES.has(type)) {
2407
+ throw new Error(`unsupported standard block type: ${type}`);
2408
+ }
2409
+ });
2410
+ if (call.selector.scope === "descendants" && !call.selector.root) {
2411
+ throw new Error("descendants scope requires a root target");
2412
+ }
2413
+ (call.selector.where || []).forEach(validateFilter);
2414
+ const changes2 = call.changes || {};
2415
+ if (!CHANGE_FIELDS.some((field) => changes2[field] !== void 0)) {
2416
+ throw new Error("at least one batch change is required");
2417
+ }
2418
+ if (changes2.text !== void 0 && typeof changes2.text !== "string") {
2419
+ throw new Error("batch text change must be a string");
2420
+ }
2421
+ if (changes2.attributes !== void 0)
2422
+ validateRecordKeys(changes2.attributes);
2423
+ if (changes2.mobileAttributes !== void 0)
2424
+ validateRecordKeys(changes2.mobileAttributes);
2425
+ if (changes2.data !== void 0)
2426
+ validateRecordKeys(changes2.data);
2427
+ if (changes2.visibility !== void 0 && (!isObject$c(changes2.visibility) || typeof changes2.visibility.desktop !== "boolean" || typeof changes2.visibility.mobile !== "boolean")) {
2428
+ throw new Error("batch visibility change is invalid");
2429
+ }
2430
+ if (((_a = changes2.visibility) == null ? void 0 : _a.desktop) === false && changes2.visibility.mobile === false) {
2431
+ throw new Error("at least one device must remain visible");
2432
+ }
2433
+ }
2434
+ function ownValue(record, key2) {
2435
+ if (!isObject$c(record) || !Object.prototype.hasOwnProperty.call(record, key2)) {
2436
+ return { exists: false, value: void 0 };
2437
+ }
2438
+ return { exists: true, value: record[key2] };
2439
+ }
2440
+ function filterValue(element, filter2) {
2441
+ if (filter2.field === "text") {
2442
+ return { exists: true, value: Node.string(element) };
2443
+ }
2444
+ const key2 = filter2.key;
2445
+ if (filter2.field === "attribute")
2446
+ return ownValue(element.attributes, key2);
2447
+ if (filter2.field === "mobileAttribute")
2448
+ return ownValue(element.mobileAttributes, key2);
2449
+ return ownValue(element.data, key2);
2450
+ }
2451
+ function matchesFilter(element, filter2) {
2452
+ const current = filterValue(element, filter2);
2453
+ if (filter2.operator === "exists")
2454
+ return current.exists;
2455
+ if (filter2.operator === "contains") {
2456
+ return typeof current.value === "string" && current.value.includes(filter2.value);
2457
+ }
2458
+ return current.exists && Object.is(current.value, filter2.value);
2459
+ }
2460
+ function resolveBatchUpdateTargets(editor, call, resolveRoot) {
2461
+ validateBatchUpdateCall(call);
2462
+ let at = [];
2463
+ if (call.selector.scope === "descendants") {
2464
+ const root2 = (resolveRoot == null ? void 0 : resolveRoot(call.selector.root)) || null;
2465
+ if (!root2)
2466
+ throw new Error("batch root target not found");
2467
+ at = root2;
2468
+ }
2469
+ const acceptedTypes = new Set(call.selector.types);
2470
+ const filters = call.selector.where || [];
2471
+ const targets = Array.from(
2472
+ Editor.nodes(editor, {
2473
+ at,
2474
+ match: (node) => {
2475
+ if (!NodeUtils.isElement(node))
2476
+ return false;
2477
+ const element = node;
2478
+ return acceptedTypes.has(element.type) && filters.every((filter2) => matchesFilter(element, filter2));
2479
+ }
2480
+ })
2481
+ ).map(([node, path2]) => ({
2482
+ element: node,
2483
+ path: [...path2]
2484
+ }));
2485
+ return { call, targets };
2486
+ }
2487
+ function assertBatchTargetLimit(batches, limit = MAX_AI_BATCH_TARGETS) {
2488
+ const count = batches.reduce(
2489
+ (total, batch) => total + batch.targets.length,
2490
+ 0
2491
+ );
2492
+ if (count > limit) {
2493
+ throw new Error(`batch target limit exceeded: ${count}/${limit}`);
2494
+ }
2495
+ }
2496
+ function getBatchRecordUpdate(current, key2, nextValue) {
2497
+ const value = nextValue === null ? void 0 : nextValue;
2498
+ const hasCurrent = Object.prototype.hasOwnProperty.call(current, key2);
2499
+ return {
2500
+ changed: !(value === void 0 && !hasCurrent) && !Object.is(current[key2], value),
2501
+ value
2502
+ };
2503
+ }
2504
+ function applyResolvedBatchUpdate(resolved, applyField) {
2505
+ const result = { applied: 0, skipped: 0, errors: [] };
2506
+ if (!resolved.targets.length)
2507
+ return __spreadProps(__spreadValues({}, result), { skipped: 1 });
2508
+ for (const target of resolved.targets) {
2509
+ let changed = false;
2510
+ for (const field of CHANGE_FIELDS) {
2511
+ if (resolved.call.changes[field] === void 0)
2512
+ continue;
2513
+ const fieldResult = applyField(
2514
+ target,
2515
+ field,
2516
+ resolved.call.changes[field]
2517
+ );
2518
+ if (typeof fieldResult === "boolean") {
2519
+ changed = fieldResult || changed;
2520
+ } else {
2521
+ changed = fieldResult.changed || changed;
2522
+ if (fieldResult.error) {
2523
+ result.errors.push(`${target.element.type}: ${fieldResult.error}`);
2524
+ }
2525
+ }
2526
+ }
2527
+ if (changed)
2528
+ result.applied += 1;
2529
+ else
2530
+ result.skipped += 1;
2531
+ }
2532
+ return result;
2533
+ }
2534
+ const changes = /* @__PURE__ */ new WeakMap();
2535
+ const operationChange = Symbol("aiExternalHistoryChange");
2536
+ const setters = /* @__PURE__ */ new WeakMap();
2537
+ const installed = /* @__PURE__ */ new WeakSet();
2538
+ function mergeChanges(earlier, later) {
2539
+ if (!earlier)
2540
+ return later;
2541
+ if (!later)
2542
+ return earlier;
2543
+ return { before: earlier.before, after: later.after };
2544
+ }
2545
+ function getAiExternalHistory(batch) {
2546
+ if (!batch)
2547
+ return void 0;
2548
+ return changes.get(batch) || batch.operations.map(
2549
+ (operation) => operation[operationChange]
2550
+ ).find(Boolean);
2551
+ }
2552
+ function installAiExternalHistory(editor, setValue) {
2553
+ setters.set(editor, setValue);
2554
+ if (installed.has(editor))
2555
+ return;
2556
+ installed.add(editor);
2557
+ const undo = editor.undo.bind(editor);
2558
+ const redo = editor.redo.bind(editor);
2559
+ const writeHistory = editor.writeHistory.bind(editor);
2560
+ let activeChange;
2561
+ editor.writeHistory = (stack, batch) => {
2562
+ const beforeLength = editor.history[stack].length;
2563
+ const previousBatch = editor.history[stack].at(-1);
2564
+ const previousChange = getAiExternalHistory(previousBatch);
2565
+ const incomingChange = mergeChanges(
2566
+ getAiExternalHistory(batch),
2567
+ activeChange
2568
+ );
2569
+ writeHistory(stack, batch);
2570
+ const actualBatch = editor.history[stack].at(-1);
2571
+ if (!actualBatch)
2572
+ return;
2573
+ const replacedPrevious = editor.history[stack].length === beforeLength && actualBatch !== previousBatch;
2574
+ const change = replacedPrevious ? mergeChanges(previousChange, incomingChange) : incomingChange;
2575
+ if (change)
2576
+ attachAiExternalHistory(actualBatch, change);
2577
+ };
2578
+ editor.undo = () => {
2579
+ var _a;
2580
+ const batch = editor.history.undos.at(-1);
2581
+ const change = getAiExternalHistory(batch);
2582
+ activeChange = change;
2583
+ try {
2584
+ undo();
2585
+ } finally {
2586
+ activeChange = void 0;
2587
+ }
2588
+ if (change)
2589
+ (_a = setters.get(editor)) == null ? void 0 : _a(change.before);
2590
+ };
2591
+ editor.redo = () => {
2592
+ var _a;
2593
+ const batch = editor.history.redos.at(-1);
2594
+ const change = getAiExternalHistory(batch);
2595
+ activeChange = change;
2596
+ try {
2597
+ redo();
2598
+ } finally {
2599
+ activeChange = void 0;
2600
+ }
2601
+ if (change)
2602
+ (_a = setters.get(editor)) == null ? void 0 : _a(change.after);
2603
+ };
2604
+ }
2605
+ function attachAiExternalHistory(batch, change) {
2606
+ changes.set(batch, change);
2607
+ batch.operations.forEach((operation) => {
2608
+ Object.assign(operation, { [operationChange]: change });
2609
+ });
2610
+ }
2611
+ function runAiEditorTransaction(editor, apply2, options2 = {}) {
2612
+ var _a;
2613
+ const undosBefore = [...editor.history.undos];
2614
+ const redosBefore = [...editor.history.redos];
2615
+ const undoStart = editor.history.undos.length;
2616
+ try {
2617
+ let result;
2618
+ HistoryEditor.withoutMerging(editor, () => {
2619
+ Editor.withoutNormalizing(editor, () => {
2620
+ result = apply2();
2621
+ });
2622
+ });
2623
+ const newBatches = editor.history.undos.splice(undoStart);
2624
+ const externalChange = ((_a = options2.getExternalChange) == null ? void 0 : _a.call(options2)) || null;
2625
+ const hasExternalChange = Boolean(
2626
+ externalChange && externalChange.before !== externalChange.after
2627
+ );
2628
+ const finalBatch = newBatches.length ? {
2629
+ operations: newBatches.flatMap((batch) => batch.operations),
2630
+ selectionBefore: newBatches[0].selectionBefore
2631
+ } : hasExternalChange ? { operations: [], selectionBefore: editor.selection } : null;
2632
+ if (finalBatch) {
2633
+ editor.history.undos.push(finalBatch);
2634
+ if (hasExternalChange && externalChange) {
2635
+ attachAiExternalHistory(finalBatch, externalChange);
2636
+ }
2637
+ if (!newBatches.length) {
2638
+ editor.history.redos = [];
2639
+ }
2640
+ }
2641
+ return result;
2642
+ } catch (error2) {
2643
+ while (editor.history.undos.length > undoStart) {
2644
+ HistoryEditor.undo(editor);
2645
+ }
2646
+ editor.history.undos = undosBefore;
2647
+ editor.history.redos = redosBefore;
2648
+ throw error2;
2649
+ }
2650
+ }
2651
+ function validateAiSubject(value) {
2652
+ if (typeof value !== "string") {
2653
+ throw new Error(t("subject must be a string"));
2654
+ }
2655
+ if (!value.trim()) {
2656
+ throw new Error(t("subject cannot be empty"));
2657
+ }
2658
+ return value;
2659
+ }
2660
+ function isAiSubjectChanged(current, next) {
2661
+ return current !== next;
2662
+ }
2663
+ function subjectAfterApplyingTemplate(current, template) {
2664
+ if (!template || typeof template !== "object" || !("subject" in template)) {
2665
+ return current;
2666
+ }
2667
+ return validateAiSubject(template.subject);
2668
+ }
2669
+ function emailAfterApplyingAiTemplate(current, template) {
2670
+ if (!template || typeof template !== "object")
2671
+ return current;
2672
+ const candidate = template;
2673
+ if (!candidate.content || typeof candidate.content !== "object") {
2674
+ return current;
2675
+ }
2676
+ return __spreadProps(__spreadValues({}, current), {
2677
+ subject: subjectAfterApplyingTemplate(
2678
+ typeof current.subject === "string" ? current.subject : "",
2679
+ candidate
2680
+ ),
2681
+ content: candidate.content
2682
+ });
2683
+ }
2336
2684
  const MAX_TOOL_CALLS = 30;
2337
2685
  const MAX_REPLACE_CHILDREN_NODES = 160;
2338
2686
  function isObject$b(value) {
@@ -2423,6 +2771,10 @@ function targetFromElement(element, fallback) {
2423
2771
  return fallback;
2424
2772
  }
2425
2773
  function describeAppliedChange(call) {
2774
+ if (call.name === "update_subject")
2775
+ return t("Subject updated");
2776
+ if (call.name === "batch_update")
2777
+ return t("Batch updated");
2426
2778
  if (call.name === "update_text")
2427
2779
  return t("Text updated");
2428
2780
  if (call.name === "update_attribute")
@@ -2511,13 +2863,21 @@ function normalizeElementBlock(block) {
2511
2863
  children: Array.isArray(normalized.children) ? normalized.children : []
2512
2864
  }));
2513
2865
  }
2866
+ function createBlockForInsert(block) {
2867
+ const normalized = normalizeBlock(block);
2868
+ const definition = BlockManager.getBlockByType(normalized.type);
2869
+ return assignIdsToElementTree(definition.create(normalized));
2870
+ }
2514
2871
  function useApplyAiToolCalls() {
2515
2872
  const editor = useSlate();
2516
2873
  const lock = useLockState();
2517
2874
  const selectedNodePath = useSelectedNodePath();
2518
2875
  const setSelectedNodePath = useSetSelectedNodePath();
2519
- const { setFieldValue, reset } = useEditorContext();
2520
- const { addBlock, copyBlock, deleteBlock, moveDown, moveUp } = useElementInteract();
2876
+ const setHoverNodePath = useSetHoverNodePath();
2877
+ const { setFieldValueTransactional, syncValues, reset, values } = useEditorContext();
2878
+ installAiExternalHistory(editor, (subject) => {
2879
+ setFieldValueTransactional(null, "subject", subject);
2880
+ });
2521
2881
  const resolveTargetPath = useEventCallback((target) => {
2522
2882
  if ("selected" in target)
2523
2883
  return selectedNodePath;
@@ -2561,6 +2921,57 @@ function useApplyAiToolCalls() {
2561
2921
  return [node, path2];
2562
2922
  }
2563
2923
  );
2924
+ const applyBatchField = useEventCallback(
2925
+ (target, field, value) => {
2926
+ const node = Node.get(editor, target.path);
2927
+ if (!NodeUtils.isElement(node))
2928
+ return false;
2929
+ const element = node;
2930
+ if (field === "text") {
2931
+ if (typeof value !== "string" || !canReplaceWithPlainText(element)) {
2932
+ return {
2933
+ changed: false,
2934
+ error: t("target has no editable plain text")
2935
+ };
2936
+ }
2937
+ if (Node.string(element) === value)
2938
+ return false;
2939
+ setFieldValueTransactional(
2940
+ target.path,
2941
+ "children",
2942
+ textToChildren(value)
2943
+ );
2944
+ return true;
2945
+ }
2946
+ if (field === "visibility") {
2947
+ if (!isObject$b(value))
2948
+ return false;
2949
+ const visible = value.desktop === true && value.mobile === false ? "desktop" : value.desktop === false && value.mobile === true ? "mobile" : void 0;
2950
+ if (element.visible === visible)
2951
+ return false;
2952
+ setFieldValueTransactional(target.path, "visible", visible);
2953
+ return true;
2954
+ }
2955
+ if (!isObject$b(value))
2956
+ return false;
2957
+ const fieldRoot = field;
2958
+ const source = element[fieldRoot];
2959
+ const current = isObject$b(source) ? source : {};
2960
+ let changed = false;
2961
+ Object.entries(value).forEach(([key2, nextValue]) => {
2962
+ const update = getBatchRecordUpdate(current, key2, nextValue);
2963
+ if (!update.changed)
2964
+ return;
2965
+ setFieldValueTransactional(
2966
+ target.path,
2967
+ `${fieldRoot}.${key2}`,
2968
+ update.value
2969
+ );
2970
+ changed = true;
2971
+ });
2972
+ return changed;
2973
+ }
2974
+ );
2564
2975
  return useEventCallback((toolCalls) => {
2565
2976
  const result = {
2566
2977
  applied: 0,
@@ -2575,63 +2986,272 @@ function useApplyAiToolCalls() {
2575
2986
  errors: [t("editor is locked")]
2576
2987
  };
2577
2988
  }
2989
+ if (toolCalls.some((call) => call.name === "apply_template") && toolCalls.length > 1) {
2990
+ return {
2991
+ applied: 0,
2992
+ skipped: toolCalls.length,
2993
+ errors: [t("apply_template cannot be mixed with other tools")],
2994
+ changes: []
2995
+ };
2996
+ }
2578
2997
  const calls = toolCalls.slice(0, MAX_TOOL_CALLS);
2579
2998
  if (toolCalls.length > MAX_TOOL_CALLS) {
2580
2999
  result.errors.push(`${t("tool calls truncated to")} ${MAX_TOOL_CALLS}`);
2581
3000
  }
2582
- Editor.withoutNormalizing(editor, () => {
3001
+ const resolvedBatches = /* @__PURE__ */ new Map();
3002
+ const resolvedTargets = /* @__PURE__ */ new Map();
3003
+ const preflightErrors = [];
3004
+ try {
2583
3005
  calls.forEach((call, index2) => {
2584
- try {
2585
- if (call.name === "apply_template") {
2586
- reset(call.template);
2587
- result.applied += 1;
2588
- return;
3006
+ if (call.name === "update_subject") {
3007
+ validateAiSubject(call.subject);
3008
+ return;
3009
+ }
3010
+ if (call.name === "apply_template") {
3011
+ if (!isObject$b(call.template)) {
3012
+ throw new Error(t("template must be an object"));
2589
3013
  }
2590
- const targetEntry = getTargetElement(call.target);
2591
- if (!targetEntry) {
2592
- result.skipped += 1;
2593
- result.errors.push(
2594
- `${call.name}: ${t("target not found")} (${targetLabel(call.target)})`
3014
+ if (calls.length > 1) {
3015
+ throw new Error(
3016
+ t("apply_template cannot be mixed with other tools")
3017
+ );
3018
+ }
3019
+ return;
3020
+ }
3021
+ if (call.name === "batch_update") {
3022
+ resolvedBatches.set(
3023
+ index2,
3024
+ resolveBatchUpdateTargets(
3025
+ editor,
3026
+ call,
3027
+ (target) => resolveTargetPath(target) || null
3028
+ )
3029
+ );
3030
+ return;
3031
+ }
3032
+ const targetEntry = getTargetElement(call.target);
3033
+ if (!targetEntry) {
3034
+ throw new Error(
3035
+ `${t("target not found")} (${targetLabel(call.target)})`
3036
+ );
3037
+ }
3038
+ const [element, path2] = targetEntry;
3039
+ resolvedTargets.set(index2, targetEntry);
3040
+ if (call.name === "update_text" && !canReplaceWithPlainText(element)) {
3041
+ throw new Error(
3042
+ hasAnyTextNode(element) ? t(
3043
+ "target contains mixed content; use a more specific text node"
3044
+ ) : t("target has no editable text")
3045
+ );
3046
+ }
3047
+ if ((call.name === "update_attribute" || call.name === "update_mobile_attribute" || call.name === "update_data") && !isSafeFieldKey(call.key)) {
3048
+ throw new Error(t("invalid field key"));
3049
+ }
3050
+ if (call.name === "update_attributes") {
3051
+ if (!isObject$b(call.attributes)) {
3052
+ throw new Error(t("attributes must be an object"));
3053
+ }
3054
+ Object.keys(call.attributes).forEach((key2) => {
3055
+ if (!isSafeFieldKey(key2)) {
3056
+ throw new Error(`${t("invalid attribute key")}: ${key2}`);
3057
+ }
3058
+ });
3059
+ }
3060
+ if (call.name === "replace_children") {
3061
+ const total = countNodes(call.children);
3062
+ if (total > MAX_REPLACE_CHILDREN_NODES) {
3063
+ throw new Error(
3064
+ `${t("replace_children is too large")} (${total} ${t("nodes")})`
2595
3065
  );
2596
- return;
2597
3066
  }
2598
- const [element, path2] = targetEntry;
2599
- const recordChange = () => {
2600
- var _a, _b;
3067
+ normalizeChildren(call.children);
3068
+ }
3069
+ if (call.name === "set_visibility" && call.desktop === false && call.mobile === false) {
3070
+ throw new Error(t("at least one device must remain visible"));
3071
+ }
3072
+ if (call.name === "update_link" && typeof call.href !== "string") {
3073
+ throw new Error(t("href is required"));
3074
+ }
3075
+ if ((call.name === "replace_block" || call.name === "move_up" || call.name === "move_down" || call.name === "duplicate" || call.name === "delete_node" || call.name === "add_block_after" || call.name === "add_block_before") && isProtectedForStructureEdit(element)) {
3076
+ throw new Error(t("protected node cannot be structurally edited"));
3077
+ }
3078
+ if (call.name === "replace_block")
3079
+ normalizeElementBlock(call.block);
3080
+ if (call.name === "add_block_after" || call.name === "add_block_before") {
3081
+ createBlockForInsert(call.block);
3082
+ }
3083
+ Node.get(editor, path2);
3084
+ });
3085
+ assertBatchTargetLimit(Array.from(resolvedBatches.values()));
3086
+ } catch (error2) {
3087
+ preflightErrors.push(
3088
+ error2 instanceof Error ? error2.message : String(error2)
3089
+ );
3090
+ }
3091
+ if (preflightErrors.length) {
3092
+ return {
3093
+ applied: 0,
3094
+ skipped: calls.length,
3095
+ errors: preflightErrors,
3096
+ changes: []
3097
+ };
3098
+ }
3099
+ const targetRefs = /* @__PURE__ */ new Map();
3100
+ const batchRefs = /* @__PURE__ */ new Map();
3101
+ resolvedTargets.forEach(([, path2], index2) => {
3102
+ targetRefs.set(index2, Editor.pathRef(editor, path2));
3103
+ });
3104
+ resolvedBatches.forEach((resolved, index2) => {
3105
+ batchRefs.set(
3106
+ index2,
3107
+ resolved.targets.map((target) => Editor.pathRef(editor, target.path))
3108
+ );
3109
+ });
3110
+ const originalValues = structuredClone(values);
3111
+ const subjectBefore = values.subject;
3112
+ let subjectAfter = subjectBefore;
3113
+ try {
3114
+ runAiEditorTransaction(
3115
+ editor,
3116
+ () => {
3117
+ calls.forEach((call, index2) => {
3118
+ var _a, _b, _c, _d;
3119
+ if (call.name === "update_subject") {
3120
+ const nextSubject = validateAiSubject(call.subject);
3121
+ if (!isAiSubjectChanged(subjectAfter, nextSubject)) {
3122
+ result.skipped += 1;
3123
+ return;
3124
+ }
3125
+ setFieldValueTransactional(null, "subject", nextSubject);
3126
+ subjectAfter = nextSubject;
3127
+ result.applied += 1;
3128
+ if (!((_a = result.changes) == null ? void 0 : _a.some((change) => change.id === "subject"))) {
3129
+ (_b = result.changes) == null ? void 0 : _b.push({
3130
+ id: "subject",
3131
+ label: t("Subject"),
3132
+ description: describeAppliedChange(call),
3133
+ target: { type: "page" }
3134
+ });
3135
+ }
3136
+ return;
3137
+ }
3138
+ if (call.name === "apply_template") {
3139
+ const nextTemplate = emailAfterApplyingAiTemplate(
3140
+ values,
3141
+ call.template
3142
+ );
3143
+ subjectAfter = subjectAfterApplyingTemplate(
3144
+ subjectAfter,
3145
+ nextTemplate
3146
+ );
3147
+ reset(nextTemplate);
3148
+ result.applied += 1;
3149
+ return;
3150
+ }
3151
+ if (call.name === "batch_update") {
3152
+ const resolved = resolvedBatches.get(index2);
3153
+ if (!resolved)
3154
+ throw new Error(t("batch targets not resolved"));
3155
+ const refs = batchRefs.get(index2) || [];
3156
+ const targets = resolved.targets.map((target, targetIndex) => {
3157
+ var _a2;
3158
+ const path22 = (_a2 = refs[targetIndex]) == null ? void 0 : _a2.current;
3159
+ if (!path22)
3160
+ throw new Error(t("batch target no longer exists"));
3161
+ return __spreadProps(__spreadValues({}, target), { path: path22 });
3162
+ });
3163
+ const batchResult = applyResolvedBatchUpdate(
3164
+ __spreadProps(__spreadValues({}, resolved), { targets }),
3165
+ applyBatchField
3166
+ );
3167
+ result.applied += batchResult.applied;
3168
+ result.skipped += batchResult.skipped;
3169
+ result.errors.push(...batchResult.errors);
3170
+ if (batchResult.applied > 0) {
3171
+ const fallbackTarget = resolved.call.selector.root || {
3172
+ type: resolved.call.selector.types[0]
3173
+ };
3174
+ (_c = result.changes) == null ? void 0 : _c.push({
3175
+ id: `batch:${index2}`,
3176
+ label: resolved.call.selector.types.join(", "),
3177
+ description: describeAppliedChange(call),
3178
+ target: fallbackTarget
3179
+ });
3180
+ }
3181
+ return;
3182
+ }
3183
+ const path2 = (_d = targetRefs.get(index2)) == null ? void 0 : _d.current;
3184
+ if (!path2)
3185
+ throw new Error(t("target no longer exists"));
3186
+ const node = Node.get(editor, path2);
3187
+ if (!NodeUtils.isElement(node)) {
3188
+ throw new Error(t("target is no longer an element"));
3189
+ }
3190
+ const element = node;
2601
3191
  const [blockElement, blockPath] = closestBlockEntry(
2602
3192
  editor,
2603
3193
  path2
2604
3194
  ) || [element, path2];
2605
- const key2 = changeKey(blockPath, describeAppliedChange(call));
2606
- if ((_a = result.changes) == null ? void 0 : _a.some((change) => change.id === key2))
2607
- return;
2608
- (_b = result.changes) == null ? void 0 : _b.push({
2609
- id: key2,
3195
+ const appliedChange = {
3196
+ id: changeKey(blockPath, describeAppliedChange(call)),
2610
3197
  label: elementLabel(blockElement),
2611
3198
  description: describeAppliedChange(call),
2612
3199
  target: targetFromElement(blockElement, call.target),
2613
3200
  path: [...blockPath]
2614
- });
2615
- };
2616
- if (call.name === "update_text") {
2617
- if (!canReplaceWithPlainText(element)) {
2618
- throw new Error(
2619
- hasAnyTextNode(element) ? t(
2620
- "target contains mixed content; use a more specific text node"
2621
- ) : t("target has no editable text")
3201
+ };
3202
+ const recordChange = () => {
3203
+ var _a2, _b2;
3204
+ if ((_a2 = result.changes) == null ? void 0 : _a2.some((change) => change.id === appliedChange.id)) {
3205
+ return;
3206
+ }
3207
+ (_b2 = result.changes) == null ? void 0 : _b2.push(appliedChange);
3208
+ };
3209
+ if (call.name === "update_text") {
3210
+ if (!canReplaceWithPlainText(element)) {
3211
+ throw new Error(
3212
+ hasAnyTextNode(element) ? t(
3213
+ "target contains mixed content; use a more specific text node"
3214
+ ) : t("target has no editable text")
3215
+ );
3216
+ }
3217
+ setFieldValueTransactional(
3218
+ path2,
3219
+ "children",
3220
+ textToChildren(call.text)
2622
3221
  );
3222
+ result.applied += 1;
3223
+ recordChange();
3224
+ return;
2623
3225
  }
2624
- setFieldValue(path2, "children", textToChildren(call.text));
2625
- result.applied += 1;
2626
- recordChange();
2627
- return;
2628
- }
2629
- if (call.name === "update_attribute") {
2630
- if (!isSafeFieldKey(call.key))
2631
- throw new Error(t("invalid attribute key"));
2632
- if (call.key.startsWith("mobileAttributes.")) {
3226
+ if (call.name === "update_attribute") {
3227
+ if (!isSafeFieldKey(call.key))
3228
+ throw new Error(t("invalid attribute key"));
3229
+ if (call.key.startsWith("mobileAttributes.")) {
3230
+ const mobileKey = normalizeMobileAttributeKey(call.key);
3231
+ setFieldValueTransactional(
3232
+ path2,
3233
+ `mobileAttributes.${mobileKey}`,
3234
+ call.value === null ? void 0 : call.value
3235
+ );
3236
+ result.applied += 1;
3237
+ recordChange();
3238
+ return;
3239
+ }
3240
+ const attributeKey = normalizeAttributeKey(call.key);
3241
+ setFieldValueTransactional(
3242
+ path2,
3243
+ `attributes.${attributeKey}`,
3244
+ call.value === null ? void 0 : call.value
3245
+ );
3246
+ result.applied += 1;
3247
+ recordChange();
3248
+ return;
3249
+ }
3250
+ if (call.name === "update_mobile_attribute") {
3251
+ if (!isSafeFieldKey(call.key))
3252
+ throw new Error(t("invalid mobile attribute key"));
2633
3253
  const mobileKey = normalizeMobileAttributeKey(call.key);
2634
- setFieldValue(
3254
+ setFieldValueTransactional(
2635
3255
  path2,
2636
3256
  `mobileAttributes.${mobileKey}`,
2637
3257
  call.value === null ? void 0 : call.value
@@ -2640,164 +3260,182 @@ function useApplyAiToolCalls() {
2640
3260
  recordChange();
2641
3261
  return;
2642
3262
  }
2643
- const attributeKey = normalizeAttributeKey(call.key);
2644
- setFieldValue(
2645
- path2,
2646
- `attributes.${attributeKey}`,
2647
- call.value === null ? void 0 : call.value
2648
- );
2649
- result.applied += 1;
2650
- recordChange();
2651
- return;
2652
- }
2653
- if (call.name === "update_mobile_attribute") {
2654
- if (!isSafeFieldKey(call.key))
2655
- throw new Error(t("invalid mobile attribute key"));
2656
- const mobileKey = normalizeMobileAttributeKey(call.key);
2657
- setFieldValue(
2658
- path2,
2659
- `mobileAttributes.${mobileKey}`,
2660
- call.value === null ? void 0 : call.value
2661
- );
2662
- result.applied += 1;
2663
- recordChange();
2664
- return;
2665
- }
2666
- if (call.name === "update_attributes") {
2667
- if (!isObject$b(call.attributes)) {
2668
- throw new Error(t("attributes must be an object"));
3263
+ if (call.name === "update_attributes") {
3264
+ if (!isObject$b(call.attributes)) {
3265
+ throw new Error(t("attributes must be an object"));
3266
+ }
3267
+ Object.entries(call.attributes).forEach(([key2, value]) => {
3268
+ if (!isSafeFieldKey(key2)) {
3269
+ throw new Error(`${t("invalid attribute key")}: ${key2}`);
3270
+ }
3271
+ const fieldRoot = call.scope === "mobile" || key2.startsWith("mobileAttributes.") ? "mobileAttributes" : "attributes";
3272
+ const normalizedKey = fieldRoot === "mobileAttributes" ? normalizeMobileAttributeKey(key2) : normalizeAttributeKey(key2);
3273
+ setFieldValueTransactional(
3274
+ path2,
3275
+ `${fieldRoot}.${normalizedKey}`,
3276
+ value === null ? void 0 : value
3277
+ );
3278
+ });
3279
+ result.applied += 1;
3280
+ recordChange();
3281
+ return;
2669
3282
  }
2670
- Object.entries(call.attributes).forEach(([key2, value]) => {
2671
- if (!isSafeFieldKey(key2)) {
2672
- throw new Error(`${t("invalid attribute key")}: ${key2}`);
3283
+ if (call.name === "update_data") {
3284
+ if (!isSafeFieldKey(call.key))
3285
+ throw new Error(t("invalid data key"));
3286
+ setFieldValueTransactional(
3287
+ path2,
3288
+ `data.${call.key}`,
3289
+ call.value === null ? void 0 : call.value
3290
+ );
3291
+ result.applied += 1;
3292
+ recordChange();
3293
+ return;
3294
+ }
3295
+ if (call.name === "replace_children") {
3296
+ const total = countNodes(call.children);
3297
+ if (total > MAX_REPLACE_CHILDREN_NODES) {
3298
+ throw new Error(
3299
+ `${t("replace_children is too large")} (${total} ${t("nodes")})`
3300
+ );
2673
3301
  }
2674
- const fieldRoot = call.scope === "mobile" || key2.startsWith("mobileAttributes.") ? "mobileAttributes" : "attributes";
2675
- const normalizedKey = fieldRoot === "mobileAttributes" ? normalizeMobileAttributeKey(key2) : normalizeAttributeKey(key2);
2676
- setFieldValue(
3302
+ setFieldValueTransactional(
2677
3303
  path2,
2678
- `${fieldRoot}.${normalizedKey}`,
2679
- value === null ? void 0 : value
3304
+ "children",
3305
+ normalizeChildren(call.children)
2680
3306
  );
2681
- });
2682
- result.applied += 1;
2683
- recordChange();
2684
- return;
2685
- }
2686
- if (call.name === "update_data") {
2687
- if (!isSafeFieldKey(call.key))
2688
- throw new Error(t("invalid data key"));
2689
- setFieldValue(
2690
- path2,
2691
- `data.${call.key}`,
2692
- call.value === null ? void 0 : call.value
2693
- );
2694
- result.applied += 1;
2695
- recordChange();
2696
- return;
2697
- }
2698
- if (call.name === "replace_children") {
2699
- const total = countNodes(call.children);
2700
- if (total > MAX_REPLACE_CHILDREN_NODES) {
3307
+ result.applied += 1;
3308
+ recordChange();
3309
+ return;
3310
+ }
3311
+ if (call.name === "set_visibility") {
3312
+ if (call.desktop === false && call.mobile === false) {
3313
+ throw new Error(t("at least one device must remain visible"));
3314
+ }
3315
+ const visible = call.desktop === true && call.mobile === false ? "desktop" : call.desktop === false && call.mobile === true ? "mobile" : void 0;
3316
+ setFieldValueTransactional(path2, "visible", visible);
3317
+ result.applied += 1;
3318
+ recordChange();
3319
+ return;
3320
+ }
3321
+ if (call.name === "update_link") {
3322
+ if (typeof call.href !== "string")
3323
+ throw new Error(t("href is required"));
3324
+ setFieldValueTransactional(path2, "attributes.href", call.href);
3325
+ if (typeof call.blank === "boolean") {
3326
+ setFieldValueTransactional(
3327
+ path2,
3328
+ "attributes.target",
3329
+ call.blank ? "_blank" : "_self"
3330
+ );
3331
+ }
3332
+ if (typeof call.title === "string") {
3333
+ setFieldValueTransactional(
3334
+ path2,
3335
+ "attributes.title",
3336
+ call.title
3337
+ );
3338
+ }
3339
+ result.applied += 1;
3340
+ recordChange();
3341
+ return;
3342
+ }
3343
+ if (call.name === "select_node") {
3344
+ setSelectedNodePath(path2);
3345
+ result.applied += 1;
3346
+ recordChange();
3347
+ return;
3348
+ }
3349
+ if (isProtectedForStructureEdit(element)) {
2701
3350
  throw new Error(
2702
- `${t("replace_children is too large")} (${total} ${t("nodes")})`
3351
+ t("protected node cannot be structurally edited")
2703
3352
  );
2704
3353
  }
2705
- setFieldValue(path2, "children", normalizeChildren(call.children));
2706
- result.applied += 1;
2707
- recordChange();
2708
- return;
2709
- }
2710
- if (call.name === "set_visibility") {
2711
- if (call.desktop === false && call.mobile === false) {
2712
- throw new Error(t("at least one device must remain visible"));
3354
+ if (call.name === "replace_block") {
3355
+ const replacement = normalizeElementBlock(call.block);
3356
+ Transforms.removeNodes(editor, { at: path2 });
3357
+ Transforms.insertNodes(editor, replacement, { at: path2 });
3358
+ result.applied += 1;
3359
+ recordChange();
3360
+ return;
2713
3361
  }
2714
- const visible = call.desktop === true && call.mobile === false ? "desktop" : call.desktop === false && call.mobile === true ? "mobile" : void 0;
2715
- setFieldValue(path2, "visible", visible);
2716
- result.applied += 1;
2717
- recordChange();
2718
- return;
2719
- }
2720
- if (call.name === "update_link") {
2721
- if (typeof call.href !== "string")
2722
- throw new Error(t("href is required"));
2723
- setFieldValue(path2, "attributes.href", call.href);
2724
- if (typeof call.blank === "boolean") {
2725
- setFieldValue(
2726
- path2,
2727
- "attributes.target",
2728
- call.blank ? "_blank" : "_self"
3362
+ if (call.name === "move_up") {
3363
+ const previous = Path.previous(path2);
3364
+ Node.get(editor, previous);
3365
+ Transforms.moveNodes(editor, { at: path2, to: previous });
3366
+ setSelectedNodePath(previous);
3367
+ result.applied += 1;
3368
+ recordChange();
3369
+ return;
3370
+ }
3371
+ if (call.name === "move_down") {
3372
+ const next = Path.next(path2);
3373
+ Node.get(editor, next);
3374
+ Transforms.moveNodes(editor, { at: path2, to: next });
3375
+ setSelectedNodePath(next);
3376
+ result.applied += 1;
3377
+ recordChange();
3378
+ return;
3379
+ }
3380
+ if (call.name === "duplicate") {
3381
+ const next = Path.next(path2);
3382
+ Transforms.insertNodes(
3383
+ editor,
3384
+ assignIdsToElementTree(element, true),
3385
+ { at: next }
2729
3386
  );
3387
+ setSelectedNodePath(next);
3388
+ result.applied += 1;
3389
+ recordChange();
3390
+ return;
2730
3391
  }
2731
- if (typeof call.title === "string") {
2732
- setFieldValue(path2, "attributes.title", call.title);
3392
+ if (call.name === "delete_node") {
3393
+ editor.removeNode({ path: path2 });
3394
+ setSelectedNodePath(null);
3395
+ setHoverNodePath(null);
3396
+ result.applied += 1;
3397
+ recordChange();
3398
+ return;
2733
3399
  }
2734
- result.applied += 1;
2735
- recordChange();
2736
- return;
2737
- }
2738
- if (call.name === "select_node") {
2739
- setSelectedNodePath(path2);
2740
- result.applied += 1;
2741
- recordChange();
2742
- return;
2743
- }
2744
- if (isProtectedForStructureEdit(element)) {
2745
- throw new Error(t("protected node cannot be structurally edited"));
2746
- }
2747
- if (call.name === "replace_block") {
2748
- const replacement = normalizeElementBlock(call.block);
2749
- Transforms.removeNodes(editor, { at: path2 });
2750
- Transforms.insertNodes(editor, replacement, { at: path2 });
2751
- result.applied += 1;
2752
- recordChange();
2753
- return;
2754
- }
2755
- if (call.name === "move_up") {
2756
- moveUp(path2);
2757
- result.applied += 1;
2758
- recordChange();
2759
- return;
2760
- }
2761
- if (call.name === "move_down") {
2762
- moveDown(path2);
2763
- result.applied += 1;
2764
- recordChange();
2765
- return;
2766
- }
2767
- if (call.name === "duplicate") {
2768
- copyBlock(path2);
2769
- result.applied += 1;
2770
- recordChange();
2771
- return;
2772
- }
2773
- if (call.name === "delete_node") {
2774
- deleteBlock(path2);
2775
- result.applied += 1;
2776
- recordChange();
2777
- return;
2778
- }
2779
- if (call.name === "add_block_after") {
2780
- addBlock(normalizeBlock(call.block), path2, true);
2781
- result.applied += 1;
2782
- recordChange();
2783
- return;
2784
- }
2785
- if (call.name === "add_block_before") {
2786
- addBlock(normalizeBlock(call.block), path2, false);
2787
- result.applied += 1;
2788
- recordChange();
2789
- return;
2790
- }
2791
- result.skipped += 1;
2792
- result.errors.push(`${call.name}: ${t("unsupported tool")}`);
2793
- } catch (error2) {
2794
- result.skipped += 1;
2795
- result.errors.push(
2796
- `${index2 + 1}. ${call.name}: ${error2 instanceof Error ? error2.message : String(error2)}`
2797
- );
3400
+ if (call.name === "add_block_after") {
3401
+ const next = Path.next(path2);
3402
+ Transforms.insertNodes(editor, createBlockForInsert(call.block), {
3403
+ at: next
3404
+ });
3405
+ setSelectedNodePath(next);
3406
+ result.applied += 1;
3407
+ recordChange();
3408
+ return;
3409
+ }
3410
+ if (call.name === "add_block_before") {
3411
+ Transforms.insertNodes(editor, createBlockForInsert(call.block), {
3412
+ at: path2
3413
+ });
3414
+ setSelectedNodePath(path2);
3415
+ result.applied += 1;
3416
+ recordChange();
3417
+ return;
3418
+ }
3419
+ result.skipped += 1;
3420
+ result.errors.push(`${call.name}: ${t("unsupported tool")}`);
3421
+ });
3422
+ },
3423
+ {
3424
+ getExternalChange: () => subjectBefore === subjectAfter ? null : { before: subjectBefore, after: subjectAfter }
2798
3425
  }
2799
- });
2800
- });
3426
+ );
3427
+ } catch (error2) {
3428
+ syncValues(originalValues);
3429
+ return {
3430
+ applied: 0,
3431
+ skipped: calls.length,
3432
+ errors: [error2 instanceof Error ? error2.message : String(error2)],
3433
+ changes: []
3434
+ };
3435
+ } finally {
3436
+ targetRefs.forEach((ref) => ref.unref());
3437
+ batchRefs.forEach((refs) => refs.forEach((ref) => ref.unref()));
3438
+ }
2801
3439
  return result;
2802
3440
  });
2803
3441
  }
@@ -16236,6 +16874,54 @@ const HoveringToolbar = () => {
16236
16874
  editorContainer || document.body
16237
16875
  );
16238
16876
  };
16877
+ const RichTextBarContext = React__default.createContext({
16878
+ activePopup: null,
16879
+ setPopupVisible: () => void 0,
16880
+ closePopup: () => void 0
16881
+ });
16882
+ const getNextActivePopup = (activePopup, popupId, visible) => {
16883
+ if (visible)
16884
+ return popupId;
16885
+ return activePopup === popupId ? null : activePopup;
16886
+ };
16887
+ const useRichTextBarPopup = (popupId) => {
16888
+ const { activePopup, setPopupVisible } = useContext(RichTextBarContext);
16889
+ return {
16890
+ popupVisible: activePopup === popupId,
16891
+ onVisibleChange: useCallback(
16892
+ (visible) => setPopupVisible(popupId, visible),
16893
+ [popupId, setPopupVisible]
16894
+ )
16895
+ };
16896
+ };
16897
+ const useRichTextBarPopupCoordinator = () => {
16898
+ const [activePopup, setActivePopup] = useState(
16899
+ null
16900
+ );
16901
+ const setPopupVisible = useCallback(
16902
+ (popupId, visible) => {
16903
+ setActivePopup(
16904
+ (current) => getNextActivePopup(current, popupId, visible)
16905
+ );
16906
+ },
16907
+ []
16908
+ );
16909
+ const closePopup = useCallback(() => setActivePopup(null), []);
16910
+ return useMemo(
16911
+ () => ({ activePopup, setPopupVisible, closePopup }),
16912
+ [activePopup, closePopup, setPopupVisible]
16913
+ );
16914
+ };
16915
+ const useRichTextBarActionBoundary = (closePopup) => useCallback(
16916
+ (event) => {
16917
+ var _a;
16918
+ const target = event.target;
16919
+ if ((_a = target == null ? void 0 : target.closest) == null ? void 0 : _a.call(target, ".arco-trigger"))
16920
+ return;
16921
+ closePopup();
16922
+ },
16923
+ [closePopup]
16924
+ );
16239
16925
  var colorString$1 = { exports: {} };
16240
16926
  var colorName = {
16241
16927
  "aliceblue": [240, 248, 255],
@@ -18230,7 +18916,7 @@ const BgColorInner = () => {
18230
18916
  const { getPopupContainer } = useContext(RichTextBarContext);
18231
18917
  const { list: colors, addCurrentColor } = useColorContext();
18232
18918
  const { useNewColorPicker = true } = useEditorProps();
18233
- const [popupVisible, setPopupVisible] = useState(false);
18919
+ const { popupVisible, onVisibleChange: setToolbarPopupVisible } = useRichTextBarPopup("background-color");
18234
18920
  let isSelectionCollapsed = true;
18235
18921
  if (editor.selection) {
18236
18922
  try {
@@ -18355,7 +19041,7 @@ const BgColorInner = () => {
18355
19041
  if (!visible) {
18356
19042
  debouncedApplyChange.flush();
18357
19043
  }
18358
- setPopupVisible(visible);
19044
+ setToolbarPopupVisible(visible);
18359
19045
  });
18360
19046
  useEffect(() => {
18361
19047
  if (isSelectionCollapsed) {
@@ -18447,6 +19133,8 @@ const BgColorInner = () => {
18447
19133
  Popover,
18448
19134
  {
18449
19135
  trigger: "click",
19136
+ popupVisible,
19137
+ onVisibleChange,
18450
19138
  triggerProps: {
18451
19139
  popupStyle: { padding: "10px" }
18452
19140
  },
@@ -18500,7 +19188,18 @@ const BgColorInner = () => {
18500
19188
  triggerElement
18501
19189
  }
18502
19190
  ));
18503
- }, [colors, getPopupContainer, normalizedColor, onChange, onClose, onLegacyChange, onVisibleChange, pickerValue, popupVisible, useNewColorPicker]);
19191
+ }, [
19192
+ colors,
19193
+ getPopupContainer,
19194
+ normalizedColor,
19195
+ onChange,
19196
+ onClose,
19197
+ onLegacyChange,
19198
+ onVisibleChange,
19199
+ pickerValue,
19200
+ popupVisible,
19201
+ useNewColorPicker
19202
+ ]);
18504
19203
  };
18505
19204
  const BgColor = () => {
18506
19205
  return /* @__PURE__ */ React__default.createElement(ColorProvider, null, /* @__PURE__ */ React__default.createElement(BgColorInner, null));
@@ -18523,7 +19222,7 @@ const FontColorInner = () => {
18523
19222
  const { addCurrentColor, list: colors } = useColorContext();
18524
19223
  const { useNewColorPicker = true } = useEditorProps();
18525
19224
  const editor = useSlate();
18526
- const [popupVisible, setPopupVisible] = useState(false);
19225
+ const { popupVisible, onVisibleChange: setToolbarPopupVisible } = useRichTextBarPopup("font-color");
18527
19226
  let isSelectionCollapsed = true;
18528
19227
  if (editor.selection) {
18529
19228
  try {
@@ -18644,7 +19343,7 @@ const FontColorInner = () => {
18644
19343
  if (!visible) {
18645
19344
  debouncedApplyChange.flush();
18646
19345
  }
18647
- setPopupVisible(visible);
19346
+ setToolbarPopupVisible(visible);
18648
19347
  });
18649
19348
  useEffect(() => {
18650
19349
  if (isSelectionCollapsed) {
@@ -18694,35 +19393,45 @@ const FontColorInner = () => {
18694
19393
  }
18695
19394
  });
18696
19395
  return useMemo(() => {
18697
- const triggerElement = /* @__PURE__ */ React__default.createElement("span", { ref, className: classnames$1("formatButton"), title: t("Font color") }, /* @__PURE__ */ React__default.createElement(
19396
+ const triggerElement = /* @__PURE__ */ React__default.createElement(
18698
19397
  "span",
18699
19398
  {
18700
- className: classnames$1("iconfont"),
18701
- style: {
18702
- display: "flex",
18703
- alignItems: "center",
18704
- justifyContent: "center",
18705
- height: "calc(var(--hovering-bar-icon-size) + 4px)",
18706
- textAlign: "center",
18707
- fontSize: "calc(var(--hovering-bar-icon-size) + 2px)",
18708
- borderRadius: "2px",
18709
- width: "calc(var(--hovering-bar-icon-size) + 4px)",
18710
- boxSizing: "border-box",
18711
- fontWeight: "500",
18712
- color: pickerValue.toLocaleLowerCase() === "#ffffff" ? void 0 : pickerValue,
18713
- borderColor: pickerValue.toLocaleLowerCase() === "#ffffff" ? void 0 : pickerValue,
18714
- borderWidth: "1px",
18715
- borderStyle: "solid",
18716
- overflow: "hidden"
18717
- }
19399
+ ref,
19400
+ className: classnames$1("formatButton"),
19401
+ title: t("Font color")
18718
19402
  },
18719
- "A"
18720
- ));
19403
+ /* @__PURE__ */ React__default.createElement(
19404
+ "span",
19405
+ {
19406
+ className: classnames$1("iconfont"),
19407
+ style: {
19408
+ display: "flex",
19409
+ alignItems: "center",
19410
+ justifyContent: "center",
19411
+ height: "calc(var(--hovering-bar-icon-size) + 4px)",
19412
+ textAlign: "center",
19413
+ fontSize: "calc(var(--hovering-bar-icon-size) + 2px)",
19414
+ borderRadius: "2px",
19415
+ width: "calc(var(--hovering-bar-icon-size) + 4px)",
19416
+ boxSizing: "border-box",
19417
+ fontWeight: "500",
19418
+ color: pickerValue.toLocaleLowerCase() === "#ffffff" ? void 0 : pickerValue,
19419
+ borderColor: pickerValue.toLocaleLowerCase() === "#ffffff" ? void 0 : pickerValue,
19420
+ borderWidth: "1px",
19421
+ borderStyle: "solid",
19422
+ overflow: "hidden"
19423
+ }
19424
+ },
19425
+ "A"
19426
+ )
19427
+ );
18721
19428
  if (!useNewColorPicker) {
18722
19429
  return /* @__PURE__ */ React__default.createElement(
18723
19430
  Popover,
18724
19431
  {
18725
19432
  trigger: "click",
19433
+ popupVisible,
19434
+ onVisibleChange,
18726
19435
  triggerProps: {
18727
19436
  popupStyle: { padding: "10px" }
18728
19437
  },
@@ -18776,7 +19485,18 @@ const FontColorInner = () => {
18776
19485
  triggerElement
18777
19486
  }
18778
19487
  ));
18779
- }, [colors, getPopupContainer, normalizedColor, onChange, onClose, onLegacyChange, onVisibleChange, pickerValue, popupVisible, useNewColorPicker]);
19488
+ }, [
19489
+ colors,
19490
+ getPopupContainer,
19491
+ normalizedColor,
19492
+ onChange,
19493
+ onClose,
19494
+ onLegacyChange,
19495
+ onVisibleChange,
19496
+ pickerValue,
19497
+ popupVisible,
19498
+ useNewColorPicker
19499
+ ]);
18780
19500
  };
18781
19501
  const FontColor = () => {
18782
19502
  return /* @__PURE__ */ React__default.createElement(ColorProvider, null, /* @__PURE__ */ React__default.createElement(FontColorInner, null));
@@ -19178,8 +19898,9 @@ const TextLink = () => {
19178
19898
  const { getPopupContainer } = useContext(RichTextBarContext);
19179
19899
  const ref = useRef(null);
19180
19900
  const linkNode = linkNodeEntry == null ? void 0 : linkNodeEntry[0];
19901
+ const { popupVisible, onVisibleChange } = useRichTextBarPopup("link");
19181
19902
  const selectedNodePath = useSelectedNodePath();
19182
- const [popupVisible, setPopupVisible] = useState(false);
19903
+ const [mergeTagPopupVisible, setMergeTagPopupVisible] = useState(false);
19183
19904
  const { handleUploadClick, showSelectFileButton, onUpload, mergetags } = useEditorProps();
19184
19905
  const [isUploading, setIsUploading] = useState(false);
19185
19906
  const uploadHandlerRef = useRef(onUpload);
@@ -19192,7 +19913,7 @@ const TextLink = () => {
19192
19913
  const newHref = PluginManager.generateVariable(mergetag);
19193
19914
  from.setFieldValue("href", newHref);
19194
19915
  setFormState((old) => __spreadProps(__spreadValues({}, old), { href: newHref }));
19195
- setPopupVisible(false);
19916
+ setMergeTagPopupVisible(false);
19196
19917
  },
19197
19918
  [from]
19198
19919
  );
@@ -19287,6 +20008,8 @@ const TextLink = () => {
19287
20008
  Popover,
19288
20009
  {
19289
20010
  trigger: "click",
20011
+ popupVisible,
20012
+ onVisibleChange,
19290
20013
  triggerProps: {
19291
20014
  position: "tr",
19292
20015
  popupStyle: { padding: "10px 0px 0px 0px", width: 400 }
@@ -19333,10 +20056,10 @@ const TextLink = () => {
19333
20056
  SharedComponents.MergeTagComponent,
19334
20057
  {
19335
20058
  mergetags: formatMergetags,
19336
- popupVisible,
19337
- onVisibleChange: setPopupVisible,
20059
+ popupVisible: mergeTagPopupVisible,
20060
+ onVisibleChange: setMergeTagPopupVisible,
19338
20061
  onChange: onChangeMergeTag,
19339
- onClose: () => setPopupVisible(false),
20062
+ onClose: () => setMergeTagPopupVisible(false),
19340
20063
  value: "",
19341
20064
  triggerElement: /* @__PURE__ */ React__default.createElement(
19342
20065
  Button$2,
@@ -19474,6 +20197,7 @@ const options$9 = [
19474
20197
  ];
19475
20198
  const TurnInto = () => {
19476
20199
  const editor = useSlate();
20200
+ const { popupVisible, onVisibleChange } = useRichTextBarPopup("turn-into");
19477
20201
  let nodeEntry = null;
19478
20202
  try {
19479
20203
  if (editor.selection && Node.has(editor, editor.selection.anchor.path)) {
@@ -19510,6 +20234,8 @@ const TurnInto = () => {
19510
20234
  Select$1,
19511
20235
  {
19512
20236
  value: node.type,
20237
+ popupVisible,
20238
+ onVisibleChange,
19513
20239
  className: "easy-email-pro-turnInto",
19514
20240
  style: { minWidth: 95 },
19515
20241
  onChange,
@@ -19529,7 +20255,7 @@ const TurnInto = () => {
19529
20255
  };
19530
20256
  const RichTextBar$1 = "";
19531
20257
  const MergeTag = React__default.memo(() => {
19532
- const [popupVisible, setPopupVisible] = useState(false);
20258
+ const { popupVisible, onVisibleChange } = useRichTextBarPopup("merge-tag");
19533
20259
  const [mergeTagRef, setMergeTagRef] = useState(null);
19534
20260
  const editor = useSlate();
19535
20261
  const { mergetags } = useEditorProps();
@@ -19541,19 +20267,19 @@ const MergeTag = React__default.memo(() => {
19541
20267
  editor.insertMergetag({
19542
20268
  mergetag
19543
20269
  });
19544
- setPopupVisible(false);
20270
+ onVisibleChange(false);
19545
20271
  },
19546
- [editor]
20272
+ [editor, onVisibleChange]
19547
20273
  );
19548
20274
  const onClose = useCallback(() => {
19549
- setPopupVisible(false);
19550
- }, []);
20275
+ onVisibleChange(false);
20276
+ }, [onVisibleChange]);
19551
20277
  return /* @__PURE__ */ React__default.createElement("div", { ref: setMergeTagRef }, /* @__PURE__ */ React__default.createElement(
19552
20278
  SharedComponents.MergeTagComponent,
19553
20279
  {
19554
20280
  mergetags: formatMergetags,
19555
20281
  popupVisible,
19556
- onVisibleChange: setPopupVisible,
20282
+ onVisibleChange,
19557
20283
  onChange,
19558
20284
  onClose,
19559
20285
  value: "",
@@ -19563,7 +20289,7 @@ const MergeTag = React__default.memo(() => {
19563
20289
  {
19564
20290
  title: t("Merge Tag"),
19565
20291
  icon: "icon-merge-tags",
19566
- onClick: () => setPopupVisible(false)
20292
+ onClick: () => onVisibleChange(false)
19567
20293
  }
19568
20294
  )
19569
20295
  }
@@ -19720,6 +20446,7 @@ const TextAlign$1 = () => {
19720
20446
  };
19721
20447
  const options$8 = ["12px", "14px", "16px", "18px", "24px", "32px", "48px"];
19722
20448
  const FontSize$2 = () => {
20449
+ const { popupVisible, onVisibleChange } = useRichTextBarPopup("font-size");
19723
20450
  const [selectRef, setSelectRef] = useState(null);
19724
20451
  const { fontSizeList } = useEditorProps();
19725
20452
  const [fontSizeValue, setFontSizeValue] = useState();
@@ -19832,6 +20559,8 @@ const FontSize$2 = () => {
19832
20559
  {
19833
20560
  className: "easy-email-pro-font-size",
19834
20561
  value: fontSizeValue,
20562
+ popupVisible,
20563
+ onVisibleChange,
19835
20564
  style: { width: 80 },
19836
20565
  onChange,
19837
20566
  triggerProps: {
@@ -19849,6 +20578,7 @@ const FontSize$2 = () => {
19849
20578
  `));
19850
20579
  };
19851
20580
  const FontFamily$2 = () => {
20581
+ const { popupVisible, onVisibleChange } = useRichTextBarPopup("font-family");
19852
20582
  const { fontList, addFont } = useFontFamily();
19853
20583
  const [selectRef, setSelectRef] = useState(null);
19854
20584
  const editor = useSlate();
@@ -19951,6 +20681,8 @@ const FontFamily$2 = () => {
19951
20681
  {
19952
20682
  className: "easy-email-pro-font-family",
19953
20683
  value: fontFamilyValue,
20684
+ popupVisible,
20685
+ onVisibleChange,
19954
20686
  style: { minWidth: 80 },
19955
20687
  onChange,
19956
20688
  triggerProps: {
@@ -20063,6 +20795,7 @@ const Image$4 = () => {
20063
20795
  const editor = useSlate();
20064
20796
  const ref = useRef(null);
20065
20797
  const { onUpload } = useEditorProps();
20798
+ const { popupVisible, onVisibleChange } = useRichTextBarPopup("image");
20066
20799
  const { lock } = useEditorState();
20067
20800
  const [from] = Form.useForm();
20068
20801
  const onSubmit = (values) => {
@@ -20101,6 +20834,8 @@ const Image$4 = () => {
20101
20834
  Popover,
20102
20835
  {
20103
20836
  trigger: "click",
20837
+ popupVisible,
20838
+ onVisibleChange,
20104
20839
  triggerProps: {
20105
20840
  position: "bl",
20106
20841
  popupStyle: { padding: "0px" }
@@ -20201,15 +20936,16 @@ const Image$4 = () => {
20201
20936
  /* @__PURE__ */ React__default.createElement("span", { ref, className: classnames$1("formatButton") }, /* @__PURE__ */ React__default.createElement("span", { className: classnames$1(" iconfont icon-img") }))
20202
20937
  );
20203
20938
  };
20204
- const RichTextBarContext = React__default.createContext({});
20205
20939
  const RichTextBar = ({
20206
20940
  list,
20207
20941
  wrap: wrap2,
20208
20942
  getPopupContainer
20209
20943
  }) => {
20944
+ const { activePopup, setPopupVisible, closePopup } = useRichTextBarPopupCoordinator();
20945
+ const onClickCapture = useRichTextBarActionBoundary(closePopup);
20210
20946
  const contextValue = useMemo(
20211
- () => ({ getPopupContainer }),
20212
- [getPopupContainer]
20947
+ () => ({ getPopupContainer, activePopup, setPopupVisible, closePopup }),
20948
+ [activePopup, closePopup, getPopupContainer, setPopupVisible]
20213
20949
  );
20214
20950
  const { MergetagPopover: MergetagPopover2, mergetags, AIAssistant: AIAssistant2 } = useEditorProps();
20215
20951
  const { selectedNode } = useSelectedNode();
@@ -20315,6 +21051,7 @@ const RichTextBar = ({
20315
21051
  {
20316
21052
  id: "RichTextBar",
20317
21053
  className: "RichTextBar",
21054
+ onClickCapture,
20318
21055
  style: {
20319
21056
  display: "inline-flex",
20320
21057
  alignItems: "center",
@@ -26220,14 +26957,14 @@ var getNative$1 = _getNative, root$2 = _root;
26220
26957
  var Set$2 = getNative$1(root$2, "Set");
26221
26958
  var _Set = Set$2;
26222
26959
  var getNative = _getNative, root$1 = _root;
26223
- var WeakMap$1 = getNative(root$1, "WeakMap");
26224
- var _WeakMap = WeakMap$1;
26225
- var DataView = _DataView, Map$1 = _Map, Promise$1 = _Promise, Set$1 = _Set, WeakMap = _WeakMap, baseGetTag$1 = _baseGetTag, toSource = _toSource;
26960
+ var WeakMap$2 = getNative(root$1, "WeakMap");
26961
+ var _WeakMap = WeakMap$2;
26962
+ var DataView = _DataView, Map$1 = _Map, Promise$1 = _Promise, Set$1 = _Set, WeakMap$1 = _WeakMap, baseGetTag$1 = _baseGetTag, toSource = _toSource;
26226
26963
  var mapTag$3 = "[object Map]", objectTag$2 = "[object Object]", promiseTag = "[object Promise]", setTag$3 = "[object Set]", weakMapTag$1 = "[object WeakMap]";
26227
26964
  var dataViewTag$2 = "[object DataView]";
26228
- var dataViewCtorString = toSource(DataView), mapCtorString = toSource(Map$1), promiseCtorString = toSource(Promise$1), setCtorString = toSource(Set$1), weakMapCtorString = toSource(WeakMap);
26965
+ var dataViewCtorString = toSource(DataView), mapCtorString = toSource(Map$1), promiseCtorString = toSource(Promise$1), setCtorString = toSource(Set$1), weakMapCtorString = toSource(WeakMap$1);
26229
26966
  var getTag$3 = baseGetTag$1;
26230
- if (DataView && getTag$3(new DataView(new ArrayBuffer(1))) != dataViewTag$2 || Map$1 && getTag$3(new Map$1()) != mapTag$3 || Promise$1 && getTag$3(Promise$1.resolve()) != promiseTag || Set$1 && getTag$3(new Set$1()) != setTag$3 || WeakMap && getTag$3(new WeakMap()) != weakMapTag$1) {
26967
+ if (DataView && getTag$3(new DataView(new ArrayBuffer(1))) != dataViewTag$2 || Map$1 && getTag$3(new Map$1()) != mapTag$3 || Promise$1 && getTag$3(Promise$1.resolve()) != promiseTag || Set$1 && getTag$3(new Set$1()) != setTag$3 || WeakMap$1 && getTag$3(new WeakMap$1()) != weakMapTag$1) {
26231
26968
  getTag$3 = function(value) {
26232
26969
  var result = baseGetTag$1(value), Ctor = result == objectTag$2 ? value.constructor : void 0, ctorString = Ctor ? toSource(Ctor) : "";
26233
26970
  if (ctorString) {
@@ -40280,6 +41017,7 @@ const AIPromptModal = (props) => {
40280
41017
  };
40281
41018
  const SvgMagic = (props) => /* @__PURE__ */ React$2.createElement("svg", __spreadValues({ className: "bi bi-magic", fill: "currentColor", height: 16, viewBox: "0 0 16 16", width: 16, xmlns: "http://www.w3.org/2000/svg" }, props), /* @__PURE__ */ React$2.createElement("path", { d: "M9.5 2.672a.5.5 0 1 0 1 0V.843a.5.5 0 0 0-1 0v1.829Zm4.5.035A.5.5 0 0 0 13.293 2L12 3.293a.5.5 0 1 0 .707.707L14 2.707ZM7.293 4A.5.5 0 1 0 8 3.293L6.707 2A.5.5 0 0 0 6 2.707L7.293 4Zm-.621 2.5a.5.5 0 1 0 0-1H4.843a.5.5 0 1 0 0 1h1.829Zm8.485 0a.5.5 0 1 0 0-1h-1.829a.5.5 0 0 0 0 1h1.829ZM13.293 10A.5.5 0 1 0 14 9.293L12.707 8a.5.5 0 1 0-.707.707L13.293 10ZM9.5 11.157a.5.5 0 0 0 1 0V9.328a.5.5 0 0 0-1 0v1.829Zm1.854-5.097a.5.5 0 0 0 0-.706l-.708-.708a.5.5 0 0 0-.707 0L8.646 5.94a.5.5 0 0 0 0 .707l.708.708a.5.5 0 0 0 .707 0l1.293-1.293Zm-3 3a.5.5 0 0 0 0-.706l-.708-.708a.5.5 0 0 0-.707 0L.646 13.94a.5.5 0 0 0 0 .707l.708.708a.5.5 0 0 0 .707 0L8.354 9.06Z" }));
40282
41019
  function AIAssistant({ isCollapsed }) {
41020
+ const { popupVisible, onVisibleChange } = useRichTextBarPopup("ai-assistant");
40283
41021
  const isEnabled = EditorAuth.getFeatureEnabled("AI_ASSISTANT");
40284
41022
  if (!isEnabled) {
40285
41023
  throw new Error(`Current plan do not support AIAssistant`);
@@ -40539,6 +41277,8 @@ function AIAssistant({ isCollapsed }) {
40539
41277
  return /* @__PURE__ */ React__default.createElement("div", { ref: setRefElement, style: { display: "flex", alignItems: "center" } }, /* @__PURE__ */ React__default.createElement(
40540
41278
  Select$1,
40541
41279
  {
41280
+ popupVisible,
41281
+ onVisibleChange,
40542
41282
  getPopupContainer: (node) => {
40543
41283
  return Array.from(document.querySelectorAll(".RichTextBar")).find(
40544
41284
  (item2) => item2.contains(node)
@@ -41921,6 +42661,15 @@ function EasyEmailProAiAgent({
41921
42661
  "×"
41922
42662
  ), /* @__PURE__ */ React__default.createElement("img", { src: previewAttachment.url, alt: previewAttachment.name, onClick: (event) => event.stopPropagation() })) : null);
41923
42663
  }
42664
+ function formatAiApplyResult(result, summary) {
42665
+ const lines = summary ? [summary] : [t("Updated the email preview.")];
42666
+ lines.push(
42667
+ result.skipped > 0 ? `${t("Updated")} ${result.applied} ${t("blocks")}; ${t("skipped")} ${result.skipped}.` : `${t("Updated")} ${result.applied} ${t("blocks")}.`
42668
+ );
42669
+ if (result.errors.length)
42670
+ lines.push(result.errors.join("\n"));
42671
+ return lines.join("\n\n");
42672
+ }
41924
42673
  const doneStatuses = ["FINISHED", "ERROR"];
41925
42674
  function getInitialConversation() {
41926
42675
  return [
@@ -42234,18 +42983,13 @@ function useAiAgentSession(options2) {
42234
42983
  ((_a = result == null ? void 0 : result.errors) == null ? void 0 : _a.filter(Boolean)) || []
42235
42984
  );
42236
42985
  updateMessage(activeRun.assistantMessageId, {
42237
- content: errors.length ? `${payload.summary || t("Some local changes were not applied.")}
42238
-
42239
- ${t(
42240
- "Some operations were not applied"
42241
- )}:
42242
- ${errors.join("\n")}` : toolCallsText(__spreadProps(__spreadValues({}, payload), { toolCalls }), result),
42986
+ content: result ? formatAiApplyResult(__spreadProps(__spreadValues({}, result), { errors }), payload.summary) : toolCallsText(__spreadProps(__spreadValues({}, payload), { toolCalls }), result),
42243
42987
  status: "ready"
42244
42988
  });
42245
42989
  setActivity(
42246
42990
  errors.length ? t("Some local changes were not applied") : t("Updated the email preview.")
42247
42991
  );
42248
- if ((result == null ? void 0 : result.applied) === void 0 || result.applied > 0) {
42992
+ if (!result || result.applied > 0) {
42249
42993
  scheduleAfterApplySnapshot(
42250
42994
  activeRun.assistantMessageId,
42251
42995
  payload.summary