@puckeditor/plugin-ai 0.8.2 → 0.8.3-canary.1003174b

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 +105 -28
  2. package/dist/index.mjs +105 -28
  3. package/package.json +11 -8
package/dist/index.js CHANGED
@@ -742,7 +742,7 @@ var import_ulid2 = require("ulid");
742
742
  // src/components/Chat/index.tsx
743
743
  init_react_import();
744
744
 
745
- // ../../node_modules/.pnpm/@ai-sdk+react@3.0.232_react@19.2.8_zod@4.4.3/node_modules/@ai-sdk/react/dist/index.mjs
745
+ // ../../node_modules/.pnpm/@ai-sdk+react@3.0.232_react@19.2.8_zod@3.25.76/node_modules/@ai-sdk/react/dist/index.mjs
746
746
  init_react_import();
747
747
  var import_ai = require("ai");
748
748
  var import_react4 = require("react");
@@ -3420,6 +3420,29 @@ var syncFieldValues = (entry, fields, props) => {
3420
3420
  });
3421
3421
  }
3422
3422
  };
3423
+ var claimedHostStyles = /* @__PURE__ */ new WeakMap();
3424
+ var claimHostStyles = (el, applyWhiteSpace) => {
3425
+ if (!claimedHostStyles.has(el)) {
3426
+ claimedHostStyles.set(el, {
3427
+ cursor: el.style.cursor,
3428
+ whiteSpace: applyWhiteSpace ? el.style.whiteSpace : null
3429
+ });
3430
+ }
3431
+ applyHostStyles(el);
3432
+ };
3433
+ var applyHostStyles = (el) => {
3434
+ const claimed = claimedHostStyles.get(el);
3435
+ if (!claimed) return;
3436
+ el.style.cursor = "text";
3437
+ if (claimed.whiteSpace !== null) el.style.whiteSpace = "pre-wrap";
3438
+ };
3439
+ var releaseHostStyles = (el) => {
3440
+ const claimed = claimedHostStyles.get(el);
3441
+ if (!claimed) return;
3442
+ el.style.cursor = claimed.cursor;
3443
+ if (claimed.whiteSpace !== null) el.style.whiteSpace = claimed.whiteSpace;
3444
+ claimedHostStyles.delete(el);
3445
+ };
3423
3446
  var attachInlineEditing = (el, {
3424
3447
  componentId,
3425
3448
  propPath,
@@ -3432,8 +3455,7 @@ var attachInlineEditing = (el, {
3432
3455
  const syncEditable = () => {
3433
3456
  el.contentEditable = hovering || focused ? mode === "richtext" ? "true" : "plaintext-only" : "false";
3434
3457
  };
3435
- const previousCursor = el.style.cursor;
3436
- el.style.cursor = "text";
3458
+ claimHostStyles(el, mode !== "richtext");
3437
3459
  syncEditable();
3438
3460
  let lastValidNumberText = el.innerText.replaceAll(/\n/gm, "");
3439
3461
  const handleInput = () => {
@@ -3511,7 +3533,7 @@ var attachInlineEditing = (el, {
3511
3533
  el.removeEventListener("mouseout", handleMouseOut, true);
3512
3534
  el.removeEventListener("focus", handleFocus);
3513
3535
  el.removeEventListener("blur", handleBlur);
3514
- el.style.cursor = previousCursor;
3536
+ releaseHostStyles(el);
3515
3537
  el.removeAttribute("contenteditable");
3516
3538
  cleanupPortal?.();
3517
3539
  };
@@ -3577,7 +3599,7 @@ var wireArrayItemEditing = ({
3577
3599
  const mode = inlineEditModeFor(field);
3578
3600
  const registration = wiring.get(target);
3579
3601
  if (registration?.propPath === propPath && registration.mode === mode) {
3580
- target.style.cursor = "text";
3602
+ applyHostStyles(target);
3581
3603
  continue;
3582
3604
  }
3583
3605
  registration?.cleanup();
@@ -3903,7 +3925,7 @@ function createDesignComponentConfig(registration) {
3903
3925
  morphed.current = { el: entryEl, html, attrSig };
3904
3926
  morphHtml(entryEl, html, attrFields);
3905
3927
  for (const el of fieldWiring.current.keys()) {
3906
- if (el.isConnected) el.style.cursor = "text";
3928
+ if (el.isConnected) applyHostStyles(el);
3907
3929
  }
3908
3930
  }
3909
3931
  syncFieldValues(entryEl, annotations.fields, props);
@@ -4062,9 +4084,11 @@ function getDynamicConfigGlobals(dynamicConfig) {
4062
4084
  ...typeof script === "string" ? { script } : {}
4063
4085
  };
4064
4086
  }
4087
+ var INLINE_EDIT_RESET = ":where([data-puck-design] [contenteditable]){overflow-wrap:inherit;line-break:inherit;-webkit-nbsp-mode:inherit}";
4065
4088
  function DesignGlobals({
4066
4089
  styles,
4067
- script
4090
+ script,
4091
+ isEditing
4068
4092
  }) {
4069
4093
  const scriptRef = (0, import_react25.useRef)(null);
4070
4094
  const hostRef = (0, import_react25.useRef)(null);
@@ -4082,6 +4106,7 @@ function DesignGlobals({
4082
4106
  };
4083
4107
  }, [script]);
4084
4108
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
4109
+ isEditing && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("style", { children: INLINE_EDIT_RESET }),
4085
4110
  styles && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("style", { children: styles }),
4086
4111
  script && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { ref: hostRef, style: { display: "none" } })
4087
4112
  ] });
@@ -4094,7 +4119,14 @@ function withDesignGlobals(root) {
4094
4119
  const render = (props) => {
4095
4120
  const { styles, script } = getDynamicConfigGlobals(props._dynamicConfig);
4096
4121
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
4097
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(DesignGlobals, { styles, script }),
4122
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
4123
+ DesignGlobals,
4124
+ {
4125
+ styles,
4126
+ script,
4127
+ isEditing: props.puck?.isEditing
4128
+ }
4129
+ ),
4098
4130
  userRender ? userRender(props) : props.children
4099
4131
  ] });
4100
4132
  };
@@ -4180,25 +4212,19 @@ function getRequestComponentConfigs(data) {
4180
4212
  }
4181
4213
  return components;
4182
4214
  }
4183
- function syncComponentList(getPuck, add, remove) {
4215
+ function syncDesignedComponentsCategory(getPuck, designedComponents, hadRemovals) {
4184
4216
  const { appState, dispatch } = getPuck();
4185
4217
  const componentList = { ...appState.ui.componentList };
4186
4218
  const existing = componentList[DESIGNED_CATEGORY];
4187
4219
  const current = existing?.components ?? [];
4188
- const removeSet = new Set(remove);
4189
- const newNames = add.filter((name) => !current.includes(name));
4190
- const hasRemovals = current.some((name) => removeSet.has(name));
4191
- if (newNames.length === 0 && !hasRemovals) return;
4192
- const components = [
4193
- ...current.filter((name) => !removeSet.has(name)),
4194
- ...newNames
4195
- ];
4220
+ const unchanged = current.length === designedComponents.length && current.every((name, i) => name === designedComponents[i]);
4221
+ if (unchanged && !hadRemovals) return;
4196
4222
  componentList[DESIGNED_CATEGORY] = {
4197
4223
  title: "Generated",
4198
4224
  expanded: true,
4199
4225
  ...existing,
4200
- components,
4201
- visible: components.length > 0
4226
+ components: designedComponents,
4227
+ visible: designedComponents.length > 0
4202
4228
  };
4203
4229
  dispatch({ type: "setUi", ui: { componentList } });
4204
4230
  }
@@ -4212,12 +4238,10 @@ function syncDesignComponents(getPuck) {
4212
4238
  }
4213
4239
  let createdSettled = ensureDesignGlobalsRoot(config);
4214
4240
  ensureDynamicConfigField(config);
4215
- const settledNames = [];
4216
4241
  for (const [name, persistedReg] of Object.entries(persisted)) {
4217
4242
  const prev = lastSynced.get(name);
4218
4243
  if (registrationStore.has(name) && (prev === persistedReg || registrationContentEqual(prev, persistedReg))) {
4219
4244
  lastSynced.set(name, persistedReg);
4220
- if (!persistedReg.streaming) settledNames.push(name);
4221
4245
  continue;
4222
4246
  }
4223
4247
  lastSynced.set(name, persistedReg);
@@ -4241,23 +4265,23 @@ function syncDesignComponents(getPuck) {
4241
4265
  }
4242
4266
  }
4243
4267
  config.components[name].fields = getSlotFields(registration.html ?? "");
4244
- if (isSettled) settledNames.push(name);
4245
4268
  }
4246
- const removedNames = [];
4269
+ let hasRemovals = false;
4247
4270
  for (const [name] of registrationStore.getAll()) {
4248
4271
  if (!(name in persisted)) {
4249
4272
  registrationStore.delete(name);
4250
4273
  lastSynced.delete(name);
4251
4274
  delete config.components[name];
4252
- removedNames.push(name);
4275
+ hasRemovals = true;
4253
4276
  }
4254
4277
  }
4255
4278
  if (createdSettled) {
4256
4279
  dispatch({ type: "setData", data: appState.data, recordHistory: false });
4257
4280
  }
4258
- if (settledNames.length > 0 || removedNames.length > 0) {
4259
- syncComponentList(getPuck, settledNames, removedNames);
4260
- }
4281
+ const designedComponents = Object.keys(persisted).filter(
4282
+ (name) => name in config.components
4283
+ );
4284
+ syncDesignedComponentsCategory(getPuck, designedComponents, hasRemovals);
4261
4285
  }
4262
4286
  function deleteDesignComponent(getPuck, name) {
4263
4287
  const { appState, config, dispatch } = getPuck();
@@ -4373,7 +4397,7 @@ var import_jsx_runtime35 = require("react/jsx-runtime");
4373
4397
  var q = (0, import_qler.default)();
4374
4398
  var DEFAULT_API_VERSION = "v2";
4375
4399
  var BUILD_OP_DEBOUNCE_MS = 1e3 / 60;
4376
- var PLUGIN_AI_VERSION = true ? "0.8.2" : "unknown";
4400
+ var PLUGIN_AI_VERSION = true ? "0.8.3-canary.1003174b" : "unknown";
4377
4401
  var BENCHMARK = false;
4378
4402
  var prefixedUlid = (prefix = "") => `${prefix ? `${prefix}_` : ""}${(0, import_ulid.ulid)()}`;
4379
4403
  var getClassName18 = getClassNameFactory("Chat", styles_module_default);
@@ -4415,6 +4439,8 @@ function Chat2(props) {
4415
4439
  const removedAttachmentClientIdsRef = (0, import_react27.useRef)(/* @__PURE__ */ new Set());
4416
4440
  const uploadControllersRef = (0, import_react27.useRef)(/* @__PURE__ */ new Map());
4417
4441
  const pendingBuildOpsRef = (0, import_react27.useRef)([]);
4442
+ const lastStreamActivityRef = (0, import_react27.useRef)(0);
4443
+ const hiddenSinceRef = (0, import_react27.useRef)(null);
4418
4444
  const [toolStatus, setToolStatus] = (0, import_react27.useState)({});
4419
4445
  const [subagentState, setSubagentState] = (0, import_react27.useState)({});
4420
4446
  const attachmentConfig = (0, import_react27.useMemo)(
@@ -4527,6 +4553,7 @@ function Chat2(props) {
4527
4553
  );
4528
4554
  const processData = (0, import_react27.useCallback)(
4529
4555
  (dataPart) => {
4556
+ lastStreamActivityRef.current = Date.now();
4530
4557
  switch (dataPart.type) {
4531
4558
  case "data-new-chat-created": {
4532
4559
  localChatId.current = dataPart.data.chatId;
@@ -4753,6 +4780,56 @@ function Chat2(props) {
4753
4780
  },
4754
4781
  [stop]
4755
4782
  );
4783
+ const statusRef = (0, import_react27.useRef)(status);
4784
+ (0, import_react27.useEffect)(() => {
4785
+ statusRef.current = status;
4786
+ }, [status]);
4787
+ (0, import_react27.useEffect)(() => {
4788
+ lastStreamActivityRef.current = Date.now();
4789
+ }, [messages]);
4790
+ (0, import_react27.useEffect)(() => {
4791
+ const FROZEN_HIDDEN_MS = 3 * 6e4;
4792
+ const RESUME_GRACE_MS = 12e3;
4793
+ const isStreaming = () => statusRef.current === "streaming" || statusRef.current === "submitted";
4794
+ let graceTimer;
4795
+ const recoverIfSevered = () => {
4796
+ if (!isStreaming()) return;
4797
+ if (Date.now() - lastStreamActivityRef.current < RESUME_GRACE_MS) return;
4798
+ stop();
4799
+ setToolStatus((current) => {
4800
+ let changed = false;
4801
+ const next = {};
4802
+ for (const [id, toolState] of Object.entries(current)) {
4803
+ if (toolState.loading) {
4804
+ changed = true;
4805
+ next[id] = { ...toolState, loading: false, label: "Interrupted" };
4806
+ } else {
4807
+ next[id] = toolState;
4808
+ }
4809
+ }
4810
+ return changed ? next : current;
4811
+ });
4812
+ };
4813
+ const onVisibilityChange = () => {
4814
+ if (document.hidden) {
4815
+ hiddenSinceRef.current = isStreaming() ? Date.now() : null;
4816
+ return;
4817
+ }
4818
+ const hiddenSince = hiddenSinceRef.current;
4819
+ hiddenSinceRef.current = null;
4820
+ if (graceTimer) clearTimeout(graceTimer);
4821
+ if (hiddenSince === null || Date.now() - hiddenSince < FROZEN_HIDDEN_MS) {
4822
+ return;
4823
+ }
4824
+ lastStreamActivityRef.current = 0;
4825
+ graceTimer = setTimeout(recoverIfSevered, RESUME_GRACE_MS);
4826
+ };
4827
+ document.addEventListener("visibilitychange", onVisibilityChange);
4828
+ return () => {
4829
+ document.removeEventListener("visibilitychange", onVisibilityChange);
4830
+ if (graceTimer) clearTimeout(graceTimer);
4831
+ };
4832
+ }, [stop]);
4756
4833
  const [forcedStatus, setForcedStatus] = (0, import_react27.useState)();
4757
4834
  const resolvedStatus = (0, import_react27.useMemo)(
4758
4835
  () => forcedStatus ?? status,
package/dist/index.mjs CHANGED
@@ -729,7 +729,7 @@ import { ulid as ulid2 } from "ulid";
729
729
  // src/components/Chat/index.tsx
730
730
  init_react_import();
731
731
 
732
- // ../../node_modules/.pnpm/@ai-sdk+react@3.0.232_react@19.2.8_zod@4.4.3/node_modules/@ai-sdk/react/dist/index.mjs
732
+ // ../../node_modules/.pnpm/@ai-sdk+react@3.0.232_react@19.2.8_zod@3.25.76/node_modules/@ai-sdk/react/dist/index.mjs
733
733
  init_react_import();
734
734
  var import_throttleit = __toESM(require_throttleit(), 1);
735
735
  import {
@@ -3452,6 +3452,29 @@ var syncFieldValues = (entry, fields, props) => {
3452
3452
  });
3453
3453
  }
3454
3454
  };
3455
+ var claimedHostStyles = /* @__PURE__ */ new WeakMap();
3456
+ var claimHostStyles = (el, applyWhiteSpace) => {
3457
+ if (!claimedHostStyles.has(el)) {
3458
+ claimedHostStyles.set(el, {
3459
+ cursor: el.style.cursor,
3460
+ whiteSpace: applyWhiteSpace ? el.style.whiteSpace : null
3461
+ });
3462
+ }
3463
+ applyHostStyles(el);
3464
+ };
3465
+ var applyHostStyles = (el) => {
3466
+ const claimed = claimedHostStyles.get(el);
3467
+ if (!claimed) return;
3468
+ el.style.cursor = "text";
3469
+ if (claimed.whiteSpace !== null) el.style.whiteSpace = "pre-wrap";
3470
+ };
3471
+ var releaseHostStyles = (el) => {
3472
+ const claimed = claimedHostStyles.get(el);
3473
+ if (!claimed) return;
3474
+ el.style.cursor = claimed.cursor;
3475
+ if (claimed.whiteSpace !== null) el.style.whiteSpace = claimed.whiteSpace;
3476
+ claimedHostStyles.delete(el);
3477
+ };
3455
3478
  var attachInlineEditing = (el, {
3456
3479
  componentId,
3457
3480
  propPath,
@@ -3464,8 +3487,7 @@ var attachInlineEditing = (el, {
3464
3487
  const syncEditable = () => {
3465
3488
  el.contentEditable = hovering || focused ? mode === "richtext" ? "true" : "plaintext-only" : "false";
3466
3489
  };
3467
- const previousCursor = el.style.cursor;
3468
- el.style.cursor = "text";
3490
+ claimHostStyles(el, mode !== "richtext");
3469
3491
  syncEditable();
3470
3492
  let lastValidNumberText = el.innerText.replaceAll(/\n/gm, "");
3471
3493
  const handleInput = () => {
@@ -3543,7 +3565,7 @@ var attachInlineEditing = (el, {
3543
3565
  el.removeEventListener("mouseout", handleMouseOut, true);
3544
3566
  el.removeEventListener("focus", handleFocus);
3545
3567
  el.removeEventListener("blur", handleBlur);
3546
- el.style.cursor = previousCursor;
3568
+ releaseHostStyles(el);
3547
3569
  el.removeAttribute("contenteditable");
3548
3570
  cleanupPortal?.();
3549
3571
  };
@@ -3609,7 +3631,7 @@ var wireArrayItemEditing = ({
3609
3631
  const mode = inlineEditModeFor(field);
3610
3632
  const registration = wiring.get(target);
3611
3633
  if (registration?.propPath === propPath && registration.mode === mode) {
3612
- target.style.cursor = "text";
3634
+ applyHostStyles(target);
3613
3635
  continue;
3614
3636
  }
3615
3637
  registration?.cleanup();
@@ -3935,7 +3957,7 @@ function createDesignComponentConfig(registration) {
3935
3957
  morphed.current = { el: entryEl, html, attrSig };
3936
3958
  morphHtml(entryEl, html, attrFields);
3937
3959
  for (const el of fieldWiring.current.keys()) {
3938
- if (el.isConnected) el.style.cursor = "text";
3960
+ if (el.isConnected) applyHostStyles(el);
3939
3961
  }
3940
3962
  }
3941
3963
  syncFieldValues(entryEl, annotations.fields, props);
@@ -4094,9 +4116,11 @@ function getDynamicConfigGlobals(dynamicConfig) {
4094
4116
  ...typeof script === "string" ? { script } : {}
4095
4117
  };
4096
4118
  }
4119
+ var INLINE_EDIT_RESET = ":where([data-puck-design] [contenteditable]){overflow-wrap:inherit;line-break:inherit;-webkit-nbsp-mode:inherit}";
4097
4120
  function DesignGlobals({
4098
4121
  styles,
4099
- script
4122
+ script,
4123
+ isEditing
4100
4124
  }) {
4101
4125
  const scriptRef = useRef12(null);
4102
4126
  const hostRef = useRef12(null);
@@ -4114,6 +4138,7 @@ function DesignGlobals({
4114
4138
  };
4115
4139
  }, [script]);
4116
4140
  return /* @__PURE__ */ jsxs19(Fragment8, { children: [
4141
+ isEditing && /* @__PURE__ */ jsx33("style", { children: INLINE_EDIT_RESET }),
4117
4142
  styles && /* @__PURE__ */ jsx33("style", { children: styles }),
4118
4143
  script && /* @__PURE__ */ jsx33("div", { ref: hostRef, style: { display: "none" } })
4119
4144
  ] });
@@ -4126,7 +4151,14 @@ function withDesignGlobals(root) {
4126
4151
  const render = (props) => {
4127
4152
  const { styles, script } = getDynamicConfigGlobals(props._dynamicConfig);
4128
4153
  return /* @__PURE__ */ jsxs19(Fragment8, { children: [
4129
- /* @__PURE__ */ jsx33(DesignGlobals, { styles, script }),
4154
+ /* @__PURE__ */ jsx33(
4155
+ DesignGlobals,
4156
+ {
4157
+ styles,
4158
+ script,
4159
+ isEditing: props.puck?.isEditing
4160
+ }
4161
+ ),
4130
4162
  userRender ? userRender(props) : props.children
4131
4163
  ] });
4132
4164
  };
@@ -4212,25 +4244,19 @@ function getRequestComponentConfigs(data) {
4212
4244
  }
4213
4245
  return components;
4214
4246
  }
4215
- function syncComponentList(getPuck, add, remove) {
4247
+ function syncDesignedComponentsCategory(getPuck, designedComponents, hadRemovals) {
4216
4248
  const { appState, dispatch } = getPuck();
4217
4249
  const componentList = { ...appState.ui.componentList };
4218
4250
  const existing = componentList[DESIGNED_CATEGORY];
4219
4251
  const current = existing?.components ?? [];
4220
- const removeSet = new Set(remove);
4221
- const newNames = add.filter((name) => !current.includes(name));
4222
- const hasRemovals = current.some((name) => removeSet.has(name));
4223
- if (newNames.length === 0 && !hasRemovals) return;
4224
- const components = [
4225
- ...current.filter((name) => !removeSet.has(name)),
4226
- ...newNames
4227
- ];
4252
+ const unchanged = current.length === designedComponents.length && current.every((name, i) => name === designedComponents[i]);
4253
+ if (unchanged && !hadRemovals) return;
4228
4254
  componentList[DESIGNED_CATEGORY] = {
4229
4255
  title: "Generated",
4230
4256
  expanded: true,
4231
4257
  ...existing,
4232
- components,
4233
- visible: components.length > 0
4258
+ components: designedComponents,
4259
+ visible: designedComponents.length > 0
4234
4260
  };
4235
4261
  dispatch({ type: "setUi", ui: { componentList } });
4236
4262
  }
@@ -4244,12 +4270,10 @@ function syncDesignComponents(getPuck) {
4244
4270
  }
4245
4271
  let createdSettled = ensureDesignGlobalsRoot(config);
4246
4272
  ensureDynamicConfigField(config);
4247
- const settledNames = [];
4248
4273
  for (const [name, persistedReg] of Object.entries(persisted)) {
4249
4274
  const prev = lastSynced.get(name);
4250
4275
  if (registrationStore.has(name) && (prev === persistedReg || registrationContentEqual(prev, persistedReg))) {
4251
4276
  lastSynced.set(name, persistedReg);
4252
- if (!persistedReg.streaming) settledNames.push(name);
4253
4277
  continue;
4254
4278
  }
4255
4279
  lastSynced.set(name, persistedReg);
@@ -4273,23 +4297,23 @@ function syncDesignComponents(getPuck) {
4273
4297
  }
4274
4298
  }
4275
4299
  config.components[name].fields = getSlotFields(registration.html ?? "");
4276
- if (isSettled) settledNames.push(name);
4277
4300
  }
4278
- const removedNames = [];
4301
+ let hasRemovals = false;
4279
4302
  for (const [name] of registrationStore.getAll()) {
4280
4303
  if (!(name in persisted)) {
4281
4304
  registrationStore.delete(name);
4282
4305
  lastSynced.delete(name);
4283
4306
  delete config.components[name];
4284
- removedNames.push(name);
4307
+ hasRemovals = true;
4285
4308
  }
4286
4309
  }
4287
4310
  if (createdSettled) {
4288
4311
  dispatch({ type: "setData", data: appState.data, recordHistory: false });
4289
4312
  }
4290
- if (settledNames.length > 0 || removedNames.length > 0) {
4291
- syncComponentList(getPuck, settledNames, removedNames);
4292
- }
4313
+ const designedComponents = Object.keys(persisted).filter(
4314
+ (name) => name in config.components
4315
+ );
4316
+ syncDesignedComponentsCategory(getPuck, designedComponents, hasRemovals);
4293
4317
  }
4294
4318
  function deleteDesignComponent(getPuck, name) {
4295
4319
  const { appState, config, dispatch } = getPuck();
@@ -4405,7 +4429,7 @@ import { Fragment as Fragment9, jsx as jsx35, jsxs as jsxs20 } from "react/jsx-r
4405
4429
  var q = qler();
4406
4430
  var DEFAULT_API_VERSION = "v2";
4407
4431
  var BUILD_OP_DEBOUNCE_MS = 1e3 / 60;
4408
- var PLUGIN_AI_VERSION = true ? "0.8.2" : "unknown";
4432
+ var PLUGIN_AI_VERSION = true ? "0.8.3-canary.1003174b" : "unknown";
4409
4433
  var BENCHMARK = false;
4410
4434
  var prefixedUlid = (prefix = "") => `${prefix ? `${prefix}_` : ""}${ulid()}`;
4411
4435
  var getClassName18 = getClassNameFactory("Chat", styles_module_default);
@@ -4447,6 +4471,8 @@ function Chat2(props) {
4447
4471
  const removedAttachmentClientIdsRef = useRef13(/* @__PURE__ */ new Set());
4448
4472
  const uploadControllersRef = useRef13(/* @__PURE__ */ new Map());
4449
4473
  const pendingBuildOpsRef = useRef13([]);
4474
+ const lastStreamActivityRef = useRef13(0);
4475
+ const hiddenSinceRef = useRef13(null);
4450
4476
  const [toolStatus, setToolStatus] = useState16({});
4451
4477
  const [subagentState, setSubagentState] = useState16({});
4452
4478
  const attachmentConfig = useMemo6(
@@ -4559,6 +4585,7 @@ function Chat2(props) {
4559
4585
  );
4560
4586
  const processData = useCallback6(
4561
4587
  (dataPart) => {
4588
+ lastStreamActivityRef.current = Date.now();
4562
4589
  switch (dataPart.type) {
4563
4590
  case "data-new-chat-created": {
4564
4591
  localChatId.current = dataPart.data.chatId;
@@ -4785,6 +4812,56 @@ function Chat2(props) {
4785
4812
  },
4786
4813
  [stop]
4787
4814
  );
4815
+ const statusRef = useRef13(status);
4816
+ useEffect13(() => {
4817
+ statusRef.current = status;
4818
+ }, [status]);
4819
+ useEffect13(() => {
4820
+ lastStreamActivityRef.current = Date.now();
4821
+ }, [messages]);
4822
+ useEffect13(() => {
4823
+ const FROZEN_HIDDEN_MS = 3 * 6e4;
4824
+ const RESUME_GRACE_MS = 12e3;
4825
+ const isStreaming = () => statusRef.current === "streaming" || statusRef.current === "submitted";
4826
+ let graceTimer;
4827
+ const recoverIfSevered = () => {
4828
+ if (!isStreaming()) return;
4829
+ if (Date.now() - lastStreamActivityRef.current < RESUME_GRACE_MS) return;
4830
+ stop();
4831
+ setToolStatus((current) => {
4832
+ let changed = false;
4833
+ const next = {};
4834
+ for (const [id, toolState] of Object.entries(current)) {
4835
+ if (toolState.loading) {
4836
+ changed = true;
4837
+ next[id] = { ...toolState, loading: false, label: "Interrupted" };
4838
+ } else {
4839
+ next[id] = toolState;
4840
+ }
4841
+ }
4842
+ return changed ? next : current;
4843
+ });
4844
+ };
4845
+ const onVisibilityChange = () => {
4846
+ if (document.hidden) {
4847
+ hiddenSinceRef.current = isStreaming() ? Date.now() : null;
4848
+ return;
4849
+ }
4850
+ const hiddenSince = hiddenSinceRef.current;
4851
+ hiddenSinceRef.current = null;
4852
+ if (graceTimer) clearTimeout(graceTimer);
4853
+ if (hiddenSince === null || Date.now() - hiddenSince < FROZEN_HIDDEN_MS) {
4854
+ return;
4855
+ }
4856
+ lastStreamActivityRef.current = 0;
4857
+ graceTimer = setTimeout(recoverIfSevered, RESUME_GRACE_MS);
4858
+ };
4859
+ document.addEventListener("visibilitychange", onVisibilityChange);
4860
+ return () => {
4861
+ document.removeEventListener("visibilitychange", onVisibilityChange);
4862
+ if (graceTimer) clearTimeout(graceTimer);
4863
+ };
4864
+ }, [stop]);
4788
4865
  const [forcedStatus, setForcedStatus] = useState16();
4789
4866
  const resolvedStatus = useMemo6(
4790
4867
  () => forcedStatus ?? status,
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@puckeditor/plugin-ai",
3
- "version": "0.8.2",
3
+ "version": "0.8.3-canary.1003174b",
4
4
  "author": "Chris Villa <chris@puckeditor.com>",
5
- "repository": "puckeditor/puck",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "puckeditor/puck"
8
+ },
6
9
  "bugs": "https://github.com/puckeditor/puck/issues",
7
10
  "homepage": "https://puckeditor.com",
8
11
  "keywords": [
@@ -49,12 +52,12 @@
49
52
  "tsup": "^8.2.4",
50
53
  "typescript": "^5.5.4",
51
54
  "vitest": "^4.1.0",
52
- "@puckeditor/platform-types": "0.8.2",
53
- "eslint-config-custom": "0.8.2",
54
- "@puckeditor/ai-types": "0.8.2",
55
- "tsconfig": "0.8.2",
56
- "tsup-config": "0.8.2",
57
- "reducer": "0.8.2"
55
+ "@puckeditor/ai-types": "0.8.3-canary.1003174b",
56
+ "@puckeditor/platform-types": "0.8.3-canary.1003174b",
57
+ "eslint-config-custom": "0.8.3-canary.1003174b",
58
+ "reducer": "0.8.3-canary.1003174b",
59
+ "tsconfig": "0.8.3-canary.1003174b",
60
+ "tsup-config": "0.8.3-canary.1003174b"
58
61
  },
59
62
  "peerDependencies": {
60
63
  "@puckeditor/core": "^0",