@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.js CHANGED
@@ -2490,7 +2490,6 @@ function ExamplePrompt({
2490
2490
  // src/components/Chat/index.tsx
2491
2491
  var import_qler = __toESM(require("qler"));
2492
2492
  var import_reducer = __toESM(require_dist());
2493
- var import_html2canvas_pro = __toESM(require("html2canvas-pro"));
2494
2493
  var import_use_debounce = require("use-debounce");
2495
2494
 
2496
2495
  // src/lib/scroll-tracking-events.ts
@@ -2550,147 +2549,16 @@ var import_react24 = require("react");
2550
2549
 
2551
2550
  // src/lib/morph-html.ts
2552
2551
  init_react_import();
2553
- var PROTECTED_ATTRS = /* @__PURE__ */ new Set([
2554
- "contenteditable",
2555
- "data-puck-overlay-portal"
2556
- ]);
2557
- var SLOT_OWNED_ATTRS = /* @__PURE__ */ new Set(["style", "class"]);
2558
- var isEventHandlerAttr = (name) => /^on/i.test(name);
2559
- var cssEscape = (value) => typeof CSS !== "undefined" && CSS.escape ? CSS.escape(value) : value.replace(/["\\]/g, "\\$&");
2560
- var FIELD_ATTR_PREFIX = "data-puck-field-";
2561
- var hasJsonFieldBinding = (el) => {
2562
- const attrBoundNames = /* @__PURE__ */ new Set();
2563
- const jsonFieldNames = [];
2564
- for (const attr of Array.from(el.attributes)) {
2565
- if (!attr.name.startsWith(FIELD_ATTR_PREFIX)) continue;
2566
- if (attr.name.startsWith("data-puck-field-value-")) continue;
2567
- if (attr.value.trimStart().startsWith("{")) {
2568
- const fieldName = attr.name.slice(FIELD_ATTR_PREFIX.length);
2569
- if (fieldName && fieldName !== "name") {
2570
- jsonFieldNames.push(fieldName);
2571
- }
2572
- } else {
2573
- const fieldName = attr.value.trim();
2574
- if (fieldName) attrBoundNames.add(fieldName);
2575
- }
2576
- }
2577
- return jsonFieldNames.some((name) => !attrBoundNames.has(name));
2578
- };
2579
- var ownsItsChildren = (el) => el.hasAttribute("data-puck-slot") || el.hasAttribute("data-puck-field-name") || el.hasAttribute("data-puck-array") || hasJsonFieldBinding(el);
2580
- var syncAttributes = (target, source) => {
2581
- const isSlot = target.hasAttribute("data-puck-slot");
2582
- for (const attr of Array.from(target.attributes)) {
2583
- if (PROTECTED_ATTRS.has(attr.name)) continue;
2584
- if (isSlot && SLOT_OWNED_ATTRS.has(attr.name)) continue;
2585
- if (isEventHandlerAttr(attr.name) || !source.hasAttribute(attr.name)) {
2586
- target.removeAttribute(attr.name);
2587
- }
2588
- }
2589
- for (const attr of Array.from(source.attributes)) {
2590
- if (PROTECTED_ATTRS.has(attr.name)) continue;
2591
- if (isSlot && SLOT_OWNED_ATTRS.has(attr.name)) continue;
2592
- if (isEventHandlerAttr(attr.name)) continue;
2593
- if (target.getAttribute(attr.name) !== attr.value) {
2594
- target.setAttribute(attr.name, attr.value);
2595
- }
2596
- }
2597
- };
2598
- var isSameKind = (a, b) => a.nodeType === b.nodeType && (a.nodeType !== Node.ELEMENT_NODE || a.tagName === b.tagName);
2599
- var isIncompleteTagArtifact = (node) => node.nodeType === Node.TEXT_NODE && /^<\/?[a-zA-Z]*$/.test(node.nodeValue ?? "");
2600
- var stripIncompleteTagArtifacts = (root) => {
2601
- for (const child of Array.from(root.childNodes)) {
2602
- if (isIncompleteTagArtifact(child)) {
2603
- root.removeChild(child);
2604
- } else if (child.nodeType === Node.ELEMENT_NODE) {
2605
- stripIncompleteTagArtifacts(child);
2606
- }
2607
- }
2608
- };
2609
- var stripEventHandlerAttrs = (root) => {
2610
- root.querySelectorAll("*").forEach((el) => {
2611
- for (const attr of Array.from(el.attributes)) {
2612
- if (isEventHandlerAttr(attr.name)) el.removeAttribute(attr.name);
2613
- }
2614
- });
2615
- };
2616
- var morphChildren = (target, source, skipChildrenOf = ownsItsChildren) => {
2617
- const sourceChildren = Array.from(source.childNodes);
2618
- for (let i = 0; i < sourceChildren.length; i++) {
2619
- const sourceChild = sourceChildren[i];
2620
- const targetChild = target.childNodes[i];
2621
- if (!targetChild) {
2622
- target.appendChild(sourceChild);
2623
- continue;
2624
- }
2625
- if (!isSameKind(targetChild, sourceChild)) {
2626
- target.replaceChild(sourceChild, targetChild);
2627
- continue;
2628
- }
2629
- if (sourceChild.nodeType !== Node.ELEMENT_NODE) {
2630
- if (targetChild.nodeValue !== sourceChild.nodeValue) {
2631
- targetChild.nodeValue = sourceChild.nodeValue;
2632
- }
2633
- continue;
2634
- }
2635
- const targetEl = targetChild;
2636
- syncAttributes(targetEl, sourceChild);
2637
- if (!skipChildrenOf(targetEl)) {
2638
- morphChildren(targetEl, sourceChild, skipChildrenOf);
2639
- }
2640
- }
2641
- while (target.childNodes.length > sourceChildren.length) {
2642
- target.removeChild(target.lastChild);
2643
- }
2644
- };
2645
- function morphHtml(container, html, attrOverrides = [], skipChildrenOf = ownsItsChildren) {
2646
- const template = container.ownerDocument.createElement("template");
2647
- template.innerHTML = html;
2648
- stripIncompleteTagArtifacts(template.content);
2649
- stripEventHandlerAttrs(template.content);
2650
- for (const { name, attr, value } of attrOverrides) {
2651
- template.content.querySelectorAll(`[data-puck-field-${attr}="${cssEscape(name)}"]`).forEach((el) => {
2652
- if (el.closest("[data-puck-array]")) return;
2653
- el.setAttribute(attr, value);
2654
- });
2655
- }
2656
- morphChildren(container, template.content, skipChildrenOf);
2657
- }
2658
2552
 
2659
- // src/lib/design/observer-guard.ts
2553
+ // src/lib/design/annotations.ts
2660
2554
  init_react_import();
2661
- var wrapScriptWithObserverGuard = (script) => {
2662
- if (!script.includes("MutationObserver")) return script;
2663
- return `(function () {
2664
- var __NativeMutationObserver = window.MutationObserver;
2665
- function MutationObserver(callback) {
2666
- var observed = [];
2667
- var inner = new __NativeMutationObserver(function (records) {
2668
- inner.disconnect();
2669
- try {
2670
- callback(records, inner);
2671
- } finally {
2672
- for (var i = 0; i < observed.length; i++) {
2673
- __nativeObserve.call(inner, observed[i][0], observed[i][1]);
2674
- }
2675
- }
2676
- });
2677
- var __nativeObserve = inner.observe;
2678
- inner.observe = function (target, options) {
2679
- observed.push([target, options]);
2680
- return __nativeObserve.call(inner, target, options);
2681
- };
2682
- return inner;
2683
- }
2684
- ${script}
2685
- })();`;
2686
- };
2687
2555
 
2688
2556
  // src/lib/html-annotations.ts
2689
2557
  init_react_import();
2690
2558
  var import_htmlparser2 = require("htmlparser2");
2691
2559
  var import_domutils = require("domutils");
2692
2560
  var import_domhandler = require("domhandler");
2693
- var FIELD_ATTR_PREFIX2 = "data-puck-field-";
2561
+ var FIELD_ATTR_PREFIX = "data-puck-field-";
2694
2562
  var OPTIONS_ATTR_PREFIX = "data-puck-options-";
2695
2563
  var BIND_ATTR_PREFIX = "data-puck-bind-";
2696
2564
  var BIND_INPUT_ATTR_PREFIX = "data-puck-bind-input-";
@@ -2738,6 +2606,19 @@ var textContentWithBreaks = (node) => {
2738
2606
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2739
2607
  var FIELD_NAME_RE = /^[A-Za-z0-9_]+(?:[-.][A-Za-z0-9_]+)*$/;
2740
2608
  var isValidFieldName = (name) => FIELD_NAME_RE.test(name);
2609
+ var isFieldNameClaimed = (name, claimed, includeExact = true) => {
2610
+ for (const entry of claimed) {
2611
+ if (includeExact && entry === name || entry.startsWith(`${name}.`) || name.startsWith(`${entry}.`)) {
2612
+ return true;
2613
+ }
2614
+ }
2615
+ return false;
2616
+ };
2617
+ var attrBoundFieldNames = (attribs) => new Set(
2618
+ Object.entries(attribs).filter(
2619
+ ([name, value]) => name.startsWith(FIELD_ATTR_PREFIX) && name !== `${FIELD_ATTR_PREFIX}name` && !name.startsWith(`${FIELD_ATTR_PREFIX}value-`) && !tryParseFieldShape(value)
2620
+ ).map(([, value]) => value.trim()).filter(isValidFieldName)
2621
+ );
2741
2622
  var getDeepValue = (source, path) => {
2742
2623
  if (!path.includes(".")) return source[path];
2743
2624
  let current = source;
@@ -2838,6 +2719,7 @@ var collectElementFields = (element, acc) => {
2838
2719
  return true;
2839
2720
  }
2840
2721
  let skipChildren = false;
2722
+ const attrBound = attrBoundFieldNames(attribs);
2841
2723
  for (const [attrName, attrValue] of Object.entries(attribs)) {
2842
2724
  if (attrName.startsWith(OPTIONS_ATTR_PREFIX)) {
2843
2725
  const fieldName = attrName.slice(OPTIONS_ATTR_PREFIX.length);
@@ -2866,8 +2748,8 @@ var collectElementFields = (element, acc) => {
2866
2748
  }
2867
2749
  continue;
2868
2750
  }
2869
- if (!attrName.startsWith(FIELD_ATTR_PREFIX2)) continue;
2870
- const target = attrName.slice(FIELD_ATTR_PREFIX2.length);
2751
+ if (!attrName.startsWith(FIELD_ATTR_PREFIX)) continue;
2752
+ const target = attrName.slice(FIELD_ATTR_PREFIX.length);
2871
2753
  if (!target) continue;
2872
2754
  const shape = tryParseFieldShape(attrValue);
2873
2755
  if (shape) {
@@ -2881,6 +2763,9 @@ var collectElementFields = (element, acc) => {
2881
2763
  if (existing) existing.shape = shape;
2882
2764
  continue;
2883
2765
  }
2766
+ if (isFieldNameClaimed(name2, attrBound, false) || isFieldNameClaimed(name2, acc.seenFields)) {
2767
+ continue;
2768
+ }
2884
2769
  acc.seenFields.add(name2);
2885
2770
  if (shape.type === "richtext") {
2886
2771
  acc.fields.push({ name: name2, binding: "text", shape, format: "json" });
@@ -2935,6 +2820,9 @@ var collectElementFields = (element, acc) => {
2935
2820
  }
2936
2821
  continue;
2937
2822
  }
2823
+ if (target === "name" && isFieldNameClaimed(name, attrBound) || isFieldNameClaimed(name, acc.seenFields)) {
2824
+ continue;
2825
+ }
2938
2826
  acc.seenFields.add(name);
2939
2827
  const fieldType = getTypeModifier(attribs, name);
2940
2828
  const constraints = fieldType === "number" ? getNumberConstraints(attribs, name) : {};
@@ -3028,7 +2916,7 @@ var parseHtmlAnnotations = (html) => {
3028
2916
  if (slotName && isValidFieldName(slotName) && !seenSlots.has(slotName)) {
3029
2917
  seenSlots.add(slotName);
3030
2918
  const shape = tryParseFieldShape(
3031
- attribs[FIELD_ATTR_PREFIX2 + slotName.toLowerCase()] ?? ""
2919
+ attribs[FIELD_ATTR_PREFIX + slotName.toLowerCase()] ?? ""
3032
2920
  );
3033
2921
  const allow = shape?.type === "slot" ? sanitizeStringArray(shape.allow) : void 0;
3034
2922
  const disallow = shape?.type === "slot" ? sanitizeStringArray(shape.disallow) : void 0;
@@ -3177,21 +3065,16 @@ var patchElement = (element, props) => {
3177
3065
  }
3178
3066
  return { didPatch, skipChildren: true };
3179
3067
  }
3180
- const attrBoundFields = /* @__PURE__ */ new Set();
3181
- for (const [an, av] of Object.entries(attribs)) {
3182
- if (an.startsWith(FIELD_ATTR_PREFIX2) && an !== `${FIELD_ATTR_PREFIX2}name` && !tryParseFieldShape(av)) {
3183
- attrBoundFields.add(av.trim());
3184
- }
3185
- }
3068
+ const attrBoundFields = attrBoundFieldNames(attribs);
3186
3069
  for (const [attrName, attrValue] of Object.entries(attribs)) {
3187
- if (!attrName.startsWith(FIELD_ATTR_PREFIX2)) continue;
3188
- const target = attrName.slice(FIELD_ATTR_PREFIX2.length);
3070
+ if (!attrName.startsWith(FIELD_ATTR_PREFIX)) continue;
3071
+ const target = attrName.slice(FIELD_ATTR_PREFIX.length);
3189
3072
  if (!target) continue;
3190
3073
  const shape = tryParseFieldShape(attrValue);
3191
3074
  if (shape) {
3192
3075
  if (shape.type === "slot") continue;
3193
3076
  const name2 = target;
3194
- if (attrBoundFields.has(name2)) continue;
3077
+ if (isFieldNameClaimed(name2, attrBoundFields)) continue;
3195
3078
  const value2 = getDeepValue(props, name2);
3196
3079
  if (shape.type === "richtext") {
3197
3080
  if (typeof value2 !== "string") continue;
@@ -3209,6 +3092,9 @@ var patchElement = (element, props) => {
3209
3092
  }
3210
3093
  const name = attrValue.trim();
3211
3094
  if (!name) continue;
3095
+ if (target === "name" && isFieldNameClaimed(name, attrBoundFields)) {
3096
+ continue;
3097
+ }
3212
3098
  const value = getDeepValue(props, name);
3213
3099
  if (target === "name" && getTypeModifier(attribs, name) === "richtext") {
3214
3100
  if (typeof value !== "string") continue;
@@ -3281,7 +3167,6 @@ var renderHtmlArrayItems = (html, arrayName, items) => {
3281
3167
  };
3282
3168
 
3283
3169
  // src/lib/design/annotations.ts
3284
- init_react_import();
3285
3170
  var cache = /* @__PURE__ */ new Map();
3286
3171
  var MAX_CACHE_ENTRIES = 32;
3287
3172
  var getAnnotations = (html) => {
@@ -3298,6 +3183,160 @@ var getSlotFields = (html) => Object.fromEntries(
3298
3183
  getAnnotations(html).slots.map((slot) => [slot.name, toSlotField(slot)])
3299
3184
  );
3300
3185
 
3186
+ // src/lib/morph-html.ts
3187
+ var PROTECTED_ATTRS = /* @__PURE__ */ new Set([
3188
+ "contenteditable",
3189
+ "data-puck-overlay-portal"
3190
+ ]);
3191
+ var SLOT_OWNED_ATTRS = /* @__PURE__ */ new Set(["style", "class"]);
3192
+ var isEventHandlerAttr = (name) => /^on/i.test(name);
3193
+ var FIELD_ATTR_PREFIX2 = "data-puck-field-";
3194
+ var LEGACY_TEXT_ATTR = `${FIELD_ATTR_PREFIX2}name`;
3195
+ var VALUE_ATTR_PREFIX = `${FIELD_ATTR_PREFIX2}value-`;
3196
+ var hasTextBinding = (el, parsedTextFields) => {
3197
+ for (const attr of Array.from(el.attributes)) {
3198
+ if (!attr.name.startsWith(FIELD_ATTR_PREFIX2)) continue;
3199
+ if (attr.name.startsWith(VALUE_ATTR_PREFIX)) continue;
3200
+ const isShape = attr.value.trimStart().startsWith("{");
3201
+ const fieldName = isShape ? attr.name.slice(FIELD_ATTR_PREFIX2.length) : attr.value.trim();
3202
+ if (isValidFieldName(fieldName) && (attr.name === LEGACY_TEXT_ATTR || isShape) && (!parsedTextFields || parsedTextFields.has(fieldName))) {
3203
+ return true;
3204
+ }
3205
+ }
3206
+ return false;
3207
+ };
3208
+ var ownsItsChildren = (el, parsedTextFields) => el.hasAttribute("data-puck-slot") || el.hasAttribute("data-puck-array") || hasTextBinding(el, parsedTextFields);
3209
+ var syncAttributes = (target, source) => {
3210
+ const isSlot = target.hasAttribute("data-puck-slot");
3211
+ for (const attr of Array.from(target.attributes)) {
3212
+ if (PROTECTED_ATTRS.has(attr.name)) continue;
3213
+ if (isSlot && SLOT_OWNED_ATTRS.has(attr.name)) continue;
3214
+ if (isEventHandlerAttr(attr.name) || !source.hasAttribute(attr.name)) {
3215
+ target.removeAttribute(attr.name);
3216
+ }
3217
+ }
3218
+ for (const attr of Array.from(source.attributes)) {
3219
+ if (PROTECTED_ATTRS.has(attr.name)) continue;
3220
+ if (isSlot && SLOT_OWNED_ATTRS.has(attr.name)) continue;
3221
+ if (isEventHandlerAttr(attr.name)) continue;
3222
+ if (target.getAttribute(attr.name) !== attr.value) {
3223
+ target.setAttribute(attr.name, attr.value);
3224
+ }
3225
+ }
3226
+ };
3227
+ var isSameKind = (a, b) => a.nodeType === b.nodeType && (a.nodeType !== Node.ELEMENT_NODE || a.tagName === b.tagName);
3228
+ var isIncompleteTagArtifact = (node) => node.nodeType === Node.TEXT_NODE && /^<\/?[a-zA-Z]*$/.test(node.nodeValue ?? "");
3229
+ var stripIncompleteTagArtifacts = (root) => {
3230
+ for (const child of Array.from(root.childNodes)) {
3231
+ if (isIncompleteTagArtifact(child)) {
3232
+ root.removeChild(child);
3233
+ } else if (child.nodeType === Node.ELEMENT_NODE) {
3234
+ stripIncompleteTagArtifacts(child);
3235
+ }
3236
+ }
3237
+ };
3238
+ var stripEventHandlerAttrs = (root) => {
3239
+ root.querySelectorAll("*").forEach((el) => {
3240
+ for (const attr of Array.from(el.attributes)) {
3241
+ if (isEventHandlerAttr(attr.name)) el.removeAttribute(attr.name);
3242
+ }
3243
+ });
3244
+ };
3245
+ var morphChildren = (target, source, skipChildrenOf = ownsItsChildren) => {
3246
+ const sourceChildren = Array.from(source.childNodes);
3247
+ for (let i = 0; i < sourceChildren.length; i++) {
3248
+ const sourceChild = sourceChildren[i];
3249
+ const targetChild = target.childNodes[i];
3250
+ if (!targetChild) {
3251
+ target.appendChild(sourceChild);
3252
+ continue;
3253
+ }
3254
+ if (!isSameKind(targetChild, sourceChild)) {
3255
+ target.replaceChild(sourceChild, targetChild);
3256
+ continue;
3257
+ }
3258
+ if (sourceChild.nodeType !== Node.ELEMENT_NODE) {
3259
+ if (targetChild.nodeValue !== sourceChild.nodeValue) {
3260
+ targetChild.nodeValue = sourceChild.nodeValue;
3261
+ }
3262
+ continue;
3263
+ }
3264
+ const targetEl = targetChild;
3265
+ syncAttributes(targetEl, sourceChild);
3266
+ if (!skipChildrenOf(targetEl)) {
3267
+ morphChildren(targetEl, sourceChild, skipChildrenOf);
3268
+ }
3269
+ }
3270
+ while (target.childNodes.length > sourceChildren.length) {
3271
+ target.removeChild(target.lastChild);
3272
+ }
3273
+ };
3274
+ function morphHtml(container, html, attrOverrides = [], skipChildrenOf) {
3275
+ const template = container.ownerDocument.createElement("template");
3276
+ template.innerHTML = html;
3277
+ stripIncompleteTagArtifacts(template.content);
3278
+ stripEventHandlerAttrs(template.content);
3279
+ if (attrOverrides.length > 0) {
3280
+ const overridesByAttr = /* @__PURE__ */ new Map();
3281
+ for (const override of attrOverrides) {
3282
+ if (isEventHandlerAttr(override.attr)) continue;
3283
+ const bindingAttr = `${FIELD_ATTR_PREFIX2}${override.attr}`;
3284
+ const fields = overridesByAttr.get(bindingAttr) ?? /* @__PURE__ */ new Map();
3285
+ fields.set(override.name, override);
3286
+ overridesByAttr.set(bindingAttr, fields);
3287
+ }
3288
+ template.content.querySelectorAll("*").forEach((el) => {
3289
+ for (const attr of Array.from(el.attributes)) {
3290
+ const override = overridesByAttr.get(attr.name)?.get(attr.value.trim());
3291
+ if (!override) continue;
3292
+ if (el.closest("[data-puck-array]")) return;
3293
+ el.setAttribute(override.attr, override.value);
3294
+ }
3295
+ });
3296
+ }
3297
+ if (skipChildrenOf) {
3298
+ morphChildren(container, template.content, skipChildrenOf);
3299
+ return;
3300
+ }
3301
+ const parsedTextFields = new Set(
3302
+ getAnnotations(html).fields.filter((field) => field.binding === "text").map((field) => field.name)
3303
+ );
3304
+ morphChildren(
3305
+ container,
3306
+ template.content,
3307
+ (el) => ownsItsChildren(el, parsedTextFields)
3308
+ );
3309
+ }
3310
+
3311
+ // src/lib/design/observer-guard.ts
3312
+ init_react_import();
3313
+ var wrapScriptWithObserverGuard = (script) => {
3314
+ if (!script.includes("MutationObserver")) return script;
3315
+ return `(function () {
3316
+ var __NativeMutationObserver = window.MutationObserver;
3317
+ function MutationObserver(callback) {
3318
+ var observed = [];
3319
+ var inner = new __NativeMutationObserver(function (records) {
3320
+ inner.disconnect();
3321
+ try {
3322
+ callback(records, inner);
3323
+ } finally {
3324
+ for (var i = 0; i < observed.length; i++) {
3325
+ __nativeObserve.call(inner, observed[i][0], observed[i][1]);
3326
+ }
3327
+ }
3328
+ });
3329
+ var __nativeObserve = inner.observe;
3330
+ inner.observe = function (target, options) {
3331
+ observed.push([target, options]);
3332
+ return __nativeObserve.call(inner, target, options);
3333
+ };
3334
+ return inner;
3335
+ }
3336
+ ${script}
3337
+ })();`;
3338
+ };
3339
+
3301
3340
  // src/lib/design/registration-store.ts
3302
3341
  init_react_import();
3303
3342
  var RegistrationStore = class {
@@ -3371,7 +3410,7 @@ init_react_import();
3371
3410
  var import_core2 = require("@puckeditor/core");
3372
3411
  var CONTENT_EDITABLE_ATTR = "data-puck-content-editable";
3373
3412
  var isInlineEditingDisabled = (el) => el.closest(`[${CONTENT_EDITABLE_ATTR}="false"]`) !== null;
3374
- var cssEscape2 = (value) => typeof CSS !== "undefined" && CSS.escape ? CSS.escape(value) : value.replace(/["\\]/g, "\\$&");
3413
+ var cssEscape = (value) => typeof CSS !== "undefined" && CSS.escape ? CSS.escape(value) : value.replace(/["\\]/g, "\\$&");
3375
3414
  var queryScopedFields = (entry, selector) => Array.from(entry.querySelectorAll(selector)).filter((el) => {
3376
3415
  if (el.closest("[data-puck-array]")) return false;
3377
3416
  const closestSlot = el.closest("[data-puck-slot]");
@@ -3379,7 +3418,7 @@ var queryScopedFields = (entry, selector) => Array.from(entry.querySelectorAll(s
3379
3418
  return true;
3380
3419
  });
3381
3420
  var findOwnArrayContainer = (entry, arrayName) => {
3382
- const selector = `[data-puck-array="${cssEscape2(arrayName)}"]`;
3421
+ const selector = `[data-puck-array="${cssEscape(arrayName)}"]`;
3383
3422
  for (const el of entry.querySelectorAll(selector)) {
3384
3423
  const closestSlot = el.closest("[data-puck-slot]");
3385
3424
  if (!closestSlot || !entry.contains(closestSlot)) return el;
@@ -3394,7 +3433,7 @@ var inlineEditModeFor = (field) => {
3394
3433
  const type = field.shape?.type ?? field.fieldType;
3395
3434
  return type === "richtext" ? "richtext" : type === "number" ? "number" : "text";
3396
3435
  };
3397
- var textFieldSelector = (field) => field.format === "json" ? `[${cssEscape2(`data-puck-field-${field.name}`)}]` : `[data-puck-field-name="${cssEscape2(field.name)}"]`;
3436
+ var textFieldSelector = (field) => field.format === "json" ? `[${cssEscape(`data-puck-field-${field.name}`)}]` : `[data-puck-field-name="${cssEscape(field.name)}"]`;
3398
3437
  var isHTMLElement = (el) => {
3399
3438
  const HTMLElementCtor = el.ownerDocument.defaultView?.HTMLElement;
3400
3439
  return !!HTMLElementCtor && el instanceof HTMLElementCtor;
@@ -3420,6 +3459,29 @@ var syncFieldValues = (entry, fields, props) => {
3420
3459
  });
3421
3460
  }
3422
3461
  };
3462
+ var claimedHostStyles = /* @__PURE__ */ new WeakMap();
3463
+ var claimHostStyles = (el, applyWhiteSpace) => {
3464
+ if (!claimedHostStyles.has(el)) {
3465
+ claimedHostStyles.set(el, {
3466
+ cursor: el.style.cursor,
3467
+ whiteSpace: applyWhiteSpace ? el.style.whiteSpace : null
3468
+ });
3469
+ }
3470
+ applyHostStyles(el);
3471
+ };
3472
+ var applyHostStyles = (el) => {
3473
+ const claimed = claimedHostStyles.get(el);
3474
+ if (!claimed) return;
3475
+ el.style.cursor = "text";
3476
+ if (claimed.whiteSpace !== null) el.style.whiteSpace = "pre-wrap";
3477
+ };
3478
+ var releaseHostStyles = (el) => {
3479
+ const claimed = claimedHostStyles.get(el);
3480
+ if (!claimed) return;
3481
+ el.style.cursor = claimed.cursor;
3482
+ if (claimed.whiteSpace !== null) el.style.whiteSpace = claimed.whiteSpace;
3483
+ claimedHostStyles.delete(el);
3484
+ };
3423
3485
  var attachInlineEditing = (el, {
3424
3486
  componentId,
3425
3487
  propPath,
@@ -3432,8 +3494,7 @@ var attachInlineEditing = (el, {
3432
3494
  const syncEditable = () => {
3433
3495
  el.contentEditable = hovering || focused ? mode === "richtext" ? "true" : "plaintext-only" : "false";
3434
3496
  };
3435
- const previousCursor = el.style.cursor;
3436
- el.style.cursor = "text";
3497
+ claimHostStyles(el, mode !== "richtext");
3437
3498
  syncEditable();
3438
3499
  let lastValidNumberText = el.innerText.replaceAll(/\n/gm, "");
3439
3500
  const handleInput = () => {
@@ -3511,7 +3572,7 @@ var attachInlineEditing = (el, {
3511
3572
  el.removeEventListener("mouseout", handleMouseOut, true);
3512
3573
  el.removeEventListener("focus", handleFocus);
3513
3574
  el.removeEventListener("blur", handleBlur);
3514
- el.style.cursor = previousCursor;
3575
+ releaseHostStyles(el);
3515
3576
  el.removeAttribute("contenteditable");
3516
3577
  cleanupPortal?.();
3517
3578
  };
@@ -3577,7 +3638,7 @@ var wireArrayItemEditing = ({
3577
3638
  const mode = inlineEditModeFor(field);
3578
3639
  const registration = wiring.get(target);
3579
3640
  if (registration?.propPath === propPath && registration.mode === mode) {
3580
- target.style.cursor = "text";
3641
+ applyHostStyles(target);
3581
3642
  continue;
3582
3643
  }
3583
3644
  registration?.cleanup();
@@ -3719,7 +3780,7 @@ var SlotPortal = ({
3719
3780
  [slotStyle]
3720
3781
  );
3721
3782
  (0, import_react23.useEffect)(() => {
3722
- const el = entry.querySelector(`[data-puck-slot="${cssEscape2(slotName)}"]`);
3783
+ const el = entry.querySelector(`[data-puck-slot="${cssEscape(slotName)}"]`);
3723
3784
  if (el !== lastTarget.current) {
3724
3785
  el?.replaceChildren();
3725
3786
  lastTarget.current = el;
@@ -3903,7 +3964,7 @@ function createDesignComponentConfig(registration) {
3903
3964
  morphed.current = { el: entryEl, html, attrSig };
3904
3965
  morphHtml(entryEl, html, attrFields);
3905
3966
  for (const el of fieldWiring.current.keys()) {
3906
- if (el.isConnected) el.style.cursor = "text";
3967
+ if (el.isConnected) applyHostStyles(el);
3907
3968
  }
3908
3969
  }
3909
3970
  syncFieldValues(entryEl, annotations.fields, props);
@@ -4062,9 +4123,11 @@ function getDynamicConfigGlobals(dynamicConfig) {
4062
4123
  ...typeof script === "string" ? { script } : {}
4063
4124
  };
4064
4125
  }
4126
+ var INLINE_EDIT_RESET = ":where([data-puck-design] [contenteditable]){overflow-wrap:inherit;line-break:inherit;-webkit-nbsp-mode:inherit}";
4065
4127
  function DesignGlobals({
4066
4128
  styles,
4067
- script
4129
+ script,
4130
+ isEditing
4068
4131
  }) {
4069
4132
  const scriptRef = (0, import_react25.useRef)(null);
4070
4133
  const hostRef = (0, import_react25.useRef)(null);
@@ -4082,6 +4145,7 @@ function DesignGlobals({
4082
4145
  };
4083
4146
  }, [script]);
4084
4147
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
4148
+ isEditing && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("style", { children: INLINE_EDIT_RESET }),
4085
4149
  styles && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("style", { children: styles }),
4086
4150
  script && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { ref: hostRef, style: { display: "none" } })
4087
4151
  ] });
@@ -4094,7 +4158,14 @@ function withDesignGlobals(root) {
4094
4158
  const render = (props) => {
4095
4159
  const { styles, script } = getDynamicConfigGlobals(props._dynamicConfig);
4096
4160
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
4097
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(DesignGlobals, { styles, script }),
4161
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
4162
+ DesignGlobals,
4163
+ {
4164
+ styles,
4165
+ script,
4166
+ isEditing: props.puck?.isEditing
4167
+ }
4168
+ ),
4098
4169
  userRender ? userRender(props) : props.children
4099
4170
  ] });
4100
4171
  };
@@ -4360,12 +4431,145 @@ var waitForIframeReady = async (root, imageTimeout = 1e4) => {
4360
4431
  await waitForNextPaint(document2);
4361
4432
  };
4362
4433
 
4434
+ // src/lib/capture-screenshot.ts
4435
+ init_react_import();
4436
+ var import_html2canvas_pro = __toESM(require("html2canvas-pro"));
4437
+
4438
+ // src/lib/screenshot-size.ts
4439
+ init_react_import();
4440
+ var MAX_WEBP_DIMENSION = 16383;
4441
+ var CAPTURE_SCALE = 2;
4442
+ var getFitRatio = (width, height, dimensionLimit = MAX_WEBP_DIMENSION) => {
4443
+ if (width <= 0 || height <= 0) return 1;
4444
+ return Math.min(1, dimensionLimit / Math.max(width, height));
4445
+ };
4446
+ var getCaptureScale = (width, height, scale) => scale * getFitRatio(width * scale, height * scale);
4447
+ var scaleDimension = (value, ratio) => Math.max(1, Math.floor(value * ratio));
4448
+ var getDocumentSize = (root) => {
4449
+ const { body, documentElement } = root.ownerDocument;
4450
+ return {
4451
+ width: Math.max(
4452
+ body?.scrollWidth ?? 0,
4453
+ body?.offsetWidth ?? 0,
4454
+ body?.clientWidth ?? 0,
4455
+ documentElement?.scrollWidth ?? 0,
4456
+ documentElement?.offsetWidth ?? 0,
4457
+ documentElement?.clientWidth ?? 0
4458
+ ),
4459
+ height: Math.max(
4460
+ body?.scrollHeight ?? 0,
4461
+ body?.offsetHeight ?? 0,
4462
+ body?.clientHeight ?? 0,
4463
+ documentElement?.scrollHeight ?? 0,
4464
+ documentElement?.offsetHeight ?? 0,
4465
+ documentElement?.clientHeight ?? 0
4466
+ )
4467
+ };
4468
+ };
4469
+
4470
+ // src/lib/capture-screenshot.ts
4471
+ var editorOnlyClassPrefixes = ["_DraggableComponent--hover", "_ActionBar"];
4472
+ var releaseCanvas = (canvas) => {
4473
+ canvas.width = 0;
4474
+ canvas.height = 0;
4475
+ };
4476
+ var drawScaled = (source, ratio) => {
4477
+ const target = document.createElement("canvas");
4478
+ target.width = scaleDimension(source.width, ratio);
4479
+ target.height = scaleDimension(source.height, ratio);
4480
+ const ctx2 = target.getContext("2d");
4481
+ if (!ctx2) {
4482
+ releaseCanvas(target);
4483
+ return source;
4484
+ }
4485
+ ctx2.imageSmoothingEnabled = true;
4486
+ ctx2.imageSmoothingQuality = "high";
4487
+ ctx2.drawImage(source, 0, 0, target.width, target.height);
4488
+ return target;
4489
+ };
4490
+ var fitCanvas = (canvas) => {
4491
+ let remaining = getFitRatio(canvas.width, canvas.height);
4492
+ if (remaining >= 1) return canvas;
4493
+ let current = canvas;
4494
+ while (remaining < 0.5) {
4495
+ const next = drawScaled(current, 0.5);
4496
+ if (current !== canvas) releaseCanvas(current);
4497
+ current = next;
4498
+ remaining *= 2;
4499
+ }
4500
+ const fitted = drawScaled(current, remaining);
4501
+ if (current !== canvas) releaseCanvas(current);
4502
+ return fitted;
4503
+ };
4504
+ var toWebp = (canvas, quality) => new Promise((resolve, reject) => {
4505
+ canvas.toBlob(
4506
+ (blob) => {
4507
+ if (blob && blob.size > 0) {
4508
+ resolve(blob);
4509
+ return;
4510
+ }
4511
+ reject(
4512
+ new Error(
4513
+ `Could not encode a ${canvas.width}x${canvas.height} canvas as WebP`
4514
+ )
4515
+ );
4516
+ },
4517
+ "image/webp",
4518
+ quality
4519
+ );
4520
+ });
4521
+ var captureScreenshot = async (root, { scale = CAPTURE_SCALE, quality = 0.8, width } = {}) => {
4522
+ const documentSize = getDocumentSize(root);
4523
+ const safeWebPScale = getCaptureScale(
4524
+ width ?? documentSize.width,
4525
+ documentSize.height,
4526
+ scale
4527
+ );
4528
+ const canvas = await (0, import_html2canvas_pro.default)(root, {
4529
+ scale: safeWebPScale,
4530
+ backgroundColor: "#ffffff",
4531
+ ...width === void 0 ? {} : { width, windowWidth: width },
4532
+ foreignObjectRendering: false,
4533
+ imageTimeout: 3e4,
4534
+ logging: false,
4535
+ allowTaint: false,
4536
+ useCORS: true,
4537
+ scrollY: 0,
4538
+ ignoreElements: (el) => Array.from(el.classList).some(
4539
+ (c) => editorOnlyClassPrefixes.some((prefix) => c.startsWith(prefix))
4540
+ )
4541
+ });
4542
+ const fitted = fitCanvas(canvas);
4543
+ try {
4544
+ return await toWebp(fitted, quality);
4545
+ } finally {
4546
+ if (fitted !== canvas) releaseCanvas(fitted);
4547
+ releaseCanvas(canvas);
4548
+ }
4549
+ };
4550
+
4551
+ // src/lib/client-tool-response.ts
4552
+ init_react_import();
4553
+ async function postClientToolResponse(host, body) {
4554
+ const response = await fetch(`${host.replace(/\/+$/, "")}/tool`, {
4555
+ method: "POST",
4556
+ headers: { "Content-Type": "application/json" },
4557
+ body: JSON.stringify(body)
4558
+ });
4559
+ if (!response.ok) {
4560
+ throw new Error(
4561
+ `Client tool endpoint returned ${response.status} ${response.statusText}`.trim()
4562
+ );
4563
+ }
4564
+ }
4565
+
4363
4566
  // src/components/Chat/index.tsx
4364
4567
  var import_jsx_runtime35 = require("react/jsx-runtime");
4365
4568
  var q = (0, import_qler.default)();
4366
4569
  var DEFAULT_API_VERSION = "v2";
4367
4570
  var BUILD_OP_DEBOUNCE_MS = 1e3 / 60;
4368
- var PLUGIN_AI_VERSION = true ? "0.8.3-canary.fcc3f680" : "unknown";
4571
+ 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.";
4572
+ var PLUGIN_AI_VERSION = true ? "0.8.3" : "unknown";
4369
4573
  var BENCHMARK = false;
4370
4574
  var prefixedUlid = (prefix = "") => `${prefix ? `${prefix}_` : ""}${(0, import_ulid.ulid)()}`;
4371
4575
  var getClassName18 = getClassNameFactory("Chat", styles_module_default);
@@ -4403,10 +4607,14 @@ function Chat2(props) {
4403
4607
  }, [puckData, getPuck]);
4404
4608
  const [error, setError] = (0, import_react27.useState)();
4405
4609
  const [userError, setUserError] = (0, import_react27.useState)();
4610
+ const clientToolEndpointUnavailableRef = (0, import_react27.useRef)(false);
4611
+ const stopChatRef = (0, import_react27.useRef)(null);
4406
4612
  const [composerAttachments, setComposerAttachments] = (0, import_react27.useState)([]);
4407
4613
  const removedAttachmentClientIdsRef = (0, import_react27.useRef)(/* @__PURE__ */ new Set());
4408
4614
  const uploadControllersRef = (0, import_react27.useRef)(/* @__PURE__ */ new Map());
4409
4615
  const pendingBuildOpsRef = (0, import_react27.useRef)([]);
4616
+ const lastStreamActivityRef = (0, import_react27.useRef)(0);
4617
+ const hiddenSinceRef = (0, import_react27.useRef)(null);
4410
4618
  const [toolStatus, setToolStatus] = (0, import_react27.useState)({});
4411
4619
  const [subagentState, setSubagentState] = (0, import_react27.useState)({});
4412
4620
  const attachmentConfig = (0, import_react27.useMemo)(
@@ -4446,24 +4654,7 @@ function Chat2(props) {
4446
4654
  throw new Error("Preview frame not found");
4447
4655
  }
4448
4656
  await waitForIframeReady(iframeDocument);
4449
- const canvas = await (0, import_html2canvas_pro.default)(iframeDocument, {
4450
- scale: 2,
4451
- backgroundColor: "#ffffff",
4452
- width,
4453
- windowWidth: width,
4454
- foreignObjectRendering: false,
4455
- imageTimeout: 3e4,
4456
- logging: false,
4457
- allowTaint: false,
4458
- useCORS: true,
4459
- scrollY: 0,
4460
- ignoreElements: (el) => Array.from(el.classList).some(
4461
- (c) => c.startsWith("_DraggableComponent--hover") || c.startsWith("_ActionBar")
4462
- )
4463
- });
4464
- const image = canvas.toDataURL("image/webp", 0.8);
4465
- const imageResponse = await fetch(image);
4466
- const blob = await imageResponse.blob();
4657
+ const blob = await captureScreenshot(iframeDocument, { width });
4467
4658
  const uploadResponse = await fetch(bucketUrl, {
4468
4659
  method: "PUT",
4469
4660
  body: blob
@@ -4517,8 +4708,43 @@ function Chat2(props) {
4517
4708
  },
4518
4709
  [debouncedFlushBuildOps]
4519
4710
  );
4711
+ const handleClientToolEndpointUnavailable = (0, import_react27.useCallback)(() => {
4712
+ if (clientToolEndpointUnavailableRef.current) return;
4713
+ clientToolEndpointUnavailableRef.current = true;
4714
+ setError(CLIENT_TOOL_ENDPOINT_UNAVAILABLE_ERROR_MESSAGE);
4715
+ console.error(CLIENT_TOOL_ENDPOINT_UNAVAILABLE_ERROR_MESSAGE);
4716
+ setToolStatus((current) => {
4717
+ let changed = false;
4718
+ const next = {};
4719
+ for (const [id, toolState] of Object.entries(current)) {
4720
+ if (toolState.loading) {
4721
+ changed = true;
4722
+ next[id] = {
4723
+ ...toolState,
4724
+ loading: false,
4725
+ label: "Verification unavailable"
4726
+ };
4727
+ } else {
4728
+ next[id] = toolState;
4729
+ }
4730
+ }
4731
+ return changed ? next : current;
4732
+ });
4733
+ stopChatRef.current?.();
4734
+ }, []);
4735
+ const sendClientToolResponse = (0, import_react27.useCallback)(
4736
+ async (body) => {
4737
+ try {
4738
+ await postClientToolResponse(host, body);
4739
+ } catch {
4740
+ handleClientToolEndpointUnavailable();
4741
+ }
4742
+ },
4743
+ [handleClientToolEndpointUnavailable, host]
4744
+ );
4520
4745
  const processData = (0, import_react27.useCallback)(
4521
4746
  (dataPart) => {
4747
+ lastStreamActivityRef.current = Date.now();
4522
4748
  switch (dataPart.type) {
4523
4749
  case "data-new-chat-created": {
4524
4750
  localChatId.current = dataPart.data.chatId;
@@ -4549,13 +4775,9 @@ function Chat2(props) {
4549
4775
  debouncedFlushBuildOps.flush();
4550
4776
  q.wait().then(() => {
4551
4777
  const { appState } = getPuck();
4552
- return fetch(`${host}/tool`, {
4553
- method: "POST",
4554
- headers: { "Content-Type": "application/json" },
4555
- body: JSON.stringify({
4556
- id: requestId,
4557
- responses: [{ action, output: appState.data }]
4558
- })
4778
+ return sendClientToolResponse({
4779
+ id: requestId,
4780
+ responses: [{ action, output: appState.data }]
4559
4781
  });
4560
4782
  }).catch((e) => {
4561
4783
  console.error("Failed to respond to client request:", e);
@@ -4572,22 +4794,7 @@ function Chat2(props) {
4572
4794
  throw new Error("Preview frame not found");
4573
4795
  }
4574
4796
  await waitForIframeReady(iframeDocument);
4575
- const canvas = await (0, import_html2canvas_pro.default)(iframeDocument, {
4576
- scale: 2,
4577
- backgroundColor: "#ffffff",
4578
- foreignObjectRendering: false,
4579
- imageTimeout: 3e4,
4580
- logging: false,
4581
- allowTaint: false,
4582
- useCORS: true,
4583
- scrollY: 0,
4584
- ignoreElements: (el) => Array.from(el.classList).some(
4585
- (c) => c.startsWith("_DraggableComponent--hover") || c.startsWith("_ActionBar")
4586
- )
4587
- });
4588
- const image = canvas.toDataURL("image/webp", 0.8);
4589
- const imageResponse = await fetch(image);
4590
- const blob = await imageResponse.blob();
4797
+ const blob = await captureScreenshot(iframeDocument);
4591
4798
  const uploadResponse = await fetch(putUrl, {
4592
4799
  method: "PUT",
4593
4800
  body: blob
@@ -4597,16 +4804,16 @@ function Chat2(props) {
4597
4804
  `Upload failed with status ${uploadResponse.status}`
4598
4805
  );
4599
4806
  }
4600
- return fetch(`${host}/tool`, {
4601
- method: "POST",
4602
- headers: { "Content-Type": "application/json" },
4603
- body: JSON.stringify({
4604
- id: requestId,
4605
- responses: [{ action, output: { sizeBytes: blob.size } }]
4606
- })
4807
+ return sendClientToolResponse({
4808
+ id: requestId,
4809
+ responses: [{ action, output: { sizeBytes: blob.size } }]
4607
4810
  });
4608
4811
  }).catch((e) => {
4609
4812
  console.error("Failed to respond to client request:", e);
4813
+ return sendClientToolResponse({
4814
+ id: requestId,
4815
+ responses: [{ action, output: { sizeBytes: 0 } }]
4816
+ });
4610
4817
  });
4611
4818
  }
4612
4819
  return;
@@ -4650,6 +4857,7 @@ function Chat2(props) {
4650
4857
  getPuck,
4651
4858
  puckDispatch,
4652
4859
  queueBuildOp,
4860
+ sendClientToolResponse,
4653
4861
  uploadScreenshot
4654
4862
  ]
4655
4863
  );
@@ -4719,12 +4927,14 @@ function Chat2(props) {
4719
4927
  if (BENCHMARK) {
4720
4928
  console.timeEnd("chat");
4721
4929
  }
4722
- setError(e.message);
4930
+ if (!clientToolEndpointUnavailableRef.current) {
4931
+ setError(e.message);
4932
+ }
4723
4933
  },
4724
4934
  onFinish: (options) => {
4725
4935
  debouncedFlushBuildOps.flush();
4726
4936
  q.wait().then(() => {
4727
- if (!options.isAbort) {
4937
+ if (!options.isAbort || clientToolEndpointUnavailableRef.current) {
4728
4938
  puckDispatch({
4729
4939
  type: "set",
4730
4940
  state: getPuck().appState,
@@ -4739,12 +4949,63 @@ function Chat2(props) {
4739
4949
  });
4740
4950
  }
4741
4951
  });
4742
- (0, import_react27.useEffect)(
4743
- () => () => {
4952
+ (0, import_react27.useEffect)(() => {
4953
+ stopChatRef.current = stop;
4954
+ return () => {
4955
+ stopChatRef.current = null;
4744
4956
  stop();
4745
- },
4746
- [stop]
4747
- );
4957
+ };
4958
+ }, [stop]);
4959
+ const statusRef = (0, import_react27.useRef)(status);
4960
+ (0, import_react27.useEffect)(() => {
4961
+ statusRef.current = status;
4962
+ }, [status]);
4963
+ (0, import_react27.useEffect)(() => {
4964
+ lastStreamActivityRef.current = Date.now();
4965
+ }, [messages]);
4966
+ (0, import_react27.useEffect)(() => {
4967
+ const FROZEN_HIDDEN_MS = 3 * 6e4;
4968
+ const RESUME_GRACE_MS = 12e3;
4969
+ const isStreaming = () => statusRef.current === "streaming" || statusRef.current === "submitted";
4970
+ let graceTimer;
4971
+ const recoverIfSevered = () => {
4972
+ if (!isStreaming()) return;
4973
+ if (Date.now() - lastStreamActivityRef.current < RESUME_GRACE_MS) return;
4974
+ stop();
4975
+ setToolStatus((current) => {
4976
+ let changed = false;
4977
+ const next = {};
4978
+ for (const [id, toolState] of Object.entries(current)) {
4979
+ if (toolState.loading) {
4980
+ changed = true;
4981
+ next[id] = { ...toolState, loading: false, label: "Interrupted" };
4982
+ } else {
4983
+ next[id] = toolState;
4984
+ }
4985
+ }
4986
+ return changed ? next : current;
4987
+ });
4988
+ };
4989
+ const onVisibilityChange = () => {
4990
+ if (document.hidden) {
4991
+ hiddenSinceRef.current = isStreaming() ? Date.now() : null;
4992
+ return;
4993
+ }
4994
+ const hiddenSince = hiddenSinceRef.current;
4995
+ hiddenSinceRef.current = null;
4996
+ if (graceTimer) clearTimeout(graceTimer);
4997
+ if (hiddenSince === null || Date.now() - hiddenSince < FROZEN_HIDDEN_MS) {
4998
+ return;
4999
+ }
5000
+ lastStreamActivityRef.current = 0;
5001
+ graceTimer = setTimeout(recoverIfSevered, RESUME_GRACE_MS);
5002
+ };
5003
+ document.addEventListener("visibilitychange", onVisibilityChange);
5004
+ return () => {
5005
+ document.removeEventListener("visibilitychange", onVisibilityChange);
5006
+ if (graceTimer) clearTimeout(graceTimer);
5007
+ };
5008
+ }, [stop]);
4748
5009
  const [forcedStatus, setForcedStatus] = (0, import_react27.useState)();
4749
5010
  const resolvedStatus = (0, import_react27.useMemo)(
4750
5011
  () => forcedStatus ?? status,
@@ -4795,6 +5056,8 @@ function Chat2(props) {
4795
5056
  if (!text && readyAttachments.length === 0) {
4796
5057
  return false;
4797
5058
  }
5059
+ clientToolEndpointUnavailableRef.current = false;
5060
+ setError("");
4798
5061
  setUserError("");
4799
5062
  if (chat?.onSubmit) {
4800
5063
  try {
@@ -5034,6 +5297,7 @@ function Chat2(props) {
5034
5297
  ),
5035
5298
  error,
5036
5299
  handleRetry: () => {
5300
+ clientToolEndpointUnavailableRef.current = false;
5037
5301
  setError("");
5038
5302
  regenerate();
5039
5303
  },