@coffer-org/plugin-webchat 2.2.4 → 2.2.6

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.
@@ -658,6 +658,19 @@ var Plus = createLucideIcon("plus", [["path", {
658
658
  * This source code is licensed under the ISC license.
659
659
  * See the LICENSE file in the root directory of this source tree.
660
660
  */
661
+ var RotateCcw = createLucideIcon("rotate-ccw", [["path", {
662
+ d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",
663
+ key: "1357e3"
664
+ }], ["path", {
665
+ d: "M3 3v5h5",
666
+ key: "1xhq8a"
667
+ }]]);
668
+ /**
669
+ * @license lucide-react v1.24.0 - ISC
670
+ *
671
+ * This source code is licensed under the ISC license.
672
+ * See the LICENSE file in the root directory of this source tree.
673
+ */
661
674
  var Send = createLucideIcon("send", [["path", {
662
675
  d: "M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z",
663
676
  key: "1ffxy3"
@@ -29297,7 +29310,7 @@ var Bubble = memo(function Bubble({ role, text, reasoning, attachments }) {
29297
29310
  });
29298
29311
  //#endregion
29299
29312
  //#region src/ui/ChatComposer.tsx
29300
- function ChatComposer({ streaming, onSend, onStop, attachments, uploading, uploadError, uploadProgress, onPickFiles, onRemoveAttachment }) {
29313
+ function ChatComposer({ streaming, onSend, onStop, attachments, uploading, uploadError, uploadProgress, canRetryUpload, onRetryUpload, onPickFiles, onRemoveAttachment }) {
29301
29314
  const { t } = useTranslation();
29302
29315
  const [text, setText] = useState("");
29303
29316
  function submit() {
@@ -29356,9 +29369,21 @@ function ChatComposer({ streaming, onSend, onStop, attachments, uploading, uploa
29356
29369
  })
29357
29370
  ]
29358
29371
  }),
29359
- uploadError && /* @__PURE__ */ jsx("span", {
29360
- className: "text-xs text-danger",
29361
- children: t(uploadError)
29372
+ uploadError && /* @__PURE__ */ jsxs("div", {
29373
+ className: "flex min-w-0 flex-1 items-center gap-2 text-xs text-danger",
29374
+ children: [/* @__PURE__ */ jsx("span", {
29375
+ className: "min-w-0 flex-1",
29376
+ children: t(uploadError)
29377
+ }), canRetryUpload && /* @__PURE__ */ jsxs("button", {
29378
+ type: "button",
29379
+ className: "inline-flex shrink-0 items-center gap-1 rounded px-1.5 py-0.5 font-medium text-accent hover:bg-elevated",
29380
+ onClick: onRetryUpload,
29381
+ children: [
29382
+ /* @__PURE__ */ jsx(RotateCcw, { size: 12 }),
29383
+ " ",
29384
+ t("webchat.retryUpload")
29385
+ ]
29386
+ })]
29362
29387
  })
29363
29388
  ]
29364
29389
  }), /* @__PURE__ */ jsxs("div", {
@@ -29509,6 +29534,7 @@ function ChatWidget() {
29509
29534
  const [uploading, setUploading] = useState(false);
29510
29535
  const [uploadProgress, setUploadProgress] = useState(0);
29511
29536
  const [uploadError, setUploadError] = useState(null);
29537
+ const [retryFiles, setRetryFiles] = useState([]);
29512
29538
  const wide = useMinWidth("sm", 640);
29513
29539
  const wideNav = useMinWidth("nav", 768);
29514
29540
  const onChatNavigate = useCallback(() => {
@@ -29545,14 +29571,17 @@ function ChatWidget() {
29545
29571
  chat.newConversation();
29546
29572
  setAttachments([]);
29547
29573
  setUploadError(null);
29574
+ setRetryFiles([]);
29548
29575
  setThreadsOpen(false);
29549
29576
  }
29550
29577
  const pickFiles = useCallback(async (files) => {
29551
29578
  if (!files.length) return;
29552
29579
  setUploadError(null);
29580
+ setRetryFiles([]);
29553
29581
  setUploading(true);
29554
29582
  setUploadProgress(0);
29555
29583
  const added = [];
29584
+ const failedFiles = [];
29556
29585
  let error = null;
29557
29586
  for (const file of files) {
29558
29587
  if (file.size > MAX_CHAT_UPLOAD_BYTES) {
@@ -29566,16 +29595,24 @@ function ChatWidget() {
29566
29595
  ...rec,
29567
29596
  label: file.name
29568
29597
  });
29569
- else error ??= "webchat.uploadFailed";
29598
+ else {
29599
+ failedFiles.push(file);
29600
+ error ??= "webchat.uploadFailed";
29601
+ }
29570
29602
  } catch {
29603
+ failedFiles.push(file);
29571
29604
  error ??= "webchat.uploadFailed";
29572
29605
  }
29573
29606
  }
29574
29607
  if (added.length) setAttachments((prev) => [...prev, ...added]);
29575
29608
  setUploading(false);
29576
29609
  setUploadProgress(0);
29610
+ setRetryFiles(failedFiles);
29577
29611
  if (error) setUploadError(error);
29578
29612
  }, []);
29613
+ const retryUploads = useCallback(() => {
29614
+ if (retryFiles.length) pickFiles(retryFiles);
29615
+ }, [pickFiles, retryFiles]);
29579
29616
  const hasConversation = chat.messages.length > 0 || chat.pending !== null;
29580
29617
  const header = /* @__PURE__ */ jsxs("div", {
29581
29618
  className: "flex shrink-0 items-center justify-between border-b border-border px-3 py-2",
@@ -29634,6 +29671,8 @@ function ChatWidget() {
29634
29671
  uploading,
29635
29672
  uploadProgress,
29636
29673
  uploadError,
29674
+ canRetryUpload: retryFiles.length > 0 && !uploading,
29675
+ onRetryUpload: retryUploads,
29637
29676
  onPickFiles: (files) => void pickFiles(Array.from(files)),
29638
29677
  onRemoveAttachment: (index) => setAttachments((prev) => prev.filter((_, i) => i !== index)),
29639
29678
  onSend: (text, refs) => {
@@ -29641,6 +29680,7 @@ function ChatWidget() {
29641
29680
  if (accepted) {
29642
29681
  setAttachments([]);
29643
29682
  setUploadError(null);
29683
+ setRetryFiles([]);
29644
29684
  }
29645
29685
  return accepted;
29646
29686
  },
@@ -29685,6 +29725,7 @@ function ChatWidget() {
29685
29725
  chat.open(id);
29686
29726
  setAttachments([]);
29687
29727
  setUploadError(null);
29728
+ setRetryFiles([]);
29688
29729
  setThreadsOpen(false);
29689
29730
  },
29690
29731
  onNew: startNew,
package/dist/schema.js CHANGED
@@ -6426,15 +6426,23 @@ function check(raw) {
6426
6426
  const required = o.required ?? false;
6427
6427
  const multiple = o.multiple ?? false;
6428
6428
  const slots = o.slots && o.slots.length > 0 ? o.slots : [""];
6429
+ const contentFields = o.fields?.length ? o.fields : [string({
6430
+ key: "text",
6431
+ label: "core.fields.text"
6432
+ })];
6433
+ const contentKeys = /* @__PURE__ */ new Set();
6434
+ for (const f of contentFields) {
6435
+ if (contentKeys.has(f.key)) throw new Error(`[field.check] duplicate content field '${f.key}'`);
6436
+ if (/^check\d+$/.test(f.key)) throw new Error(`[field.check] content field '${f.key}' is reserved for checkbox slots`);
6437
+ if (!multiple && (f.type.virtual || f.type.columns)) throw new Error(`[field.check] single content field '${f.key}' must be a scalar stored field`);
6438
+ contentKeys.add(f.key);
6439
+ }
6440
+ const checkFields = slots.map((label, i) => boolean({
6441
+ key: `check${i}`,
6442
+ label: label || "core.fields.check"
6443
+ }));
6429
6444
  if (multiple) {
6430
- const fields = slots.map((label, i) => boolean({
6431
- key: `check${i}`,
6432
- label: label || "core.fields.check"
6433
- }));
6434
- fields.push(string({
6435
- key: "text",
6436
- label: "core.fields.text"
6437
- }));
6445
+ const fields = [...checkFields, ...contentFields];
6438
6446
  return group({
6439
6447
  key: o.key,
6440
6448
  label: o.label ?? o.key,
@@ -6444,8 +6452,12 @@ function check(raw) {
6444
6452
  view: { kind: "checklist" }
6445
6453
  });
6446
6454
  }
6447
- const shape = { text: string$1() };
6448
- const columns = { text: "text" };
6455
+ const shape = {};
6456
+ const columns = {};
6457
+ for (const f of contentFields) {
6458
+ shape[f.key] = f.type.zod;
6459
+ columns[f.key] = f.type.column;
6460
+ }
6449
6461
  slots.forEach((_, i) => {
6450
6462
  shape[`check${i}`] = boolean$1();
6451
6463
  columns[`check${i}`] = "boolean";
@@ -6471,7 +6483,13 @@ function check(raw) {
6471
6483
  required,
6472
6484
  prim: "check",
6473
6485
  column: "text",
6474
- hints: { slots },
6486
+ hints: {
6487
+ slots,
6488
+ fields: contentFields.map((f) => ({
6489
+ key: f.key,
6490
+ ...toClient(f.type)
6491
+ }))
6492
+ },
6475
6493
  columns,
6476
6494
  zod: optionalize(s, required)
6477
6495
  });
package/dist/web.js CHANGED
@@ -11,7 +11,7 @@ import { definePluginUI } from "@coffer-org/web-sdk";
11
11
  var ui_default = definePluginUI({ slots: [{
12
12
  slot: "overlay",
13
13
  id: "webchat",
14
- load: () => import("./ChatWidget-DueGsCOz.js")
14
+ load: () => import("./ChatWidget-By7bmS3U.js")
15
15
  }] });
16
16
  //#endregion
17
17
  export { ui_default as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coffer-org/plugin-webchat",
3
- "version": "2.2.4",
3
+ "version": "2.2.6",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=24"
@@ -26,9 +26,9 @@
26
26
  "test:ui": "vitest run --root ."
27
27
  },
28
28
  "dependencies": {
29
- "@coffer-org/plugin-orchestrator": "^2.3.0",
30
- "@coffer-org/sdk": "^2.1.2",
31
- "@coffer-org/server": "^2.2.2",
29
+ "@coffer-org/plugin-orchestrator": "^2.3.3",
30
+ "@coffer-org/sdk": "^2.1.3",
31
+ "@coffer-org/server": "^2.5.2",
32
32
  "react-markdown": "^10.1.0",
33
33
  "remark-gfm": "^4.0.1"
34
34
  },