@puckeditor/plugin-ai 0.9.0-canary.d8bdabee → 0.9.0-canary.fb1abcf5

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 +567 -411
  2. package/dist/index.mjs +551 -395
  3. package/package.json +9 -9
package/dist/index.js CHANGED
@@ -995,7 +995,7 @@ function useChat({
995
995
  // src/components/Chat/index.tsx
996
996
  var import_core5 = require("@puckeditor/core");
997
997
  var import_ai5 = require("ai");
998
- var import_react28 = require("react");
998
+ var import_react27 = require("react");
999
999
  var import_ulid = require("ulid");
1000
1000
 
1001
1001
  // ../utils/lib/get-class-name-factory.ts
@@ -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
@@ -2546,151 +2545,20 @@ var import_core4 = require("@puckeditor/core");
2546
2545
  // src/lib/design/component.tsx
2547
2546
  init_react_import();
2548
2547
  var import_core3 = require("@puckeditor/core");
2549
- var import_react25 = __toESM(require("react"));
2548
+ 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-";
@@ -2700,26 +2568,21 @@ var MAX_ATTR_PREFIX = "data-puck-max-";
2700
2568
  var STEP_ATTR_PREFIX = "data-puck-step-";
2701
2569
  var SLOT_ATTR = "data-puck-slot";
2702
2570
  var ARRAY_ATTR = "data-puck-array";
2703
- var BUILT_IN_FIELD_TYPES = /* @__PURE__ */ new Set([
2571
+ var RECOGNIZED_FIELD_TYPES = /* @__PURE__ */ new Set([
2704
2572
  "text",
2705
2573
  "textarea",
2706
2574
  "number",
2707
2575
  "richtext",
2708
2576
  "radio",
2709
2577
  "select",
2710
- "slot",
2711
- "array",
2712
- "object",
2713
- "external",
2714
- "custom"
2578
+ "slot"
2715
2579
  ]);
2716
- var isBuiltInFieldType = (type) => BUILT_IN_FIELD_TYPES.has(type);
2717
- var tryParseFieldShape = (attrValue) => {
2718
- const trimmed = attrValue.trimStart();
2580
+ var tryParseFieldShape = (value) => {
2581
+ const trimmed = value.trimStart();
2719
2582
  if (!trimmed.startsWith("{")) return null;
2720
2583
  try {
2721
2584
  const parsed = JSON.parse(trimmed);
2722
- if (isRecord(parsed) && typeof parsed.type === "string" && parsed.type) {
2585
+ if (isRecord(parsed) && typeof parsed.type === "string" && RECOGNIZED_FIELD_TYPES.has(parsed.type)) {
2723
2586
  return parsed;
2724
2587
  }
2725
2588
  } catch {
@@ -2743,6 +2606,19 @@ var textContentWithBreaks = (node) => {
2743
2606
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2744
2607
  var FIELD_NAME_RE = /^[A-Za-z0-9_]+(?:[-.][A-Za-z0-9_]+)*$/;
2745
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
+ );
2746
2622
  var getDeepValue = (source, path) => {
2747
2623
  if (!path.includes(".")) return source[path];
2748
2624
  let current = source;
@@ -2843,6 +2719,7 @@ var collectElementFields = (element, acc) => {
2843
2719
  return true;
2844
2720
  }
2845
2721
  let skipChildren = false;
2722
+ const attrBound = attrBoundFieldNames(attribs);
2846
2723
  for (const [attrName, attrValue] of Object.entries(attribs)) {
2847
2724
  if (attrName.startsWith(OPTIONS_ATTR_PREFIX)) {
2848
2725
  const fieldName = attrName.slice(OPTIONS_ATTR_PREFIX.length);
@@ -2871,8 +2748,8 @@ var collectElementFields = (element, acc) => {
2871
2748
  }
2872
2749
  continue;
2873
2750
  }
2874
- if (!attrName.startsWith(FIELD_ATTR_PREFIX2)) continue;
2875
- 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);
2876
2753
  if (!target) continue;
2877
2754
  const shape = tryParseFieldShape(attrValue);
2878
2755
  if (shape) {
@@ -2886,17 +2763,11 @@ var collectElementFields = (element, acc) => {
2886
2763
  if (existing) existing.shape = shape;
2887
2764
  continue;
2888
2765
  }
2766
+ if (isFieldNameClaimed(name2, attrBound, false) || isFieldNameClaimed(name2, acc.seenFields)) {
2767
+ continue;
2768
+ }
2889
2769
  acc.seenFields.add(name2);
2890
- if (!isBuiltInFieldType(shape.type)) {
2891
- const { type, ...value } = shape;
2892
- acc.fields.push({
2893
- name: name2,
2894
- binding: "text",
2895
- shape: { type },
2896
- format: "json"
2897
- });
2898
- setExtractedValue(acc.values, name2, value);
2899
- } else if (shape.type === "richtext") {
2770
+ if (shape.type === "richtext") {
2900
2771
  acc.fields.push({ name: name2, binding: "text", shape, format: "json" });
2901
2772
  setExtractedValue(acc.values, name2, (0, import_domutils.getInnerHTML)(element).trim());
2902
2773
  skipChildren = true;
@@ -2949,6 +2820,9 @@ var collectElementFields = (element, acc) => {
2949
2820
  }
2950
2821
  continue;
2951
2822
  }
2823
+ if (target === "name" && isFieldNameClaimed(name, attrBound) || isFieldNameClaimed(name, acc.seenFields)) {
2824
+ continue;
2825
+ }
2952
2826
  acc.seenFields.add(name);
2953
2827
  const fieldType = getTypeModifier(attribs, name);
2954
2828
  const constraints = fieldType === "number" ? getNumberConstraints(attribs, name) : {};
@@ -3042,7 +2916,7 @@ var parseHtmlAnnotations = (html) => {
3042
2916
  if (slotName && isValidFieldName(slotName) && !seenSlots.has(slotName)) {
3043
2917
  seenSlots.add(slotName);
3044
2918
  const shape = tryParseFieldShape(
3045
- attribs[FIELD_ATTR_PREFIX2 + slotName.toLowerCase()] ?? ""
2919
+ attribs[FIELD_ATTR_PREFIX + slotName.toLowerCase()] ?? ""
3046
2920
  );
3047
2921
  const allow = shape?.type === "slot" ? sanitizeStringArray(shape.allow) : void 0;
3048
2922
  const disallow = shape?.type === "slot" ? sanitizeStringArray(shape.disallow) : void 0;
@@ -3191,21 +3065,16 @@ var patchElement = (element, props) => {
3191
3065
  }
3192
3066
  return { didPatch, skipChildren: true };
3193
3067
  }
3194
- const attrBoundFields = /* @__PURE__ */ new Set();
3195
- for (const [an, av] of Object.entries(attribs)) {
3196
- if (an.startsWith(FIELD_ATTR_PREFIX2) && an !== `${FIELD_ATTR_PREFIX2}name` && !tryParseFieldShape(av)) {
3197
- attrBoundFields.add(av.trim());
3198
- }
3199
- }
3068
+ const attrBoundFields = attrBoundFieldNames(attribs);
3200
3069
  for (const [attrName, attrValue] of Object.entries(attribs)) {
3201
- if (!attrName.startsWith(FIELD_ATTR_PREFIX2)) continue;
3202
- 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);
3203
3072
  if (!target) continue;
3204
3073
  const shape = tryParseFieldShape(attrValue);
3205
3074
  if (shape) {
3206
3075
  if (shape.type === "slot") continue;
3207
3076
  const name2 = target;
3208
- if (attrBoundFields.has(name2)) continue;
3077
+ if (isFieldNameClaimed(name2, attrBoundFields)) continue;
3209
3078
  const value2 = getDeepValue(props, name2);
3210
3079
  if (shape.type === "richtext") {
3211
3080
  if (typeof value2 !== "string") continue;
@@ -3223,6 +3092,9 @@ var patchElement = (element, props) => {
3223
3092
  }
3224
3093
  const name = attrValue.trim();
3225
3094
  if (!name) continue;
3095
+ if (target === "name" && isFieldNameClaimed(name, attrBoundFields)) {
3096
+ continue;
3097
+ }
3226
3098
  const value = getDeepValue(props, name);
3227
3099
  if (target === "name" && getTypeModifier(attribs, name) === "richtext") {
3228
3100
  if (typeof value !== "string") continue;
@@ -3295,7 +3167,6 @@ var renderHtmlArrayItems = (html, arrayName, items) => {
3295
3167
  };
3296
3168
 
3297
3169
  // src/lib/design/annotations.ts
3298
- init_react_import();
3299
3170
  var cache = /* @__PURE__ */ new Map();
3300
3171
  var MAX_CACHE_ENTRIES = 32;
3301
3172
  var getAnnotations = (html) => {
@@ -3311,22 +3182,159 @@ var getAnnotations = (html) => {
3311
3182
  var getSlotFields = (html) => Object.fromEntries(
3312
3183
  getAnnotations(html).slots.map((slot) => [slot.name, toSlotField(slot)])
3313
3184
  );
3314
- var UNTRANSFORMABLE_FIELD_TYPES = /* @__PURE__ */ new Set(["richtext", "array", "slot"]);
3315
- var isTransformable = (field) => {
3316
- if (!field?.type) return false;
3317
- if (UNTRANSFORMABLE_FIELD_TYPES.has(field.type)) return false;
3318
- const nested = field.objectFields ?? field.arrayFields;
3319
- return nested ? Object.values(nested).every(isTransformable) : true;
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;
3320
3207
  };
3321
- var getTransformableFields = (reg) => {
3322
- const annotations = getAnnotations(reg.html ?? "");
3323
- const source = {
3324
- ...toPuckFields(annotations.fields, annotations.values),
3325
- ...reg.fields ?? {}
3326
- };
3327
- return Object.fromEntries(
3328
- Object.entries(source).filter(([, field]) => isTransformable(field))
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)
3329
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
+ })();`;
3330
3338
  };
3331
3339
 
3332
3340
  // src/lib/design/registration-store.ts
@@ -3402,7 +3410,7 @@ init_react_import();
3402
3410
  var import_core2 = require("@puckeditor/core");
3403
3411
  var CONTENT_EDITABLE_ATTR = "data-puck-content-editable";
3404
3412
  var isInlineEditingDisabled = (el) => el.closest(`[${CONTENT_EDITABLE_ATTR}="false"]`) !== null;
3405
- 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, "\\$&");
3406
3414
  var queryScopedFields = (entry, selector) => Array.from(entry.querySelectorAll(selector)).filter((el) => {
3407
3415
  if (el.closest("[data-puck-array]")) return false;
3408
3416
  const closestSlot = el.closest("[data-puck-slot]");
@@ -3410,7 +3418,7 @@ var queryScopedFields = (entry, selector) => Array.from(entry.querySelectorAll(s
3410
3418
  return true;
3411
3419
  });
3412
3420
  var findOwnArrayContainer = (entry, arrayName) => {
3413
- const selector = `[data-puck-array="${cssEscape2(arrayName)}"]`;
3421
+ const selector = `[data-puck-array="${cssEscape(arrayName)}"]`;
3414
3422
  for (const el of entry.querySelectorAll(selector)) {
3415
3423
  const closestSlot = el.closest("[data-puck-slot]");
3416
3424
  if (!closestSlot || !entry.contains(closestSlot)) return el;
@@ -3425,7 +3433,7 @@ var inlineEditModeFor = (field) => {
3425
3433
  const type = field.shape?.type ?? field.fieldType;
3426
3434
  return type === "richtext" ? "richtext" : type === "number" ? "number" : "text";
3427
3435
  };
3428
- 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)}"]`;
3429
3437
  var isHTMLElement = (el) => {
3430
3438
  const HTMLElementCtor = el.ownerDocument.defaultView?.HTMLElement;
3431
3439
  return !!HTMLElementCtor && el instanceof HTMLElementCtor;
@@ -3451,6 +3459,29 @@ var syncFieldValues = (entry, fields, props) => {
3451
3459
  });
3452
3460
  }
3453
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
+ };
3454
3485
  var attachInlineEditing = (el, {
3455
3486
  componentId,
3456
3487
  propPath,
@@ -3463,8 +3494,7 @@ var attachInlineEditing = (el, {
3463
3494
  const syncEditable = () => {
3464
3495
  el.contentEditable = hovering || focused ? mode === "richtext" ? "true" : "plaintext-only" : "false";
3465
3496
  };
3466
- const previousCursor = el.style.cursor;
3467
- el.style.cursor = "text";
3497
+ claimHostStyles(el, mode !== "richtext");
3468
3498
  syncEditable();
3469
3499
  let lastValidNumberText = el.innerText.replaceAll(/\n/gm, "");
3470
3500
  const handleInput = () => {
@@ -3542,7 +3572,7 @@ var attachInlineEditing = (el, {
3542
3572
  el.removeEventListener("mouseout", handleMouseOut, true);
3543
3573
  el.removeEventListener("focus", handleFocus);
3544
3574
  el.removeEventListener("blur", handleBlur);
3545
- el.style.cursor = previousCursor;
3575
+ releaseHostStyles(el);
3546
3576
  el.removeAttribute("contenteditable");
3547
3577
  cleanupPortal?.();
3548
3578
  };
@@ -3608,7 +3638,7 @@ var wireArrayItemEditing = ({
3608
3638
  const mode = inlineEditModeFor(field);
3609
3639
  const registration = wiring.get(target);
3610
3640
  if (registration?.propPath === propPath && registration.mode === mode) {
3611
- target.style.cursor = "text";
3641
+ applyHostStyles(target);
3612
3642
  continue;
3613
3643
  }
3614
3644
  registration?.cleanup();
@@ -3674,15 +3704,7 @@ var parseStyleString = (cssText) => {
3674
3704
  return style;
3675
3705
  };
3676
3706
  var isEventHandlerAttr2 = (name) => /^on/i.test(name);
3677
- var FIELD_ATTR_PREFIX3 = "data-puck-field-";
3678
- var fieldContentFor = (attribs, fields) => {
3679
- for (const name of Object.keys(fields)) {
3680
- if (attribs[`${FIELD_ATTR_PREFIX3}${name}`] !== void 0) return name;
3681
- if (attribs["data-puck-field-name"] === name) return name;
3682
- }
3683
- return void 0;
3684
- };
3685
- var nodeToReact = (node, slots, fields, key) => {
3707
+ var nodeToReact = (node, slots, key) => {
3686
3708
  if (node.type === "text") {
3687
3709
  return node.data;
3688
3710
  }
@@ -3713,17 +3735,13 @@ var nodeToReact = (node, slots, fields, key) => {
3713
3735
  if (VOID_ELEMENTS.has(tag)) {
3714
3736
  return (0, import_react22.createElement)(tag, props);
3715
3737
  }
3716
- const fieldName = fieldContentFor(attribs, fields);
3717
- if (fieldName !== void 0) {
3718
- return (0, import_react22.createElement)(tag, props, fields[fieldName]);
3719
- }
3720
- const children = el.children.map((child, i) => nodeToReact(child, slots, fields, i)).filter((c) => c !== null);
3738
+ const children = el.children.map((child, i) => nodeToReact(child, slots, i)).filter((c) => c !== null);
3721
3739
  return (0, import_react22.createElement)(tag, props, ...children);
3722
3740
  };
3723
- var htmlToReactTree = (html, slots, fields = {}) => {
3741
+ var htmlToReactTree = (html, slots) => {
3724
3742
  if (!html) return null;
3725
3743
  const doc = (0, import_htmlparser22.parseDocument)(html);
3726
- const children = doc.children.map((child, i) => nodeToReact(child, slots, fields, i)).filter((c) => c !== null);
3744
+ const children = doc.children.map((child, i) => nodeToReact(child, slots, i)).filter((c) => c !== null);
3727
3745
  if (children.length === 0) return null;
3728
3746
  if (children.length === 1) return children[0];
3729
3747
  return (0, import_react22.createElement)(import_react22.Fragment, null, ...children);
@@ -3762,7 +3780,7 @@ var SlotPortal = ({
3762
3780
  [slotStyle]
3763
3781
  );
3764
3782
  (0, import_react23.useEffect)(() => {
3765
- const el = entry.querySelector(`[data-puck-slot="${cssEscape2(slotName)}"]`);
3783
+ const el = entry.querySelector(`[data-puck-slot="${cssEscape(slotName)}"]`);
3766
3784
  if (el !== lastTarget.current) {
3767
3785
  el?.replaceChildren();
3768
3786
  lastTarget.current = el;
@@ -3780,32 +3798,10 @@ var SlotPortal = ({
3780
3798
  );
3781
3799
  };
3782
3800
 
3783
- // src/lib/design/field-portal.tsx
3784
- init_react_import();
3785
- var import_react24 = require("react");
3786
- var import_react_dom2 = require("react-dom");
3787
- var FieldPortal = ({
3788
- entry,
3789
- html,
3790
- selector,
3791
- children
3792
- }) => {
3793
- const [target, setTarget] = (0, import_react24.useState)(null);
3794
- const lastTarget = (0, import_react24.useRef)(null);
3795
- (0, import_react24.useEffect)(() => {
3796
- const el = queryScopedFields(entry, selector)[0] ?? null;
3797
- if (el !== lastTarget.current) {
3798
- el?.replaceChildren();
3799
- lastTarget.current = el;
3800
- setTarget(el);
3801
- }
3802
- }, [entry, html, selector]);
3803
- if (!target) return null;
3804
- return (0, import_react_dom2.createPortal)(children, target);
3805
- };
3806
-
3807
3801
  // src/lib/design/component.tsx
3808
3802
  var import_jsx_runtime32 = require("react/jsx-runtime");
3803
+ var DESIGN_COMPONENT = Symbol("puck-ai.designComponent");
3804
+ var isDesignComponent = (config) => !!config?.[DESIGN_COMPONENT];
3809
3805
  var streamingValues = /* @__PURE__ */ new Map();
3810
3806
  var instanceKey = (name, instanceId) => `${name}:${typeof instanceId === "string" ? instanceId : ""}`;
3811
3807
  var getRegistrationSlotFields = (reg) => {
@@ -3830,9 +3826,9 @@ function createDesignComponentConfig(registration) {
3830
3826
  registrationStore.apply(registration);
3831
3827
  }
3832
3828
  const slotFields = getRegistrationSlotFields(registration);
3833
- return {
3829
+ const componentConfig = {
3834
3830
  ...registration.label ? { label: registration.label } : {},
3835
- fields: { ...slotFields, ...getTransformableFields(registration) },
3831
+ fields: { ...slotFields },
3836
3832
  defaultProps: { ...registration.defaultProps ?? {} },
3837
3833
  resolveData: (data, { trigger }) => {
3838
3834
  if (trigger !== "replace" && trigger !== "insert" && trigger !== "force") {
@@ -3891,6 +3887,8 @@ function createDesignComponentConfig(registration) {
3891
3887
  },
3892
3888
  render: (renderProps) => renderProps.puck?.isEditing ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(EditorEntry, { ...renderProps }) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Entry, { ...renderProps, getPuck: null })
3893
3889
  };
3890
+ componentConfig[DESIGN_COMPONENT] = true;
3891
+ return componentConfig;
3894
3892
  function EditorEntry(renderProps) {
3895
3893
  const getPuck = (0, import_core3.useGetPuck)();
3896
3894
  return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Entry, { ...renderProps, getPuck });
@@ -3902,11 +3900,11 @@ function createDesignComponentConfig(registration) {
3902
3900
  style,
3903
3901
  ...props
3904
3902
  }) {
3905
- const getSnapshot = (0, import_react25.useCallback)(
3903
+ const getSnapshot = (0, import_react24.useCallback)(
3906
3904
  () => registrationStore.get(name) ?? registration,
3907
3905
  []
3908
3906
  );
3909
- const reg = (0, import_react25.useSyncExternalStore)(
3907
+ const reg = (0, import_react24.useSyncExternalStore)(
3910
3908
  registrationStore.subscribe,
3911
3909
  getSnapshot,
3912
3910
  getSnapshot
@@ -3914,10 +3912,10 @@ function createDesignComponentConfig(registration) {
3914
3912
  const html = reg.html ?? "";
3915
3913
  const styles = reg.styles;
3916
3914
  const script = reg.script;
3917
- const [entryEl, setEntryEl] = (0, import_react25.useState)(null);
3918
- const scriptRef = (0, import_react25.useRef)(null);
3919
- const annotations = (0, import_react25.useMemo)(() => getAnnotations(html), [html]);
3920
- const { attrFields, arrayFields, attrSig, arraySig } = (0, import_react25.useMemo)(() => {
3915
+ const [entryEl, setEntryEl] = (0, import_react24.useState)(null);
3916
+ const scriptRef = (0, import_react24.useRef)(null);
3917
+ const annotations = (0, import_react24.useMemo)(() => getAnnotations(html), [html]);
3918
+ const { attrFields, arrayFields, attrSig, arraySig } = (0, import_react24.useMemo)(() => {
3921
3919
  const attr = [];
3922
3920
  const arrays = [];
3923
3921
  for (const field of annotations.fields) {
@@ -3939,86 +3937,56 @@ function createDesignComponentConfig(registration) {
3939
3937
  arraySig: JSON.stringify(arrays)
3940
3938
  };
3941
3939
  }, [props, annotations]);
3942
- const nodeFields = (0, import_react25.useMemo)(() => {
3943
- const out = [];
3944
- for (const field of annotations.fields) {
3945
- if (field.binding !== "text") continue;
3946
- const value = getDeepValue(props, field.name);
3947
- if (!import_react25.default.isValidElement(value)) continue;
3948
- out.push({
3949
- name: field.name,
3950
- selector: textFieldSelector(field),
3951
- node: value
3952
- });
3953
- }
3954
- return out;
3955
- }, [annotations, props]);
3956
- const storedProps = (0, import_react25.useMemo)(
3957
- () => getPuck ? getPuck().getItemById(id)?.props : void 0,
3958
- [getPuck, id, props]
3959
- );
3960
- const editableFields = (0, import_react25.useMemo)(() => {
3961
- const mounted = new Set(nodeFields.map((f) => f.name));
3962
- return annotations.fields.filter((field) => {
3963
- if (mounted.has(field.name)) return false;
3964
- if (field.binding === "array") return true;
3965
- const type = field.shape?.type;
3966
- if (type && !isBuiltInFieldType(type)) return false;
3967
- if (!storedProps) return true;
3968
- return getDeepValue(props, field.name) === getDeepValue(storedProps, field.name);
3969
- });
3970
- }, [annotations, nodeFields, props, storedProps]);
3971
- const patchedHtml = (0, import_react25.useMemo)(() => {
3940
+ const patchedHtml = (0, import_react24.useMemo)(() => {
3972
3941
  if (puck.isEditing) return html;
3973
3942
  return patchHtmlAnnotations(html, props);
3974
3943
  }, [html, props, puck.isEditing]);
3975
- const fieldValueAttrs = (0, import_react25.useMemo)(() => {
3944
+ const fieldValueAttrs = (0, import_react24.useMemo)(() => {
3976
3945
  const attrs = {};
3977
3946
  attrs["data-puck-design"] = name;
3978
3947
  for (const field of annotations.fields) {
3979
3948
  const value = getDeepValue(props, field.name);
3980
3949
  if (value === void 0 || value === null) continue;
3981
3950
  if (!isValidFieldName(field.name)) continue;
3982
- if (import_react25.default.isValidElement(value)) continue;
3983
3951
  attrs[`data-puck-field-value-${field.name}`] = typeof value === "string" || typeof value === "number" ? String(value) : JSON.stringify(value);
3984
3952
  }
3985
3953
  return attrs;
3986
3954
  }, [annotations, props, puck.isEditing]);
3987
- const editorEntryNode = (0, import_react25.useMemo)(
3955
+ const editorEntryNode = (0, import_react24.useMemo)(
3988
3956
  () => puck.isEditing ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { ref: setEntryEl }) : null,
3989
3957
  [puck.isEditing]
3990
3958
  );
3991
- (0, import_react25.useEffect)(() => {
3959
+ (0, import_react24.useEffect)(() => {
3992
3960
  if (!puck.isEditing || !getPuck) return;
3993
3961
  getPuck().resolveDataById(id, "force");
3994
3962
  }, [reg, puck.isEditing, getPuck, id]);
3995
- const fieldWiring = (0, import_react25.useRef)(/* @__PURE__ */ new Map());
3996
- const morphed = (0, import_react25.useRef)(null);
3997
- (0, import_react25.useLayoutEffect)(() => {
3963
+ const fieldWiring = (0, import_react24.useRef)(/* @__PURE__ */ new Map());
3964
+ const morphed = (0, import_react24.useRef)(null);
3965
+ (0, import_react24.useLayoutEffect)(() => {
3998
3966
  if (!puck.isEditing || !entryEl) return;
3999
3967
  if (morphed.current?.el !== entryEl || morphed.current?.html !== html || morphed.current?.attrSig !== attrSig) {
4000
3968
  morphed.current = { el: entryEl, html, attrSig };
4001
3969
  morphHtml(entryEl, html, attrFields);
4002
3970
  for (const el of fieldWiring.current.keys()) {
4003
- if (el.isConnected) el.style.cursor = "text";
3971
+ if (el.isConnected) applyHostStyles(el);
4004
3972
  }
4005
3973
  }
4006
3974
  syncFieldValues(entryEl, annotations.fields, props);
4007
3975
  if (getPuck) {
4008
- wireFieldEditing(entryEl, editableFields, fieldWiring.current, {
3976
+ wireFieldEditing(entryEl, annotations.fields, fieldWiring.current, {
4009
3977
  componentId: id,
4010
3978
  getPuck
4011
3979
  });
4012
3980
  }
4013
3981
  });
4014
- (0, import_react25.useEffect)(
3982
+ (0, import_react24.useEffect)(
4015
3983
  () => () => {
4016
3984
  fieldWiring.current.forEach((cleanup) => cleanup());
4017
3985
  fieldWiring.current.clear();
4018
3986
  },
4019
3987
  []
4020
3988
  );
4021
- (0, import_react25.useEffect)(() => {
3989
+ (0, import_react24.useEffect)(() => {
4022
3990
  if (!entryEl || !puck.isEditing) return;
4023
3991
  for (const attr of Array.from(entryEl.attributes)) {
4024
3992
  if ((attr.name === "data-puck-design" || attr.name.startsWith("data-puck-field-value-")) && !(attr.name in fieldValueAttrs)) {
@@ -4031,10 +3999,10 @@ function createDesignComponentConfig(registration) {
4031
3999
  }
4032
4000
  }
4033
4001
  }, [entryEl, fieldValueAttrs]);
4034
- const appliedArrayHtml = (0, import_react25.useRef)(/* @__PURE__ */ new WeakMap());
4035
- const arrayItemWiring = (0, import_react25.useRef)({});
4036
- const appliedArrays = (0, import_react25.useRef)(null);
4037
- (0, import_react25.useEffect)(() => {
4002
+ const appliedArrayHtml = (0, import_react24.useRef)(/* @__PURE__ */ new WeakMap());
4003
+ const arrayItemWiring = (0, import_react24.useRef)({});
4004
+ const appliedArrays = (0, import_react24.useRef)(null);
4005
+ (0, import_react24.useEffect)(() => {
4038
4006
  if (!puck.isEditing || !entryEl || !getPuck) return;
4039
4007
  if (appliedArrays.current?.el === entryEl && appliedArrays.current?.html === html && appliedArrays.current?.sig === arraySig) {
4040
4008
  return;
@@ -4086,7 +4054,7 @@ function createDesignComponentConfig(registration) {
4086
4054
  id,
4087
4055
  getPuck
4088
4056
  ]);
4089
- (0, import_react25.useEffect)(
4057
+ (0, import_react24.useEffect)(
4090
4058
  () => () => {
4091
4059
  Object.values(arrayItemWiring.current).forEach(
4092
4060
  (wiring) => wiring.forEach(({ cleanup }) => cleanup())
@@ -4094,7 +4062,7 @@ function createDesignComponentConfig(registration) {
4094
4062
  },
4095
4063
  []
4096
4064
  );
4097
- (0, import_react25.useEffect)(() => {
4065
+ (0, import_react24.useEffect)(() => {
4098
4066
  if (!script || !entryEl) return;
4099
4067
  scriptRef.current?.remove();
4100
4068
  const doc = entryEl.ownerDocument;
@@ -4121,30 +4089,16 @@ function createDesignComponentConfig(registration) {
4121
4089
  );
4122
4090
  }
4123
4091
  }
4124
- const fieldContent = {};
4125
- for (const { name: name2, node } of nodeFields) {
4126
- fieldContent[name2] = node;
4127
- }
4128
4092
  return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_jsx_runtime32.Fragment, { children: [
4129
4093
  styles && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("style", { children: styles }),
4130
4094
  style && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("style", { children: style }),
4131
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { ref: setEntryEl, ...fieldValueAttrs, children: htmlToReactTree(patchedHtml, slotContent, fieldContent) })
4095
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { ref: setEntryEl, ...fieldValueAttrs, children: htmlToReactTree(patchedHtml, slotContent) })
4132
4096
  ] });
4133
4097
  }
4134
4098
  return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_jsx_runtime32.Fragment, { children: [
4135
4099
  styles && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("style", { children: styles }),
4136
4100
  style && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("style", { children: style }),
4137
4101
  editorEntryNode,
4138
- entryEl && nodeFields.map(({ name: name2, selector, node }) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
4139
- FieldPortal,
4140
- {
4141
- entry: entryEl,
4142
- html,
4143
- selector,
4144
- children: node
4145
- },
4146
- name2
4147
- )),
4148
4102
  entryEl && annotations.slots.map((slot) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
4149
4103
  SlotPortal,
4150
4104
  {
@@ -4163,7 +4117,7 @@ function createDesignComponentConfig(registration) {
4163
4117
 
4164
4118
  // src/lib/design/globals.tsx
4165
4119
  init_react_import();
4166
- var import_react26 = require("react");
4120
+ var import_react25 = require("react");
4167
4121
  var import_jsx_runtime33 = require("react/jsx-runtime");
4168
4122
  function getDynamicConfigGlobals(dynamicConfig) {
4169
4123
  if (typeof dynamicConfig !== "object" || dynamicConfig === null) return {};
@@ -4173,13 +4127,15 @@ function getDynamicConfigGlobals(dynamicConfig) {
4173
4127
  ...typeof script === "string" ? { script } : {}
4174
4128
  };
4175
4129
  }
4130
+ var INLINE_EDIT_RESET = ":where([data-puck-design] [contenteditable]){overflow-wrap:inherit;line-break:inherit;-webkit-nbsp-mode:inherit}";
4176
4131
  function DesignGlobals({
4177
4132
  styles,
4178
- script
4133
+ script,
4134
+ isEditing
4179
4135
  }) {
4180
- const scriptRef = (0, import_react26.useRef)(null);
4181
- const hostRef = (0, import_react26.useRef)(null);
4182
- (0, import_react26.useEffect)(() => {
4136
+ const scriptRef = (0, import_react25.useRef)(null);
4137
+ const hostRef = (0, import_react25.useRef)(null);
4138
+ (0, import_react25.useEffect)(() => {
4183
4139
  if (!script) return;
4184
4140
  scriptRef.current?.remove();
4185
4141
  const doc = hostRef.current?.ownerDocument ?? document;
@@ -4193,6 +4149,7 @@ function DesignGlobals({
4193
4149
  };
4194
4150
  }, [script]);
4195
4151
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
4152
+ isEditing && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("style", { children: INLINE_EDIT_RESET }),
4196
4153
  styles && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("style", { children: styles }),
4197
4154
  script && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { ref: hostRef, style: { display: "none" } })
4198
4155
  ] });
@@ -4205,7 +4162,14 @@ function withDesignGlobals(root) {
4205
4162
  const render = (props) => {
4206
4163
  const { styles, script } = getDynamicConfigGlobals(props._dynamicConfig);
4207
4164
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
4208
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(DesignGlobals, { styles, script }),
4165
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
4166
+ DesignGlobals,
4167
+ {
4168
+ styles,
4169
+ script,
4170
+ isEditing: props.puck?.isEditing
4171
+ }
4172
+ ),
4209
4173
  userRender ? userRender(props) : props.children
4210
4174
  ] });
4211
4175
  };
@@ -4319,7 +4283,7 @@ function syncDesignComponents(getPuck) {
4319
4283
  ensureDynamicConfigField(config);
4320
4284
  for (const [name, persistedReg] of Object.entries(persisted)) {
4321
4285
  const prev = lastSynced.get(name);
4322
- if (registrationStore.has(name) && (prev === persistedReg || registrationContentEqual(prev, persistedReg))) {
4286
+ if (registrationStore.has(name) && config.components[name] && (prev === persistedReg || registrationContentEqual(prev, persistedReg))) {
4323
4287
  lastSynced.set(name, persistedReg);
4324
4288
  continue;
4325
4289
  }
@@ -4343,18 +4307,17 @@ function syncDesignComponents(getPuck) {
4343
4307
  };
4344
4308
  }
4345
4309
  }
4346
- config.components[name].fields = {
4347
- ...getSlotFields(registration.html ?? ""),
4348
- ...getTransformableFields(registration)
4349
- };
4310
+ config.components[name].fields = getSlotFields(registration.html ?? "");
4350
4311
  }
4351
4312
  let hasRemovals = false;
4352
4313
  for (const [name] of registrationStore.getAll()) {
4353
4314
  if (!(name in persisted)) {
4354
4315
  registrationStore.delete(name);
4355
4316
  lastSynced.delete(name);
4356
- delete config.components[name];
4357
- hasRemovals = true;
4317
+ if (isDesignComponent(config.components[name])) {
4318
+ delete config.components[name];
4319
+ hasRemovals = true;
4320
+ }
4358
4321
  }
4359
4322
  }
4360
4323
  if (createdSettled) {
@@ -4474,12 +4437,145 @@ var waitForIframeReady = async (root, imageTimeout = 1e4) => {
4474
4437
  await waitForNextPaint(document2);
4475
4438
  };
4476
4439
 
4440
+ // src/lib/capture-screenshot.ts
4441
+ init_react_import();
4442
+ var import_html2canvas_pro = __toESM(require("html2canvas-pro"));
4443
+
4444
+ // src/lib/screenshot-size.ts
4445
+ init_react_import();
4446
+ var MAX_WEBP_DIMENSION = 16383;
4447
+ var CAPTURE_SCALE = 2;
4448
+ var getFitRatio = (width, height, dimensionLimit = MAX_WEBP_DIMENSION) => {
4449
+ if (width <= 0 || height <= 0) return 1;
4450
+ return Math.min(1, dimensionLimit / Math.max(width, height));
4451
+ };
4452
+ var getCaptureScale = (width, height, scale) => scale * getFitRatio(width * scale, height * scale);
4453
+ var scaleDimension = (value, ratio) => Math.max(1, Math.floor(value * ratio));
4454
+ var getDocumentSize = (root) => {
4455
+ const { body, documentElement } = root.ownerDocument;
4456
+ return {
4457
+ width: Math.max(
4458
+ body?.scrollWidth ?? 0,
4459
+ body?.offsetWidth ?? 0,
4460
+ body?.clientWidth ?? 0,
4461
+ documentElement?.scrollWidth ?? 0,
4462
+ documentElement?.offsetWidth ?? 0,
4463
+ documentElement?.clientWidth ?? 0
4464
+ ),
4465
+ height: Math.max(
4466
+ body?.scrollHeight ?? 0,
4467
+ body?.offsetHeight ?? 0,
4468
+ body?.clientHeight ?? 0,
4469
+ documentElement?.scrollHeight ?? 0,
4470
+ documentElement?.offsetHeight ?? 0,
4471
+ documentElement?.clientHeight ?? 0
4472
+ )
4473
+ };
4474
+ };
4475
+
4476
+ // src/lib/capture-screenshot.ts
4477
+ var editorOnlyClassPrefixes = ["_DraggableComponent--hover", "_ActionBar"];
4478
+ var releaseCanvas = (canvas) => {
4479
+ canvas.width = 0;
4480
+ canvas.height = 0;
4481
+ };
4482
+ var drawScaled = (source, ratio) => {
4483
+ const target = document.createElement("canvas");
4484
+ target.width = scaleDimension(source.width, ratio);
4485
+ target.height = scaleDimension(source.height, ratio);
4486
+ const ctx2 = target.getContext("2d");
4487
+ if (!ctx2) {
4488
+ releaseCanvas(target);
4489
+ return source;
4490
+ }
4491
+ ctx2.imageSmoothingEnabled = true;
4492
+ ctx2.imageSmoothingQuality = "high";
4493
+ ctx2.drawImage(source, 0, 0, target.width, target.height);
4494
+ return target;
4495
+ };
4496
+ var fitCanvas = (canvas) => {
4497
+ let remaining = getFitRatio(canvas.width, canvas.height);
4498
+ if (remaining >= 1) return canvas;
4499
+ let current = canvas;
4500
+ while (remaining < 0.5) {
4501
+ const next = drawScaled(current, 0.5);
4502
+ if (current !== canvas) releaseCanvas(current);
4503
+ current = next;
4504
+ remaining *= 2;
4505
+ }
4506
+ const fitted = drawScaled(current, remaining);
4507
+ if (current !== canvas) releaseCanvas(current);
4508
+ return fitted;
4509
+ };
4510
+ var toWebp = (canvas, quality) => new Promise((resolve, reject) => {
4511
+ canvas.toBlob(
4512
+ (blob) => {
4513
+ if (blob && blob.size > 0) {
4514
+ resolve(blob);
4515
+ return;
4516
+ }
4517
+ reject(
4518
+ new Error(
4519
+ `Could not encode a ${canvas.width}x${canvas.height} canvas as WebP`
4520
+ )
4521
+ );
4522
+ },
4523
+ "image/webp",
4524
+ quality
4525
+ );
4526
+ });
4527
+ var captureScreenshot = async (root, { scale = CAPTURE_SCALE, quality = 0.8, width } = {}) => {
4528
+ const documentSize = getDocumentSize(root);
4529
+ const safeWebPScale = getCaptureScale(
4530
+ width ?? documentSize.width,
4531
+ documentSize.height,
4532
+ scale
4533
+ );
4534
+ const canvas = await (0, import_html2canvas_pro.default)(root, {
4535
+ scale: safeWebPScale,
4536
+ backgroundColor: "#ffffff",
4537
+ ...width === void 0 ? {} : { width, windowWidth: width },
4538
+ foreignObjectRendering: false,
4539
+ imageTimeout: 3e4,
4540
+ logging: false,
4541
+ allowTaint: false,
4542
+ useCORS: true,
4543
+ scrollY: 0,
4544
+ ignoreElements: (el) => Array.from(el.classList).some(
4545
+ (c) => editorOnlyClassPrefixes.some((prefix) => c.startsWith(prefix))
4546
+ )
4547
+ });
4548
+ const fitted = fitCanvas(canvas);
4549
+ try {
4550
+ return await toWebp(fitted, quality);
4551
+ } finally {
4552
+ if (fitted !== canvas) releaseCanvas(fitted);
4553
+ releaseCanvas(canvas);
4554
+ }
4555
+ };
4556
+
4557
+ // src/lib/client-tool-response.ts
4558
+ init_react_import();
4559
+ async function postClientToolResponse(host, body) {
4560
+ const response = await fetch(`${host.replace(/\/+$/, "")}/tool`, {
4561
+ method: "POST",
4562
+ headers: { "Content-Type": "application/json" },
4563
+ body: JSON.stringify(body)
4564
+ });
4565
+ if (!response.ok) {
4566
+ throw new Error(
4567
+ `Client tool endpoint returned ${response.status} ${response.statusText}`.trim()
4568
+ );
4569
+ }
4570
+ }
4571
+
4477
4572
  // src/components/Chat/index.tsx
4478
4573
  var import_jsx_runtime35 = require("react/jsx-runtime");
4479
4574
  var q = (0, import_qler.default)();
4480
4575
  var DEFAULT_API_VERSION = "v2";
4481
4576
  var BUILD_OP_DEBOUNCE_MS = 1e3 / 60;
4482
- var PLUGIN_AI_VERSION = true ? "0.9.0-canary.d8bdabee" : "unknown";
4577
+ 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.";
4578
+ var PLUGIN_AI_VERSION = true ? "0.9.0-canary.fb1abcf5" : "unknown";
4483
4579
  var BENCHMARK = false;
4484
4580
  var prefixedUlid = (prefix = "") => `${prefix ? `${prefix}_` : ""}${(0, import_ulid.ulid)()}`;
4485
4581
  var getClassName18 = getClassNameFactory("Chat", styles_module_default);
@@ -4502,28 +4598,32 @@ function Chat2(props) {
4502
4598
  } = chat ?? {};
4503
4599
  const puckDispatch = usePuck((s) => s.dispatch);
4504
4600
  const getPuck = (0, import_core5.useGetPuck)();
4505
- const localChatId = (0, import_react28.useRef)("");
4506
- const inputRef = (0, import_react28.useRef)(null);
4507
- const pluginRef = (0, import_react28.useRef)(null);
4508
- const [mode, setMode] = (0, import_react28.useState)(defaultMode);
4509
- const modeRef = (0, import_react28.useRef)(mode);
4601
+ const localChatId = (0, import_react27.useRef)("");
4602
+ const inputRef = (0, import_react27.useRef)(null);
4603
+ const pluginRef = (0, import_react27.useRef)(null);
4604
+ const [mode, setMode] = (0, import_react27.useState)(defaultMode);
4605
+ const modeRef = (0, import_react27.useRef)(mode);
4510
4606
  const designModeVisible = typeof designMode === "boolean" ? designMode : designMode.visible ?? false;
4511
- (0, import_react28.useEffect)(() => {
4607
+ (0, import_react27.useEffect)(() => {
4512
4608
  modeRef.current = mode;
4513
4609
  }, [mode]);
4514
4610
  const puckData = usePuck((s) => s.appState.data);
4515
- (0, import_react28.useEffect)(() => {
4611
+ (0, import_react27.useEffect)(() => {
4516
4612
  syncDesignComponents(getPuck);
4517
4613
  }, [puckData, getPuck]);
4518
- const [error, setError] = (0, import_react28.useState)();
4519
- const [userError, setUserError] = (0, import_react28.useState)();
4520
- const [composerAttachments, setComposerAttachments] = (0, import_react28.useState)([]);
4521
- const removedAttachmentClientIdsRef = (0, import_react28.useRef)(/* @__PURE__ */ new Set());
4522
- const uploadControllersRef = (0, import_react28.useRef)(/* @__PURE__ */ new Map());
4523
- const pendingBuildOpsRef = (0, import_react28.useRef)([]);
4524
- const [toolStatus, setToolStatus] = (0, import_react28.useState)({});
4525
- const [subagentState, setSubagentState] = (0, import_react28.useState)({});
4526
- const attachmentConfig = (0, import_react28.useMemo)(
4614
+ const [error, setError] = (0, import_react27.useState)();
4615
+ const [userError, setUserError] = (0, import_react27.useState)();
4616
+ const clientToolEndpointUnavailableRef = (0, import_react27.useRef)(false);
4617
+ const stopChatRef = (0, import_react27.useRef)(null);
4618
+ const [composerAttachments, setComposerAttachments] = (0, import_react27.useState)([]);
4619
+ const removedAttachmentClientIdsRef = (0, import_react27.useRef)(/* @__PURE__ */ new Set());
4620
+ const uploadControllersRef = (0, import_react27.useRef)(/* @__PURE__ */ new Map());
4621
+ const pendingBuildOpsRef = (0, import_react27.useRef)([]);
4622
+ const lastStreamActivityRef = (0, import_react27.useRef)(0);
4623
+ const hiddenSinceRef = (0, import_react27.useRef)(null);
4624
+ const [toolStatus, setToolStatus] = (0, import_react27.useState)({});
4625
+ const [subagentState, setSubagentState] = (0, import_react27.useState)({});
4626
+ const attachmentConfig = (0, import_react27.useMemo)(
4527
4627
  () => ({
4528
4628
  accept: attachments?.accept ?? defaultAttachmentAccept,
4529
4629
  enabled: attachments?.enabled ?? true,
@@ -4533,14 +4633,14 @@ function Chat2(props) {
4533
4633
  }),
4534
4634
  [attachments, host]
4535
4635
  );
4536
- (0, import_react28.useEffect)(
4636
+ (0, import_react27.useEffect)(
4537
4637
  () => () => {
4538
4638
  uploadControllersRef.current.forEach((controller) => controller.abort());
4539
4639
  uploadControllersRef.current.clear();
4540
4640
  },
4541
4641
  []
4542
4642
  );
4543
- const deleteAttachment = (0, import_react28.useCallback)(
4643
+ const deleteAttachment = (0, import_react27.useCallback)(
4544
4644
  async (attachmentId) => {
4545
4645
  try {
4546
4646
  await fetch(`${attachmentConfig.endpoint}/${attachmentId}`, {
@@ -4552,7 +4652,7 @@ function Chat2(props) {
4552
4652
  },
4553
4653
  [attachmentConfig.endpoint]
4554
4654
  );
4555
- const uploadScreenshot = (0, import_react28.useCallback)(
4655
+ const uploadScreenshot = (0, import_react27.useCallback)(
4556
4656
  async (width, bucketUrl) => {
4557
4657
  try {
4558
4658
  const iframeDocument = document?.getElementById("preview-frame")?.contentDocument?.documentElement ?? null;
@@ -4560,24 +4660,7 @@ function Chat2(props) {
4560
4660
  throw new Error("Preview frame not found");
4561
4661
  }
4562
4662
  await waitForIframeReady(iframeDocument);
4563
- const canvas = await (0, import_html2canvas_pro.default)(iframeDocument, {
4564
- scale: 2,
4565
- backgroundColor: "#ffffff",
4566
- width,
4567
- windowWidth: width,
4568
- foreignObjectRendering: false,
4569
- imageTimeout: 3e4,
4570
- logging: false,
4571
- allowTaint: false,
4572
- useCORS: true,
4573
- scrollY: 0,
4574
- ignoreElements: (el) => Array.from(el.classList).some(
4575
- (c) => c.startsWith("_DraggableComponent--hover") || c.startsWith("_ActionBar")
4576
- )
4577
- });
4578
- const image = canvas.toDataURL("image/webp", 0.8);
4579
- const imageResponse = await fetch(image);
4580
- const blob = await imageResponse.blob();
4663
+ const blob = await captureScreenshot(iframeDocument, { width });
4581
4664
  const uploadResponse = await fetch(bucketUrl, {
4582
4665
  method: "PUT",
4583
4666
  body: blob
@@ -4624,15 +4707,50 @@ function Chat2(props) {
4624
4707
  BUILD_OP_DEBOUNCE_MS,
4625
4708
  { maxWait: BUILD_OP_DEBOUNCE_MS }
4626
4709
  );
4627
- const queueBuildOp = (0, import_react28.useCallback)(
4710
+ const queueBuildOp = (0, import_react27.useCallback)(
4628
4711
  (operation) => {
4629
4712
  pendingBuildOpsRef.current.push(operation);
4630
4713
  debouncedFlushBuildOps();
4631
4714
  },
4632
4715
  [debouncedFlushBuildOps]
4633
4716
  );
4634
- const processData = (0, import_react28.useCallback)(
4717
+ const handleClientToolEndpointUnavailable = (0, import_react27.useCallback)(() => {
4718
+ if (clientToolEndpointUnavailableRef.current) return;
4719
+ clientToolEndpointUnavailableRef.current = true;
4720
+ setError(CLIENT_TOOL_ENDPOINT_UNAVAILABLE_ERROR_MESSAGE);
4721
+ console.error(CLIENT_TOOL_ENDPOINT_UNAVAILABLE_ERROR_MESSAGE);
4722
+ setToolStatus((current) => {
4723
+ let changed = false;
4724
+ const next = {};
4725
+ for (const [id, toolState] of Object.entries(current)) {
4726
+ if (toolState.loading) {
4727
+ changed = true;
4728
+ next[id] = {
4729
+ ...toolState,
4730
+ loading: false,
4731
+ label: "Verification unavailable"
4732
+ };
4733
+ } else {
4734
+ next[id] = toolState;
4735
+ }
4736
+ }
4737
+ return changed ? next : current;
4738
+ });
4739
+ stopChatRef.current?.();
4740
+ }, []);
4741
+ const sendClientToolResponse = (0, import_react27.useCallback)(
4742
+ async (body) => {
4743
+ try {
4744
+ await postClientToolResponse(host, body);
4745
+ } catch {
4746
+ handleClientToolEndpointUnavailable();
4747
+ }
4748
+ },
4749
+ [handleClientToolEndpointUnavailable, host]
4750
+ );
4751
+ const processData = (0, import_react27.useCallback)(
4635
4752
  (dataPart) => {
4753
+ lastStreamActivityRef.current = Date.now();
4636
4754
  switch (dataPart.type) {
4637
4755
  case "data-new-chat-created": {
4638
4756
  localChatId.current = dataPart.data.chatId;
@@ -4663,13 +4781,9 @@ function Chat2(props) {
4663
4781
  debouncedFlushBuildOps.flush();
4664
4782
  q.wait().then(() => {
4665
4783
  const { appState } = getPuck();
4666
- return fetch(`${host}/tool`, {
4667
- method: "POST",
4668
- headers: { "Content-Type": "application/json" },
4669
- body: JSON.stringify({
4670
- id: requestId,
4671
- responses: [{ action, output: appState.data }]
4672
- })
4784
+ return sendClientToolResponse({
4785
+ id: requestId,
4786
+ responses: [{ action, output: appState.data }]
4673
4787
  });
4674
4788
  }).catch((e) => {
4675
4789
  console.error("Failed to respond to client request:", e);
@@ -4686,22 +4800,7 @@ function Chat2(props) {
4686
4800
  throw new Error("Preview frame not found");
4687
4801
  }
4688
4802
  await waitForIframeReady(iframeDocument);
4689
- const canvas = await (0, import_html2canvas_pro.default)(iframeDocument, {
4690
- scale: 2,
4691
- backgroundColor: "#ffffff",
4692
- foreignObjectRendering: false,
4693
- imageTimeout: 3e4,
4694
- logging: false,
4695
- allowTaint: false,
4696
- useCORS: true,
4697
- scrollY: 0,
4698
- ignoreElements: (el) => Array.from(el.classList).some(
4699
- (c) => c.startsWith("_DraggableComponent--hover") || c.startsWith("_ActionBar")
4700
- )
4701
- });
4702
- const image = canvas.toDataURL("image/webp", 0.8);
4703
- const imageResponse = await fetch(image);
4704
- const blob = await imageResponse.blob();
4803
+ const blob = await captureScreenshot(iframeDocument);
4705
4804
  const uploadResponse = await fetch(putUrl, {
4706
4805
  method: "PUT",
4707
4806
  body: blob
@@ -4711,16 +4810,16 @@ function Chat2(props) {
4711
4810
  `Upload failed with status ${uploadResponse.status}`
4712
4811
  );
4713
4812
  }
4714
- return fetch(`${host}/tool`, {
4715
- method: "POST",
4716
- headers: { "Content-Type": "application/json" },
4717
- body: JSON.stringify({
4718
- id: requestId,
4719
- responses: [{ action, output: { sizeBytes: blob.size } }]
4720
- })
4813
+ return sendClientToolResponse({
4814
+ id: requestId,
4815
+ responses: [{ action, output: { sizeBytes: blob.size } }]
4721
4816
  });
4722
4817
  }).catch((e) => {
4723
4818
  console.error("Failed to respond to client request:", e);
4819
+ return sendClientToolResponse({
4820
+ id: requestId,
4821
+ responses: [{ action, output: { sizeBytes: 0 } }]
4822
+ });
4724
4823
  });
4725
4824
  }
4726
4825
  return;
@@ -4764,6 +4863,7 @@ function Chat2(props) {
4764
4863
  getPuck,
4765
4864
  puckDispatch,
4766
4865
  queueBuildOp,
4866
+ sendClientToolResponse,
4767
4867
  uploadScreenshot
4768
4868
  ]
4769
4869
  );
@@ -4833,12 +4933,14 @@ function Chat2(props) {
4833
4933
  if (BENCHMARK) {
4834
4934
  console.timeEnd("chat");
4835
4935
  }
4836
- setError(e.message);
4936
+ if (!clientToolEndpointUnavailableRef.current) {
4937
+ setError(e.message);
4938
+ }
4837
4939
  },
4838
4940
  onFinish: (options) => {
4839
4941
  debouncedFlushBuildOps.flush();
4840
4942
  q.wait().then(() => {
4841
- if (!options.isAbort) {
4943
+ if (!options.isAbort || clientToolEndpointUnavailableRef.current) {
4842
4944
  puckDispatch({
4843
4945
  type: "set",
4844
4946
  state: getPuck().appState,
@@ -4853,18 +4955,69 @@ function Chat2(props) {
4853
4955
  });
4854
4956
  }
4855
4957
  });
4856
- (0, import_react28.useEffect)(
4857
- () => () => {
4958
+ (0, import_react27.useEffect)(() => {
4959
+ stopChatRef.current = stop;
4960
+ return () => {
4961
+ stopChatRef.current = null;
4858
4962
  stop();
4859
- },
4860
- [stop]
4861
- );
4862
- const [forcedStatus, setForcedStatus] = (0, import_react28.useState)();
4863
- const resolvedStatus = (0, import_react28.useMemo)(
4963
+ };
4964
+ }, [stop]);
4965
+ const statusRef = (0, import_react27.useRef)(status);
4966
+ (0, import_react27.useEffect)(() => {
4967
+ statusRef.current = status;
4968
+ }, [status]);
4969
+ (0, import_react27.useEffect)(() => {
4970
+ lastStreamActivityRef.current = Date.now();
4971
+ }, [messages]);
4972
+ (0, import_react27.useEffect)(() => {
4973
+ const FROZEN_HIDDEN_MS = 3 * 6e4;
4974
+ const RESUME_GRACE_MS = 12e3;
4975
+ const isStreaming = () => statusRef.current === "streaming" || statusRef.current === "submitted";
4976
+ let graceTimer;
4977
+ const recoverIfSevered = () => {
4978
+ if (!isStreaming()) return;
4979
+ if (Date.now() - lastStreamActivityRef.current < RESUME_GRACE_MS) return;
4980
+ stop();
4981
+ setToolStatus((current) => {
4982
+ let changed = false;
4983
+ const next = {};
4984
+ for (const [id, toolState] of Object.entries(current)) {
4985
+ if (toolState.loading) {
4986
+ changed = true;
4987
+ next[id] = { ...toolState, loading: false, label: "Interrupted" };
4988
+ } else {
4989
+ next[id] = toolState;
4990
+ }
4991
+ }
4992
+ return changed ? next : current;
4993
+ });
4994
+ };
4995
+ const onVisibilityChange = () => {
4996
+ if (document.hidden) {
4997
+ hiddenSinceRef.current = isStreaming() ? Date.now() : null;
4998
+ return;
4999
+ }
5000
+ const hiddenSince = hiddenSinceRef.current;
5001
+ hiddenSinceRef.current = null;
5002
+ if (graceTimer) clearTimeout(graceTimer);
5003
+ if (hiddenSince === null || Date.now() - hiddenSince < FROZEN_HIDDEN_MS) {
5004
+ return;
5005
+ }
5006
+ lastStreamActivityRef.current = 0;
5007
+ graceTimer = setTimeout(recoverIfSevered, RESUME_GRACE_MS);
5008
+ };
5009
+ document.addEventListener("visibilitychange", onVisibilityChange);
5010
+ return () => {
5011
+ document.removeEventListener("visibilitychange", onVisibilityChange);
5012
+ if (graceTimer) clearTimeout(graceTimer);
5013
+ };
5014
+ }, [stop]);
5015
+ const [forcedStatus, setForcedStatus] = (0, import_react27.useState)();
5016
+ const resolvedStatus = (0, import_react27.useMemo)(
4864
5017
  () => forcedStatus ?? status,
4865
5018
  [status, forcedStatus]
4866
5019
  );
4867
- (0, import_react28.useEffect)(() => {
5020
+ (0, import_react27.useEffect)(() => {
4868
5021
  if (!scrollTrackingId) return;
4869
5022
  dispatchScrollTrackingEvent({
4870
5023
  id: scrollTrackingId,
@@ -4877,7 +5030,7 @@ function Chat2(props) {
4877
5030
  });
4878
5031
  };
4879
5032
  }, [resolvedStatus, scrollTrackingId]);
4880
- (0, import_react28.useEffect)(() => {
5033
+ (0, import_react27.useEffect)(() => {
4881
5034
  window.__PUCK_AI = {
4882
5035
  processData,
4883
5036
  setMessages,
@@ -4909,6 +5062,8 @@ function Chat2(props) {
4909
5062
  if (!text && readyAttachments.length === 0) {
4910
5063
  return false;
4911
5064
  }
5065
+ clientToolEndpointUnavailableRef.current = false;
5066
+ setError("");
4912
5067
  setUserError("");
4913
5068
  if (chat?.onSubmit) {
4914
5069
  try {
@@ -5086,7 +5241,7 @@ function Chat2(props) {
5086
5241
  if (!target?.attachmentId) return;
5087
5242
  await deleteAttachment(target.attachmentId);
5088
5243
  };
5089
- const messagesWithStatuses = (0, import_react28.useMemo)(() => {
5244
+ const messagesWithStatuses = (0, import_react27.useMemo)(() => {
5090
5245
  return messages.map((msg) => {
5091
5246
  return {
5092
5247
  ...msg,
@@ -5148,6 +5303,7 @@ function Chat2(props) {
5148
5303
  ),
5149
5304
  error,
5150
5305
  handleRetry: () => {
5306
+ clientToolEndpointUnavailableRef.current = false;
5151
5307
  setError("");
5152
5308
  regenerate();
5153
5309
  },
@@ -5270,8 +5426,8 @@ function AttachmentComposer({
5270
5426
  maxFiles,
5271
5427
  onChange
5272
5428
  }) {
5273
- const id = (0, import_react28.useId)();
5274
- const inputId = (0, import_react28.useMemo)(() => `attachment-input-${id}`, []);
5429
+ const id = (0, import_react27.useId)();
5430
+ const inputId = (0, import_react27.useMemo)(() => `attachment-input-${id}`, []);
5275
5431
  return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: getClassName18("attachmentControls"), children: [
5276
5432
  /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
5277
5433
  "input",
@@ -5371,7 +5527,7 @@ function isScrolledIntoView(el) {
5371
5527
 
5372
5528
  // src/components/ScrollTracking/ScrollTracking.tsx
5373
5529
  init_react_import();
5374
- var import_react30 = require("react");
5530
+ var import_react29 = require("react");
5375
5531
 
5376
5532
  // src/lib/scroll-into-view-local.ts
5377
5533
  init_react_import();
@@ -5394,9 +5550,9 @@ function scrollIntoViewLocal(el, win, behavior = "smooth") {
5394
5550
 
5395
5551
  // src/lib/use-frame-mutation-observer.ts
5396
5552
  init_react_import();
5397
- var import_react29 = require("react");
5553
+ var import_react28 = require("react");
5398
5554
  function useFrameMutationObserver(callback) {
5399
- return (0, import_react29.useCallback)(() => {
5555
+ return (0, import_react28.useCallback)(() => {
5400
5556
  const frame = document?.getElementById(
5401
5557
  "preview-frame"
5402
5558
  );
@@ -5441,9 +5597,9 @@ var ScrollTracking = ({
5441
5597
  children,
5442
5598
  trackingId
5443
5599
  }) => {
5444
- const followedRefs = (0, import_react30.useRef)([]);
5445
- const enabledRef = (0, import_react30.useRef)(false);
5446
- (0, import_react30.useEffect)(() => {
5600
+ const followedRefs = (0, import_react29.useRef)([]);
5601
+ const enabledRef = (0, import_react29.useRef)(false);
5602
+ (0, import_react29.useEffect)(() => {
5447
5603
  return subscribeToScrollTrackingEvents(({ id, enabled }) => {
5448
5604
  if (id !== trackingId) return;
5449
5605
  enabledRef.current = enabled;
@@ -5462,7 +5618,7 @@ var ScrollTracking = ({
5462
5618
  });
5463
5619
  }
5464
5620
  });
5465
- (0, import_react30.useEffect)(() => {
5621
+ (0, import_react29.useEffect)(() => {
5466
5622
  follow();
5467
5623
  }, [follow]);
5468
5624
  return children;
@@ -5475,7 +5631,7 @@ var import_core7 = require("@puckeditor/core");
5475
5631
  // src/components/ManageComponents/index.tsx
5476
5632
  init_react_import();
5477
5633
  var import_core6 = require("@puckeditor/core");
5478
- var import_react31 = require("react");
5634
+ var import_react30 = require("react");
5479
5635
 
5480
5636
  // css-module:/home/runner/work/platform/platform/packages/plugin-ai/src/components/ManageComponents/styles.module.css#css-module
5481
5637
  init_react_import();
@@ -5484,7 +5640,7 @@ var styles_module_default10 = { "ManageComponents-cta": "_ManageComponents-cta_x
5484
5640
  // src/components/ManageComponents/index.tsx
5485
5641
  var import_jsx_runtime36 = require("react/jsx-runtime");
5486
5642
  var cx10 = getClassNameFactory2("ManageComponents", styles_module_default10);
5487
- var useRegistrations = () => (0, import_react31.useSyncExternalStore)(
5643
+ var useRegistrations = () => (0, import_react30.useSyncExternalStore)(
5488
5644
  registrationStore.subscribe,
5489
5645
  () => registrationStore.getAll(),
5490
5646
  () => registrationStore.getAll()
@@ -5492,9 +5648,9 @@ var useRegistrations = () => (0, import_react31.useSyncExternalStore)(
5492
5648
  function ManageModalContent({ onClose }) {
5493
5649
  const getPuck = (0, import_core6.useGetPuck)();
5494
5650
  const { visible, setVisible } = useModal();
5495
- const [confirmTarget, setConfirmTarget] = (0, import_react31.useState)(null);
5651
+ const [confirmTarget, setConfirmTarget] = (0, import_react30.useState)(null);
5496
5652
  const entries = useRegistrations();
5497
- (0, import_react31.useEffect)(() => {
5653
+ (0, import_react30.useEffect)(() => {
5498
5654
  if (!visible) onClose();
5499
5655
  }, [visible, onClose]);
5500
5656
  const handleDelete = (name) => {
@@ -5549,9 +5705,9 @@ function ManageModalContent({ onClose }) {
5549
5705
  ] }) });
5550
5706
  }
5551
5707
  function ManageComponentsCta() {
5552
- const [open, setOpen] = (0, import_react31.useState)(false);
5708
+ const [open, setOpen] = (0, import_react30.useState)(false);
5553
5709
  const entries = useRegistrations();
5554
- const handleClose = (0, import_react31.useCallback)(() => setOpen(false), []);
5710
+ const handleClose = (0, import_react30.useCallback)(() => setOpen(false), []);
5555
5711
  if (entries.length === 0) return null;
5556
5712
  return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
5557
5713
  /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("button", { className: cx10("cta"), onClick: () => setOpen(true), type: "button", children: [