@puckeditor/plugin-ai 0.8.3-canary.fcc3f680 → 0.8.3

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 (3) hide show
  1. package/dist/index.js +478 -214
  2. package/dist/index.mjs +478 -214
  3. package/package.json +7 -7
package/dist/index.mjs CHANGED
@@ -2510,7 +2510,6 @@ function ExamplePrompt({
2510
2510
  // src/components/Chat/index.tsx
2511
2511
  var import_reducer = __toESM(require_dist());
2512
2512
  import qler from "qler";
2513
- import html2canvas from "html2canvas-pro";
2514
2513
  import { useDebouncedCallback } from "use-debounce";
2515
2514
 
2516
2515
  // src/lib/scroll-tracking-events.ts
@@ -2582,147 +2581,16 @@ import {
2582
2581
 
2583
2582
  // src/lib/morph-html.ts
2584
2583
  init_react_import();
2585
- var PROTECTED_ATTRS = /* @__PURE__ */ new Set([
2586
- "contenteditable",
2587
- "data-puck-overlay-portal"
2588
- ]);
2589
- var SLOT_OWNED_ATTRS = /* @__PURE__ */ new Set(["style", "class"]);
2590
- var isEventHandlerAttr = (name) => /^on/i.test(name);
2591
- var cssEscape = (value) => typeof CSS !== "undefined" && CSS.escape ? CSS.escape(value) : value.replace(/["\\]/g, "\\$&");
2592
- var FIELD_ATTR_PREFIX = "data-puck-field-";
2593
- var hasJsonFieldBinding = (el) => {
2594
- const attrBoundNames = /* @__PURE__ */ new Set();
2595
- const jsonFieldNames = [];
2596
- for (const attr of Array.from(el.attributes)) {
2597
- if (!attr.name.startsWith(FIELD_ATTR_PREFIX)) continue;
2598
- if (attr.name.startsWith("data-puck-field-value-")) continue;
2599
- if (attr.value.trimStart().startsWith("{")) {
2600
- const fieldName = attr.name.slice(FIELD_ATTR_PREFIX.length);
2601
- if (fieldName && fieldName !== "name") {
2602
- jsonFieldNames.push(fieldName);
2603
- }
2604
- } else {
2605
- const fieldName = attr.value.trim();
2606
- if (fieldName) attrBoundNames.add(fieldName);
2607
- }
2608
- }
2609
- return jsonFieldNames.some((name) => !attrBoundNames.has(name));
2610
- };
2611
- var ownsItsChildren = (el) => el.hasAttribute("data-puck-slot") || el.hasAttribute("data-puck-field-name") || el.hasAttribute("data-puck-array") || hasJsonFieldBinding(el);
2612
- var syncAttributes = (target, source) => {
2613
- const isSlot = target.hasAttribute("data-puck-slot");
2614
- for (const attr of Array.from(target.attributes)) {
2615
- if (PROTECTED_ATTRS.has(attr.name)) continue;
2616
- if (isSlot && SLOT_OWNED_ATTRS.has(attr.name)) continue;
2617
- if (isEventHandlerAttr(attr.name) || !source.hasAttribute(attr.name)) {
2618
- target.removeAttribute(attr.name);
2619
- }
2620
- }
2621
- for (const attr of Array.from(source.attributes)) {
2622
- if (PROTECTED_ATTRS.has(attr.name)) continue;
2623
- if (isSlot && SLOT_OWNED_ATTRS.has(attr.name)) continue;
2624
- if (isEventHandlerAttr(attr.name)) continue;
2625
- if (target.getAttribute(attr.name) !== attr.value) {
2626
- target.setAttribute(attr.name, attr.value);
2627
- }
2628
- }
2629
- };
2630
- var isSameKind = (a, b) => a.nodeType === b.nodeType && (a.nodeType !== Node.ELEMENT_NODE || a.tagName === b.tagName);
2631
- var isIncompleteTagArtifact = (node) => node.nodeType === Node.TEXT_NODE && /^<\/?[a-zA-Z]*$/.test(node.nodeValue ?? "");
2632
- var stripIncompleteTagArtifacts = (root) => {
2633
- for (const child of Array.from(root.childNodes)) {
2634
- if (isIncompleteTagArtifact(child)) {
2635
- root.removeChild(child);
2636
- } else if (child.nodeType === Node.ELEMENT_NODE) {
2637
- stripIncompleteTagArtifacts(child);
2638
- }
2639
- }
2640
- };
2641
- var stripEventHandlerAttrs = (root) => {
2642
- root.querySelectorAll("*").forEach((el) => {
2643
- for (const attr of Array.from(el.attributes)) {
2644
- if (isEventHandlerAttr(attr.name)) el.removeAttribute(attr.name);
2645
- }
2646
- });
2647
- };
2648
- var morphChildren = (target, source, skipChildrenOf = ownsItsChildren) => {
2649
- const sourceChildren = Array.from(source.childNodes);
2650
- for (let i = 0; i < sourceChildren.length; i++) {
2651
- const sourceChild = sourceChildren[i];
2652
- const targetChild = target.childNodes[i];
2653
- if (!targetChild) {
2654
- target.appendChild(sourceChild);
2655
- continue;
2656
- }
2657
- if (!isSameKind(targetChild, sourceChild)) {
2658
- target.replaceChild(sourceChild, targetChild);
2659
- continue;
2660
- }
2661
- if (sourceChild.nodeType !== Node.ELEMENT_NODE) {
2662
- if (targetChild.nodeValue !== sourceChild.nodeValue) {
2663
- targetChild.nodeValue = sourceChild.nodeValue;
2664
- }
2665
- continue;
2666
- }
2667
- const targetEl = targetChild;
2668
- syncAttributes(targetEl, sourceChild);
2669
- if (!skipChildrenOf(targetEl)) {
2670
- morphChildren(targetEl, sourceChild, skipChildrenOf);
2671
- }
2672
- }
2673
- while (target.childNodes.length > sourceChildren.length) {
2674
- target.removeChild(target.lastChild);
2675
- }
2676
- };
2677
- function morphHtml(container, html, attrOverrides = [], skipChildrenOf = ownsItsChildren) {
2678
- const template = container.ownerDocument.createElement("template");
2679
- template.innerHTML = html;
2680
- stripIncompleteTagArtifacts(template.content);
2681
- stripEventHandlerAttrs(template.content);
2682
- for (const { name, attr, value } of attrOverrides) {
2683
- template.content.querySelectorAll(`[data-puck-field-${attr}="${cssEscape(name)}"]`).forEach((el) => {
2684
- if (el.closest("[data-puck-array]")) return;
2685
- el.setAttribute(attr, value);
2686
- });
2687
- }
2688
- morphChildren(container, template.content, skipChildrenOf);
2689
- }
2690
2584
 
2691
- // src/lib/design/observer-guard.ts
2585
+ // src/lib/design/annotations.ts
2692
2586
  init_react_import();
2693
- var wrapScriptWithObserverGuard = (script) => {
2694
- if (!script.includes("MutationObserver")) return script;
2695
- return `(function () {
2696
- var __NativeMutationObserver = window.MutationObserver;
2697
- function MutationObserver(callback) {
2698
- var observed = [];
2699
- var inner = new __NativeMutationObserver(function (records) {
2700
- inner.disconnect();
2701
- try {
2702
- callback(records, inner);
2703
- } finally {
2704
- for (var i = 0; i < observed.length; i++) {
2705
- __nativeObserve.call(inner, observed[i][0], observed[i][1]);
2706
- }
2707
- }
2708
- });
2709
- var __nativeObserve = inner.observe;
2710
- inner.observe = function (target, options) {
2711
- observed.push([target, options]);
2712
- return __nativeObserve.call(inner, target, options);
2713
- };
2714
- return inner;
2715
- }
2716
- ${script}
2717
- })();`;
2718
- };
2719
2587
 
2720
2588
  // src/lib/html-annotations.ts
2721
2589
  init_react_import();
2722
2590
  import { parseDocument } from "htmlparser2";
2723
2591
  import { appendChild, getInnerHTML, getOuterHTML, textContent } from "domutils";
2724
2592
  import { Text } from "domhandler";
2725
- var FIELD_ATTR_PREFIX2 = "data-puck-field-";
2593
+ var FIELD_ATTR_PREFIX = "data-puck-field-";
2726
2594
  var OPTIONS_ATTR_PREFIX = "data-puck-options-";
2727
2595
  var BIND_ATTR_PREFIX = "data-puck-bind-";
2728
2596
  var BIND_INPUT_ATTR_PREFIX = "data-puck-bind-input-";
@@ -2770,6 +2638,19 @@ var textContentWithBreaks = (node) => {
2770
2638
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2771
2639
  var FIELD_NAME_RE = /^[A-Za-z0-9_]+(?:[-.][A-Za-z0-9_]+)*$/;
2772
2640
  var isValidFieldName = (name) => FIELD_NAME_RE.test(name);
2641
+ var isFieldNameClaimed = (name, claimed, includeExact = true) => {
2642
+ for (const entry of claimed) {
2643
+ if (includeExact && entry === name || entry.startsWith(`${name}.`) || name.startsWith(`${entry}.`)) {
2644
+ return true;
2645
+ }
2646
+ }
2647
+ return false;
2648
+ };
2649
+ var attrBoundFieldNames = (attribs) => new Set(
2650
+ Object.entries(attribs).filter(
2651
+ ([name, value]) => name.startsWith(FIELD_ATTR_PREFIX) && name !== `${FIELD_ATTR_PREFIX}name` && !name.startsWith(`${FIELD_ATTR_PREFIX}value-`) && !tryParseFieldShape(value)
2652
+ ).map(([, value]) => value.trim()).filter(isValidFieldName)
2653
+ );
2773
2654
  var getDeepValue = (source, path) => {
2774
2655
  if (!path.includes(".")) return source[path];
2775
2656
  let current = source;
@@ -2870,6 +2751,7 @@ var collectElementFields = (element, acc) => {
2870
2751
  return true;
2871
2752
  }
2872
2753
  let skipChildren = false;
2754
+ const attrBound = attrBoundFieldNames(attribs);
2873
2755
  for (const [attrName, attrValue] of Object.entries(attribs)) {
2874
2756
  if (attrName.startsWith(OPTIONS_ATTR_PREFIX)) {
2875
2757
  const fieldName = attrName.slice(OPTIONS_ATTR_PREFIX.length);
@@ -2898,8 +2780,8 @@ var collectElementFields = (element, acc) => {
2898
2780
  }
2899
2781
  continue;
2900
2782
  }
2901
- if (!attrName.startsWith(FIELD_ATTR_PREFIX2)) continue;
2902
- const target = attrName.slice(FIELD_ATTR_PREFIX2.length);
2783
+ if (!attrName.startsWith(FIELD_ATTR_PREFIX)) continue;
2784
+ const target = attrName.slice(FIELD_ATTR_PREFIX.length);
2903
2785
  if (!target) continue;
2904
2786
  const shape = tryParseFieldShape(attrValue);
2905
2787
  if (shape) {
@@ -2913,6 +2795,9 @@ var collectElementFields = (element, acc) => {
2913
2795
  if (existing) existing.shape = shape;
2914
2796
  continue;
2915
2797
  }
2798
+ if (isFieldNameClaimed(name2, attrBound, false) || isFieldNameClaimed(name2, acc.seenFields)) {
2799
+ continue;
2800
+ }
2916
2801
  acc.seenFields.add(name2);
2917
2802
  if (shape.type === "richtext") {
2918
2803
  acc.fields.push({ name: name2, binding: "text", shape, format: "json" });
@@ -2967,6 +2852,9 @@ var collectElementFields = (element, acc) => {
2967
2852
  }
2968
2853
  continue;
2969
2854
  }
2855
+ if (target === "name" && isFieldNameClaimed(name, attrBound) || isFieldNameClaimed(name, acc.seenFields)) {
2856
+ continue;
2857
+ }
2970
2858
  acc.seenFields.add(name);
2971
2859
  const fieldType = getTypeModifier(attribs, name);
2972
2860
  const constraints = fieldType === "number" ? getNumberConstraints(attribs, name) : {};
@@ -3060,7 +2948,7 @@ var parseHtmlAnnotations = (html) => {
3060
2948
  if (slotName && isValidFieldName(slotName) && !seenSlots.has(slotName)) {
3061
2949
  seenSlots.add(slotName);
3062
2950
  const shape = tryParseFieldShape(
3063
- attribs[FIELD_ATTR_PREFIX2 + slotName.toLowerCase()] ?? ""
2951
+ attribs[FIELD_ATTR_PREFIX + slotName.toLowerCase()] ?? ""
3064
2952
  );
3065
2953
  const allow = shape?.type === "slot" ? sanitizeStringArray(shape.allow) : void 0;
3066
2954
  const disallow = shape?.type === "slot" ? sanitizeStringArray(shape.disallow) : void 0;
@@ -3209,21 +3097,16 @@ var patchElement = (element, props) => {
3209
3097
  }
3210
3098
  return { didPatch, skipChildren: true };
3211
3099
  }
3212
- const attrBoundFields = /* @__PURE__ */ new Set();
3213
- for (const [an, av] of Object.entries(attribs)) {
3214
- if (an.startsWith(FIELD_ATTR_PREFIX2) && an !== `${FIELD_ATTR_PREFIX2}name` && !tryParseFieldShape(av)) {
3215
- attrBoundFields.add(av.trim());
3216
- }
3217
- }
3100
+ const attrBoundFields = attrBoundFieldNames(attribs);
3218
3101
  for (const [attrName, attrValue] of Object.entries(attribs)) {
3219
- if (!attrName.startsWith(FIELD_ATTR_PREFIX2)) continue;
3220
- const target = attrName.slice(FIELD_ATTR_PREFIX2.length);
3102
+ if (!attrName.startsWith(FIELD_ATTR_PREFIX)) continue;
3103
+ const target = attrName.slice(FIELD_ATTR_PREFIX.length);
3221
3104
  if (!target) continue;
3222
3105
  const shape = tryParseFieldShape(attrValue);
3223
3106
  if (shape) {
3224
3107
  if (shape.type === "slot") continue;
3225
3108
  const name2 = target;
3226
- if (attrBoundFields.has(name2)) continue;
3109
+ if (isFieldNameClaimed(name2, attrBoundFields)) continue;
3227
3110
  const value2 = getDeepValue(props, name2);
3228
3111
  if (shape.type === "richtext") {
3229
3112
  if (typeof value2 !== "string") continue;
@@ -3241,6 +3124,9 @@ var patchElement = (element, props) => {
3241
3124
  }
3242
3125
  const name = attrValue.trim();
3243
3126
  if (!name) continue;
3127
+ if (target === "name" && isFieldNameClaimed(name, attrBoundFields)) {
3128
+ continue;
3129
+ }
3244
3130
  const value = getDeepValue(props, name);
3245
3131
  if (target === "name" && getTypeModifier(attribs, name) === "richtext") {
3246
3132
  if (typeof value !== "string") continue;
@@ -3313,7 +3199,6 @@ var renderHtmlArrayItems = (html, arrayName, items) => {
3313
3199
  };
3314
3200
 
3315
3201
  // src/lib/design/annotations.ts
3316
- init_react_import();
3317
3202
  var cache = /* @__PURE__ */ new Map();
3318
3203
  var MAX_CACHE_ENTRIES = 32;
3319
3204
  var getAnnotations = (html) => {
@@ -3330,6 +3215,160 @@ var getSlotFields = (html) => Object.fromEntries(
3330
3215
  getAnnotations(html).slots.map((slot) => [slot.name, toSlotField(slot)])
3331
3216
  );
3332
3217
 
3218
+ // src/lib/morph-html.ts
3219
+ var PROTECTED_ATTRS = /* @__PURE__ */ new Set([
3220
+ "contenteditable",
3221
+ "data-puck-overlay-portal"
3222
+ ]);
3223
+ var SLOT_OWNED_ATTRS = /* @__PURE__ */ new Set(["style", "class"]);
3224
+ var isEventHandlerAttr = (name) => /^on/i.test(name);
3225
+ var FIELD_ATTR_PREFIX2 = "data-puck-field-";
3226
+ var LEGACY_TEXT_ATTR = `${FIELD_ATTR_PREFIX2}name`;
3227
+ var VALUE_ATTR_PREFIX = `${FIELD_ATTR_PREFIX2}value-`;
3228
+ var hasTextBinding = (el, parsedTextFields) => {
3229
+ for (const attr of Array.from(el.attributes)) {
3230
+ if (!attr.name.startsWith(FIELD_ATTR_PREFIX2)) continue;
3231
+ if (attr.name.startsWith(VALUE_ATTR_PREFIX)) continue;
3232
+ const isShape = attr.value.trimStart().startsWith("{");
3233
+ const fieldName = isShape ? attr.name.slice(FIELD_ATTR_PREFIX2.length) : attr.value.trim();
3234
+ if (isValidFieldName(fieldName) && (attr.name === LEGACY_TEXT_ATTR || isShape) && (!parsedTextFields || parsedTextFields.has(fieldName))) {
3235
+ return true;
3236
+ }
3237
+ }
3238
+ return false;
3239
+ };
3240
+ var ownsItsChildren = (el, parsedTextFields) => el.hasAttribute("data-puck-slot") || el.hasAttribute("data-puck-array") || hasTextBinding(el, parsedTextFields);
3241
+ var syncAttributes = (target, source) => {
3242
+ const isSlot = target.hasAttribute("data-puck-slot");
3243
+ for (const attr of Array.from(target.attributes)) {
3244
+ if (PROTECTED_ATTRS.has(attr.name)) continue;
3245
+ if (isSlot && SLOT_OWNED_ATTRS.has(attr.name)) continue;
3246
+ if (isEventHandlerAttr(attr.name) || !source.hasAttribute(attr.name)) {
3247
+ target.removeAttribute(attr.name);
3248
+ }
3249
+ }
3250
+ for (const attr of Array.from(source.attributes)) {
3251
+ if (PROTECTED_ATTRS.has(attr.name)) continue;
3252
+ if (isSlot && SLOT_OWNED_ATTRS.has(attr.name)) continue;
3253
+ if (isEventHandlerAttr(attr.name)) continue;
3254
+ if (target.getAttribute(attr.name) !== attr.value) {
3255
+ target.setAttribute(attr.name, attr.value);
3256
+ }
3257
+ }
3258
+ };
3259
+ var isSameKind = (a, b) => a.nodeType === b.nodeType && (a.nodeType !== Node.ELEMENT_NODE || a.tagName === b.tagName);
3260
+ var isIncompleteTagArtifact = (node) => node.nodeType === Node.TEXT_NODE && /^<\/?[a-zA-Z]*$/.test(node.nodeValue ?? "");
3261
+ var stripIncompleteTagArtifacts = (root) => {
3262
+ for (const child of Array.from(root.childNodes)) {
3263
+ if (isIncompleteTagArtifact(child)) {
3264
+ root.removeChild(child);
3265
+ } else if (child.nodeType === Node.ELEMENT_NODE) {
3266
+ stripIncompleteTagArtifacts(child);
3267
+ }
3268
+ }
3269
+ };
3270
+ var stripEventHandlerAttrs = (root) => {
3271
+ root.querySelectorAll("*").forEach((el) => {
3272
+ for (const attr of Array.from(el.attributes)) {
3273
+ if (isEventHandlerAttr(attr.name)) el.removeAttribute(attr.name);
3274
+ }
3275
+ });
3276
+ };
3277
+ var morphChildren = (target, source, skipChildrenOf = ownsItsChildren) => {
3278
+ const sourceChildren = Array.from(source.childNodes);
3279
+ for (let i = 0; i < sourceChildren.length; i++) {
3280
+ const sourceChild = sourceChildren[i];
3281
+ const targetChild = target.childNodes[i];
3282
+ if (!targetChild) {
3283
+ target.appendChild(sourceChild);
3284
+ continue;
3285
+ }
3286
+ if (!isSameKind(targetChild, sourceChild)) {
3287
+ target.replaceChild(sourceChild, targetChild);
3288
+ continue;
3289
+ }
3290
+ if (sourceChild.nodeType !== Node.ELEMENT_NODE) {
3291
+ if (targetChild.nodeValue !== sourceChild.nodeValue) {
3292
+ targetChild.nodeValue = sourceChild.nodeValue;
3293
+ }
3294
+ continue;
3295
+ }
3296
+ const targetEl = targetChild;
3297
+ syncAttributes(targetEl, sourceChild);
3298
+ if (!skipChildrenOf(targetEl)) {
3299
+ morphChildren(targetEl, sourceChild, skipChildrenOf);
3300
+ }
3301
+ }
3302
+ while (target.childNodes.length > sourceChildren.length) {
3303
+ target.removeChild(target.lastChild);
3304
+ }
3305
+ };
3306
+ function morphHtml(container, html, attrOverrides = [], skipChildrenOf) {
3307
+ const template = container.ownerDocument.createElement("template");
3308
+ template.innerHTML = html;
3309
+ stripIncompleteTagArtifacts(template.content);
3310
+ stripEventHandlerAttrs(template.content);
3311
+ if (attrOverrides.length > 0) {
3312
+ const overridesByAttr = /* @__PURE__ */ new Map();
3313
+ for (const override of attrOverrides) {
3314
+ if (isEventHandlerAttr(override.attr)) continue;
3315
+ const bindingAttr = `${FIELD_ATTR_PREFIX2}${override.attr}`;
3316
+ const fields = overridesByAttr.get(bindingAttr) ?? /* @__PURE__ */ new Map();
3317
+ fields.set(override.name, override);
3318
+ overridesByAttr.set(bindingAttr, fields);
3319
+ }
3320
+ template.content.querySelectorAll("*").forEach((el) => {
3321
+ for (const attr of Array.from(el.attributes)) {
3322
+ const override = overridesByAttr.get(attr.name)?.get(attr.value.trim());
3323
+ if (!override) continue;
3324
+ if (el.closest("[data-puck-array]")) return;
3325
+ el.setAttribute(override.attr, override.value);
3326
+ }
3327
+ });
3328
+ }
3329
+ if (skipChildrenOf) {
3330
+ morphChildren(container, template.content, skipChildrenOf);
3331
+ return;
3332
+ }
3333
+ const parsedTextFields = new Set(
3334
+ getAnnotations(html).fields.filter((field) => field.binding === "text").map((field) => field.name)
3335
+ );
3336
+ morphChildren(
3337
+ container,
3338
+ template.content,
3339
+ (el) => ownsItsChildren(el, parsedTextFields)
3340
+ );
3341
+ }
3342
+
3343
+ // src/lib/design/observer-guard.ts
3344
+ init_react_import();
3345
+ var wrapScriptWithObserverGuard = (script) => {
3346
+ if (!script.includes("MutationObserver")) return script;
3347
+ return `(function () {
3348
+ var __NativeMutationObserver = window.MutationObserver;
3349
+ function MutationObserver(callback) {
3350
+ var observed = [];
3351
+ var inner = new __NativeMutationObserver(function (records) {
3352
+ inner.disconnect();
3353
+ try {
3354
+ callback(records, inner);
3355
+ } finally {
3356
+ for (var i = 0; i < observed.length; i++) {
3357
+ __nativeObserve.call(inner, observed[i][0], observed[i][1]);
3358
+ }
3359
+ }
3360
+ });
3361
+ var __nativeObserve = inner.observe;
3362
+ inner.observe = function (target, options) {
3363
+ observed.push([target, options]);
3364
+ return __nativeObserve.call(inner, target, options);
3365
+ };
3366
+ return inner;
3367
+ }
3368
+ ${script}
3369
+ })();`;
3370
+ };
3371
+
3333
3372
  // src/lib/design/registration-store.ts
3334
3373
  init_react_import();
3335
3374
  var RegistrationStore = class {
@@ -3403,7 +3442,7 @@ init_react_import();
3403
3442
  import { registerOverlayPortal, setDeep } from "@puckeditor/core";
3404
3443
  var CONTENT_EDITABLE_ATTR = "data-puck-content-editable";
3405
3444
  var isInlineEditingDisabled = (el) => el.closest(`[${CONTENT_EDITABLE_ATTR}="false"]`) !== null;
3406
- var cssEscape2 = (value) => typeof CSS !== "undefined" && CSS.escape ? CSS.escape(value) : value.replace(/["\\]/g, "\\$&");
3445
+ var cssEscape = (value) => typeof CSS !== "undefined" && CSS.escape ? CSS.escape(value) : value.replace(/["\\]/g, "\\$&");
3407
3446
  var queryScopedFields = (entry, selector) => Array.from(entry.querySelectorAll(selector)).filter((el) => {
3408
3447
  if (el.closest("[data-puck-array]")) return false;
3409
3448
  const closestSlot = el.closest("[data-puck-slot]");
@@ -3411,7 +3450,7 @@ var queryScopedFields = (entry, selector) => Array.from(entry.querySelectorAll(s
3411
3450
  return true;
3412
3451
  });
3413
3452
  var findOwnArrayContainer = (entry, arrayName) => {
3414
- const selector = `[data-puck-array="${cssEscape2(arrayName)}"]`;
3453
+ const selector = `[data-puck-array="${cssEscape(arrayName)}"]`;
3415
3454
  for (const el of entry.querySelectorAll(selector)) {
3416
3455
  const closestSlot = el.closest("[data-puck-slot]");
3417
3456
  if (!closestSlot || !entry.contains(closestSlot)) return el;
@@ -3426,7 +3465,7 @@ var inlineEditModeFor = (field) => {
3426
3465
  const type = field.shape?.type ?? field.fieldType;
3427
3466
  return type === "richtext" ? "richtext" : type === "number" ? "number" : "text";
3428
3467
  };
3429
- var textFieldSelector = (field) => field.format === "json" ? `[${cssEscape2(`data-puck-field-${field.name}`)}]` : `[data-puck-field-name="${cssEscape2(field.name)}"]`;
3468
+ var textFieldSelector = (field) => field.format === "json" ? `[${cssEscape(`data-puck-field-${field.name}`)}]` : `[data-puck-field-name="${cssEscape(field.name)}"]`;
3430
3469
  var isHTMLElement = (el) => {
3431
3470
  const HTMLElementCtor = el.ownerDocument.defaultView?.HTMLElement;
3432
3471
  return !!HTMLElementCtor && el instanceof HTMLElementCtor;
@@ -3452,6 +3491,29 @@ var syncFieldValues = (entry, fields, props) => {
3452
3491
  });
3453
3492
  }
3454
3493
  };
3494
+ var claimedHostStyles = /* @__PURE__ */ new WeakMap();
3495
+ var claimHostStyles = (el, applyWhiteSpace) => {
3496
+ if (!claimedHostStyles.has(el)) {
3497
+ claimedHostStyles.set(el, {
3498
+ cursor: el.style.cursor,
3499
+ whiteSpace: applyWhiteSpace ? el.style.whiteSpace : null
3500
+ });
3501
+ }
3502
+ applyHostStyles(el);
3503
+ };
3504
+ var applyHostStyles = (el) => {
3505
+ const claimed = claimedHostStyles.get(el);
3506
+ if (!claimed) return;
3507
+ el.style.cursor = "text";
3508
+ if (claimed.whiteSpace !== null) el.style.whiteSpace = "pre-wrap";
3509
+ };
3510
+ var releaseHostStyles = (el) => {
3511
+ const claimed = claimedHostStyles.get(el);
3512
+ if (!claimed) return;
3513
+ el.style.cursor = claimed.cursor;
3514
+ if (claimed.whiteSpace !== null) el.style.whiteSpace = claimed.whiteSpace;
3515
+ claimedHostStyles.delete(el);
3516
+ };
3455
3517
  var attachInlineEditing = (el, {
3456
3518
  componentId,
3457
3519
  propPath,
@@ -3464,8 +3526,7 @@ var attachInlineEditing = (el, {
3464
3526
  const syncEditable = () => {
3465
3527
  el.contentEditable = hovering || focused ? mode === "richtext" ? "true" : "plaintext-only" : "false";
3466
3528
  };
3467
- const previousCursor = el.style.cursor;
3468
- el.style.cursor = "text";
3529
+ claimHostStyles(el, mode !== "richtext");
3469
3530
  syncEditable();
3470
3531
  let lastValidNumberText = el.innerText.replaceAll(/\n/gm, "");
3471
3532
  const handleInput = () => {
@@ -3543,7 +3604,7 @@ var attachInlineEditing = (el, {
3543
3604
  el.removeEventListener("mouseout", handleMouseOut, true);
3544
3605
  el.removeEventListener("focus", handleFocus);
3545
3606
  el.removeEventListener("blur", handleBlur);
3546
- el.style.cursor = previousCursor;
3607
+ releaseHostStyles(el);
3547
3608
  el.removeAttribute("contenteditable");
3548
3609
  cleanupPortal?.();
3549
3610
  };
@@ -3609,7 +3670,7 @@ var wireArrayItemEditing = ({
3609
3670
  const mode = inlineEditModeFor(field);
3610
3671
  const registration = wiring.get(target);
3611
3672
  if (registration?.propPath === propPath && registration.mode === mode) {
3612
- target.style.cursor = "text";
3673
+ applyHostStyles(target);
3613
3674
  continue;
3614
3675
  }
3615
3676
  registration?.cleanup();
@@ -3751,7 +3812,7 @@ var SlotPortal = ({
3751
3812
  [slotStyle]
3752
3813
  );
3753
3814
  useEffect10(() => {
3754
- const el = entry.querySelector(`[data-puck-slot="${cssEscape2(slotName)}"]`);
3815
+ const el = entry.querySelector(`[data-puck-slot="${cssEscape(slotName)}"]`);
3755
3816
  if (el !== lastTarget.current) {
3756
3817
  el?.replaceChildren();
3757
3818
  lastTarget.current = el;
@@ -3935,7 +3996,7 @@ function createDesignComponentConfig(registration) {
3935
3996
  morphed.current = { el: entryEl, html, attrSig };
3936
3997
  morphHtml(entryEl, html, attrFields);
3937
3998
  for (const el of fieldWiring.current.keys()) {
3938
- if (el.isConnected) el.style.cursor = "text";
3999
+ if (el.isConnected) applyHostStyles(el);
3939
4000
  }
3940
4001
  }
3941
4002
  syncFieldValues(entryEl, annotations.fields, props);
@@ -4094,9 +4155,11 @@ function getDynamicConfigGlobals(dynamicConfig) {
4094
4155
  ...typeof script === "string" ? { script } : {}
4095
4156
  };
4096
4157
  }
4158
+ var INLINE_EDIT_RESET = ":where([data-puck-design] [contenteditable]){overflow-wrap:inherit;line-break:inherit;-webkit-nbsp-mode:inherit}";
4097
4159
  function DesignGlobals({
4098
4160
  styles,
4099
- script
4161
+ script,
4162
+ isEditing
4100
4163
  }) {
4101
4164
  const scriptRef = useRef12(null);
4102
4165
  const hostRef = useRef12(null);
@@ -4114,6 +4177,7 @@ function DesignGlobals({
4114
4177
  };
4115
4178
  }, [script]);
4116
4179
  return /* @__PURE__ */ jsxs19(Fragment8, { children: [
4180
+ isEditing && /* @__PURE__ */ jsx33("style", { children: INLINE_EDIT_RESET }),
4117
4181
  styles && /* @__PURE__ */ jsx33("style", { children: styles }),
4118
4182
  script && /* @__PURE__ */ jsx33("div", { ref: hostRef, style: { display: "none" } })
4119
4183
  ] });
@@ -4126,7 +4190,14 @@ function withDesignGlobals(root) {
4126
4190
  const render = (props) => {
4127
4191
  const { styles, script } = getDynamicConfigGlobals(props._dynamicConfig);
4128
4192
  return /* @__PURE__ */ jsxs19(Fragment8, { children: [
4129
- /* @__PURE__ */ jsx33(DesignGlobals, { styles, script }),
4193
+ /* @__PURE__ */ jsx33(
4194
+ DesignGlobals,
4195
+ {
4196
+ styles,
4197
+ script,
4198
+ isEditing: props.puck?.isEditing
4199
+ }
4200
+ ),
4130
4201
  userRender ? userRender(props) : props.children
4131
4202
  ] });
4132
4203
  };
@@ -4392,12 +4463,145 @@ var waitForIframeReady = async (root, imageTimeout = 1e4) => {
4392
4463
  await waitForNextPaint(document2);
4393
4464
  };
4394
4465
 
4466
+ // src/lib/capture-screenshot.ts
4467
+ init_react_import();
4468
+ import html2canvas from "html2canvas-pro";
4469
+
4470
+ // src/lib/screenshot-size.ts
4471
+ init_react_import();
4472
+ var MAX_WEBP_DIMENSION = 16383;
4473
+ var CAPTURE_SCALE = 2;
4474
+ var getFitRatio = (width, height, dimensionLimit = MAX_WEBP_DIMENSION) => {
4475
+ if (width <= 0 || height <= 0) return 1;
4476
+ return Math.min(1, dimensionLimit / Math.max(width, height));
4477
+ };
4478
+ var getCaptureScale = (width, height, scale) => scale * getFitRatio(width * scale, height * scale);
4479
+ var scaleDimension = (value, ratio) => Math.max(1, Math.floor(value * ratio));
4480
+ var getDocumentSize = (root) => {
4481
+ const { body, documentElement } = root.ownerDocument;
4482
+ return {
4483
+ width: Math.max(
4484
+ body?.scrollWidth ?? 0,
4485
+ body?.offsetWidth ?? 0,
4486
+ body?.clientWidth ?? 0,
4487
+ documentElement?.scrollWidth ?? 0,
4488
+ documentElement?.offsetWidth ?? 0,
4489
+ documentElement?.clientWidth ?? 0
4490
+ ),
4491
+ height: Math.max(
4492
+ body?.scrollHeight ?? 0,
4493
+ body?.offsetHeight ?? 0,
4494
+ body?.clientHeight ?? 0,
4495
+ documentElement?.scrollHeight ?? 0,
4496
+ documentElement?.offsetHeight ?? 0,
4497
+ documentElement?.clientHeight ?? 0
4498
+ )
4499
+ };
4500
+ };
4501
+
4502
+ // src/lib/capture-screenshot.ts
4503
+ var editorOnlyClassPrefixes = ["_DraggableComponent--hover", "_ActionBar"];
4504
+ var releaseCanvas = (canvas) => {
4505
+ canvas.width = 0;
4506
+ canvas.height = 0;
4507
+ };
4508
+ var drawScaled = (source, ratio) => {
4509
+ const target = document.createElement("canvas");
4510
+ target.width = scaleDimension(source.width, ratio);
4511
+ target.height = scaleDimension(source.height, ratio);
4512
+ const ctx2 = target.getContext("2d");
4513
+ if (!ctx2) {
4514
+ releaseCanvas(target);
4515
+ return source;
4516
+ }
4517
+ ctx2.imageSmoothingEnabled = true;
4518
+ ctx2.imageSmoothingQuality = "high";
4519
+ ctx2.drawImage(source, 0, 0, target.width, target.height);
4520
+ return target;
4521
+ };
4522
+ var fitCanvas = (canvas) => {
4523
+ let remaining = getFitRatio(canvas.width, canvas.height);
4524
+ if (remaining >= 1) return canvas;
4525
+ let current = canvas;
4526
+ while (remaining < 0.5) {
4527
+ const next = drawScaled(current, 0.5);
4528
+ if (current !== canvas) releaseCanvas(current);
4529
+ current = next;
4530
+ remaining *= 2;
4531
+ }
4532
+ const fitted = drawScaled(current, remaining);
4533
+ if (current !== canvas) releaseCanvas(current);
4534
+ return fitted;
4535
+ };
4536
+ var toWebp = (canvas, quality) => new Promise((resolve, reject) => {
4537
+ canvas.toBlob(
4538
+ (blob) => {
4539
+ if (blob && blob.size > 0) {
4540
+ resolve(blob);
4541
+ return;
4542
+ }
4543
+ reject(
4544
+ new Error(
4545
+ `Could not encode a ${canvas.width}x${canvas.height} canvas as WebP`
4546
+ )
4547
+ );
4548
+ },
4549
+ "image/webp",
4550
+ quality
4551
+ );
4552
+ });
4553
+ var captureScreenshot = async (root, { scale = CAPTURE_SCALE, quality = 0.8, width } = {}) => {
4554
+ const documentSize = getDocumentSize(root);
4555
+ const safeWebPScale = getCaptureScale(
4556
+ width ?? documentSize.width,
4557
+ documentSize.height,
4558
+ scale
4559
+ );
4560
+ const canvas = await html2canvas(root, {
4561
+ scale: safeWebPScale,
4562
+ backgroundColor: "#ffffff",
4563
+ ...width === void 0 ? {} : { width, windowWidth: width },
4564
+ foreignObjectRendering: false,
4565
+ imageTimeout: 3e4,
4566
+ logging: false,
4567
+ allowTaint: false,
4568
+ useCORS: true,
4569
+ scrollY: 0,
4570
+ ignoreElements: (el) => Array.from(el.classList).some(
4571
+ (c) => editorOnlyClassPrefixes.some((prefix) => c.startsWith(prefix))
4572
+ )
4573
+ });
4574
+ const fitted = fitCanvas(canvas);
4575
+ try {
4576
+ return await toWebp(fitted, quality);
4577
+ } finally {
4578
+ if (fitted !== canvas) releaseCanvas(fitted);
4579
+ releaseCanvas(canvas);
4580
+ }
4581
+ };
4582
+
4583
+ // src/lib/client-tool-response.ts
4584
+ init_react_import();
4585
+ async function postClientToolResponse(host, body) {
4586
+ const response = await fetch(`${host.replace(/\/+$/, "")}/tool`, {
4587
+ method: "POST",
4588
+ headers: { "Content-Type": "application/json" },
4589
+ body: JSON.stringify(body)
4590
+ });
4591
+ if (!response.ok) {
4592
+ throw new Error(
4593
+ `Client tool endpoint returned ${response.status} ${response.statusText}`.trim()
4594
+ );
4595
+ }
4596
+ }
4597
+
4395
4598
  // src/components/Chat/index.tsx
4396
4599
  import { Fragment as Fragment9, jsx as jsx35, jsxs as jsxs20 } from "react/jsx-runtime";
4397
4600
  var q = qler();
4398
4601
  var DEFAULT_API_VERSION = "v2";
4399
4602
  var BUILD_OP_DEBOUNCE_MS = 1e3 / 60;
4400
- var PLUGIN_AI_VERSION = true ? "0.8.3-canary.fcc3f680" : "unknown";
4603
+ var CLIENT_TOOL_ENDPOINT_UNAVAILABLE_ERROR_MESSAGE = "Unable to verify the page data. Please enable the `/chat/tool` endpoint on the server and try again.";
4604
+ var PLUGIN_AI_VERSION = true ? "0.8.3" : "unknown";
4401
4605
  var BENCHMARK = false;
4402
4606
  var prefixedUlid = (prefix = "") => `${prefix ? `${prefix}_` : ""}${ulid()}`;
4403
4607
  var getClassName18 = getClassNameFactory("Chat", styles_module_default);
@@ -4435,10 +4639,14 @@ function Chat2(props) {
4435
4639
  }, [puckData, getPuck]);
4436
4640
  const [error, setError] = useState16();
4437
4641
  const [userError, setUserError] = useState16();
4642
+ const clientToolEndpointUnavailableRef = useRef13(false);
4643
+ const stopChatRef = useRef13(null);
4438
4644
  const [composerAttachments, setComposerAttachments] = useState16([]);
4439
4645
  const removedAttachmentClientIdsRef = useRef13(/* @__PURE__ */ new Set());
4440
4646
  const uploadControllersRef = useRef13(/* @__PURE__ */ new Map());
4441
4647
  const pendingBuildOpsRef = useRef13([]);
4648
+ const lastStreamActivityRef = useRef13(0);
4649
+ const hiddenSinceRef = useRef13(null);
4442
4650
  const [toolStatus, setToolStatus] = useState16({});
4443
4651
  const [subagentState, setSubagentState] = useState16({});
4444
4652
  const attachmentConfig = useMemo6(
@@ -4478,24 +4686,7 @@ function Chat2(props) {
4478
4686
  throw new Error("Preview frame not found");
4479
4687
  }
4480
4688
  await waitForIframeReady(iframeDocument);
4481
- const canvas = await html2canvas(iframeDocument, {
4482
- scale: 2,
4483
- backgroundColor: "#ffffff",
4484
- width,
4485
- windowWidth: width,
4486
- foreignObjectRendering: false,
4487
- imageTimeout: 3e4,
4488
- logging: false,
4489
- allowTaint: false,
4490
- useCORS: true,
4491
- scrollY: 0,
4492
- ignoreElements: (el) => Array.from(el.classList).some(
4493
- (c) => c.startsWith("_DraggableComponent--hover") || c.startsWith("_ActionBar")
4494
- )
4495
- });
4496
- const image = canvas.toDataURL("image/webp", 0.8);
4497
- const imageResponse = await fetch(image);
4498
- const blob = await imageResponse.blob();
4689
+ const blob = await captureScreenshot(iframeDocument, { width });
4499
4690
  const uploadResponse = await fetch(bucketUrl, {
4500
4691
  method: "PUT",
4501
4692
  body: blob
@@ -4549,8 +4740,43 @@ function Chat2(props) {
4549
4740
  },
4550
4741
  [debouncedFlushBuildOps]
4551
4742
  );
4743
+ const handleClientToolEndpointUnavailable = useCallback6(() => {
4744
+ if (clientToolEndpointUnavailableRef.current) return;
4745
+ clientToolEndpointUnavailableRef.current = true;
4746
+ setError(CLIENT_TOOL_ENDPOINT_UNAVAILABLE_ERROR_MESSAGE);
4747
+ console.error(CLIENT_TOOL_ENDPOINT_UNAVAILABLE_ERROR_MESSAGE);
4748
+ setToolStatus((current) => {
4749
+ let changed = false;
4750
+ const next = {};
4751
+ for (const [id, toolState] of Object.entries(current)) {
4752
+ if (toolState.loading) {
4753
+ changed = true;
4754
+ next[id] = {
4755
+ ...toolState,
4756
+ loading: false,
4757
+ label: "Verification unavailable"
4758
+ };
4759
+ } else {
4760
+ next[id] = toolState;
4761
+ }
4762
+ }
4763
+ return changed ? next : current;
4764
+ });
4765
+ stopChatRef.current?.();
4766
+ }, []);
4767
+ const sendClientToolResponse = useCallback6(
4768
+ async (body) => {
4769
+ try {
4770
+ await postClientToolResponse(host, body);
4771
+ } catch {
4772
+ handleClientToolEndpointUnavailable();
4773
+ }
4774
+ },
4775
+ [handleClientToolEndpointUnavailable, host]
4776
+ );
4552
4777
  const processData = useCallback6(
4553
4778
  (dataPart) => {
4779
+ lastStreamActivityRef.current = Date.now();
4554
4780
  switch (dataPart.type) {
4555
4781
  case "data-new-chat-created": {
4556
4782
  localChatId.current = dataPart.data.chatId;
@@ -4581,13 +4807,9 @@ function Chat2(props) {
4581
4807
  debouncedFlushBuildOps.flush();
4582
4808
  q.wait().then(() => {
4583
4809
  const { appState } = getPuck();
4584
- return fetch(`${host}/tool`, {
4585
- method: "POST",
4586
- headers: { "Content-Type": "application/json" },
4587
- body: JSON.stringify({
4588
- id: requestId,
4589
- responses: [{ action, output: appState.data }]
4590
- })
4810
+ return sendClientToolResponse({
4811
+ id: requestId,
4812
+ responses: [{ action, output: appState.data }]
4591
4813
  });
4592
4814
  }).catch((e) => {
4593
4815
  console.error("Failed to respond to client request:", e);
@@ -4604,22 +4826,7 @@ function Chat2(props) {
4604
4826
  throw new Error("Preview frame not found");
4605
4827
  }
4606
4828
  await waitForIframeReady(iframeDocument);
4607
- const canvas = await html2canvas(iframeDocument, {
4608
- scale: 2,
4609
- backgroundColor: "#ffffff",
4610
- foreignObjectRendering: false,
4611
- imageTimeout: 3e4,
4612
- logging: false,
4613
- allowTaint: false,
4614
- useCORS: true,
4615
- scrollY: 0,
4616
- ignoreElements: (el) => Array.from(el.classList).some(
4617
- (c) => c.startsWith("_DraggableComponent--hover") || c.startsWith("_ActionBar")
4618
- )
4619
- });
4620
- const image = canvas.toDataURL("image/webp", 0.8);
4621
- const imageResponse = await fetch(image);
4622
- const blob = await imageResponse.blob();
4829
+ const blob = await captureScreenshot(iframeDocument);
4623
4830
  const uploadResponse = await fetch(putUrl, {
4624
4831
  method: "PUT",
4625
4832
  body: blob
@@ -4629,16 +4836,16 @@ function Chat2(props) {
4629
4836
  `Upload failed with status ${uploadResponse.status}`
4630
4837
  );
4631
4838
  }
4632
- return fetch(`${host}/tool`, {
4633
- method: "POST",
4634
- headers: { "Content-Type": "application/json" },
4635
- body: JSON.stringify({
4636
- id: requestId,
4637
- responses: [{ action, output: { sizeBytes: blob.size } }]
4638
- })
4839
+ return sendClientToolResponse({
4840
+ id: requestId,
4841
+ responses: [{ action, output: { sizeBytes: blob.size } }]
4639
4842
  });
4640
4843
  }).catch((e) => {
4641
4844
  console.error("Failed to respond to client request:", e);
4845
+ return sendClientToolResponse({
4846
+ id: requestId,
4847
+ responses: [{ action, output: { sizeBytes: 0 } }]
4848
+ });
4642
4849
  });
4643
4850
  }
4644
4851
  return;
@@ -4682,6 +4889,7 @@ function Chat2(props) {
4682
4889
  getPuck,
4683
4890
  puckDispatch,
4684
4891
  queueBuildOp,
4892
+ sendClientToolResponse,
4685
4893
  uploadScreenshot
4686
4894
  ]
4687
4895
  );
@@ -4751,12 +4959,14 @@ function Chat2(props) {
4751
4959
  if (BENCHMARK) {
4752
4960
  console.timeEnd("chat");
4753
4961
  }
4754
- setError(e.message);
4962
+ if (!clientToolEndpointUnavailableRef.current) {
4963
+ setError(e.message);
4964
+ }
4755
4965
  },
4756
4966
  onFinish: (options) => {
4757
4967
  debouncedFlushBuildOps.flush();
4758
4968
  q.wait().then(() => {
4759
- if (!options.isAbort) {
4969
+ if (!options.isAbort || clientToolEndpointUnavailableRef.current) {
4760
4970
  puckDispatch({
4761
4971
  type: "set",
4762
4972
  state: getPuck().appState,
@@ -4771,12 +4981,63 @@ function Chat2(props) {
4771
4981
  });
4772
4982
  }
4773
4983
  });
4774
- useEffect13(
4775
- () => () => {
4984
+ useEffect13(() => {
4985
+ stopChatRef.current = stop;
4986
+ return () => {
4987
+ stopChatRef.current = null;
4776
4988
  stop();
4777
- },
4778
- [stop]
4779
- );
4989
+ };
4990
+ }, [stop]);
4991
+ const statusRef = useRef13(status);
4992
+ useEffect13(() => {
4993
+ statusRef.current = status;
4994
+ }, [status]);
4995
+ useEffect13(() => {
4996
+ lastStreamActivityRef.current = Date.now();
4997
+ }, [messages]);
4998
+ useEffect13(() => {
4999
+ const FROZEN_HIDDEN_MS = 3 * 6e4;
5000
+ const RESUME_GRACE_MS = 12e3;
5001
+ const isStreaming = () => statusRef.current === "streaming" || statusRef.current === "submitted";
5002
+ let graceTimer;
5003
+ const recoverIfSevered = () => {
5004
+ if (!isStreaming()) return;
5005
+ if (Date.now() - lastStreamActivityRef.current < RESUME_GRACE_MS) return;
5006
+ stop();
5007
+ setToolStatus((current) => {
5008
+ let changed = false;
5009
+ const next = {};
5010
+ for (const [id, toolState] of Object.entries(current)) {
5011
+ if (toolState.loading) {
5012
+ changed = true;
5013
+ next[id] = { ...toolState, loading: false, label: "Interrupted" };
5014
+ } else {
5015
+ next[id] = toolState;
5016
+ }
5017
+ }
5018
+ return changed ? next : current;
5019
+ });
5020
+ };
5021
+ const onVisibilityChange = () => {
5022
+ if (document.hidden) {
5023
+ hiddenSinceRef.current = isStreaming() ? Date.now() : null;
5024
+ return;
5025
+ }
5026
+ const hiddenSince = hiddenSinceRef.current;
5027
+ hiddenSinceRef.current = null;
5028
+ if (graceTimer) clearTimeout(graceTimer);
5029
+ if (hiddenSince === null || Date.now() - hiddenSince < FROZEN_HIDDEN_MS) {
5030
+ return;
5031
+ }
5032
+ lastStreamActivityRef.current = 0;
5033
+ graceTimer = setTimeout(recoverIfSevered, RESUME_GRACE_MS);
5034
+ };
5035
+ document.addEventListener("visibilitychange", onVisibilityChange);
5036
+ return () => {
5037
+ document.removeEventListener("visibilitychange", onVisibilityChange);
5038
+ if (graceTimer) clearTimeout(graceTimer);
5039
+ };
5040
+ }, [stop]);
4780
5041
  const [forcedStatus, setForcedStatus] = useState16();
4781
5042
  const resolvedStatus = useMemo6(
4782
5043
  () => forcedStatus ?? status,
@@ -4827,6 +5088,8 @@ function Chat2(props) {
4827
5088
  if (!text && readyAttachments.length === 0) {
4828
5089
  return false;
4829
5090
  }
5091
+ clientToolEndpointUnavailableRef.current = false;
5092
+ setError("");
4830
5093
  setUserError("");
4831
5094
  if (chat?.onSubmit) {
4832
5095
  try {
@@ -5066,6 +5329,7 @@ function Chat2(props) {
5066
5329
  ),
5067
5330
  error,
5068
5331
  handleRetry: () => {
5332
+ clientToolEndpointUnavailableRef.current = false;
5069
5333
  setError("");
5070
5334
  regenerate();
5071
5335
  },