@solhun/feedback-kit-web 0.6.0 → 0.6.1

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.
package/dist/index.js CHANGED
@@ -973,7 +973,7 @@ import {
973
973
  } from "react";
974
974
 
975
975
  // src/version.ts
976
- var VERSION = true ? "0.6.0" : "dev";
976
+ var VERSION = true ? "0.6.1" : "dev";
977
977
 
978
978
  // src/feedback-kit.tsx
979
979
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -1036,26 +1036,41 @@ async function transcodeToJpeg(dataUrl) {
1036
1036
  return null;
1037
1037
  }
1038
1038
  }
1039
+ function looksLikeImageName(name) {
1040
+ return /\.(png|jpe?g|gif|webp|bmp|avif|heic|heif)$/i.test(name);
1041
+ }
1042
+ function isImageFile(file) {
1043
+ if (file.type.startsWith("image/")) return true;
1044
+ return (file.type === "" || file.type === "application/octet-stream") && looksLikeImageName(file.name);
1045
+ }
1046
+ function contentTypeOf(file) {
1047
+ if (file.type !== "") return file.type;
1048
+ const name = file instanceof File ? file.name : "";
1049
+ if (/\.png$/i.test(name)) return "image/png";
1050
+ if (/\.jpe?g$/i.test(name)) return "image/jpeg";
1051
+ return "";
1052
+ }
1039
1053
  async function readImageFile(file) {
1040
- if (!file.type.startsWith("image/")) return null;
1054
+ const named = file instanceof File ? file : null;
1055
+ if (!file.type.startsWith("image/") && !(named !== null && isImageFile(named))) return null;
1041
1056
  const dataUrl = await readDataUrl(file);
1042
1057
  if (!dataUrl) return null;
1043
- if (file.type === "image/png" || file.type === "image/jpeg") {
1058
+ const type = contentTypeOf(file);
1059
+ if (type === "image/png" || type === "image/jpeg") {
1044
1060
  const base64 = base64Of(dataUrl);
1045
- return base64 ? { base64, contentType: file.type } : null;
1061
+ return base64 ? { base64, contentType: type } : null;
1046
1062
  }
1047
1063
  return transcodeToJpeg(dataUrl);
1048
1064
  }
1049
1065
  function firstImageOf(transfer) {
1050
1066
  if (!transfer) return null;
1051
1067
  for (const item of Array.from(transfer.items ?? [])) {
1052
- if (item.kind === "file" && item.type.startsWith("image/")) {
1053
- const file = item.getAsFile();
1054
- if (file) return file;
1055
- }
1068
+ if (item.kind !== "file") continue;
1069
+ const file = item.getAsFile();
1070
+ if (file && isImageFile(file)) return file;
1056
1071
  }
1057
1072
  for (const file of Array.from(transfer.files ?? [])) {
1058
- if (file.type.startsWith("image/")) return file;
1073
+ if (isImageFile(file)) return file;
1059
1074
  }
1060
1075
  return null;
1061
1076
  }
@@ -1397,6 +1412,23 @@ function FeedbackKit(props) {
1397
1412
  document.addEventListener("keydown", onKeyDownCapture, true);
1398
1413
  return () => document.removeEventListener("keydown", onKeyDownCapture, true);
1399
1414
  }, [kit, widgetState, pickingState.popup]);
1415
+ useEffect(() => {
1416
+ if (!kit || !widgetState?.modal.open) return;
1417
+ if (pickingState.popup) return;
1418
+ const locked = widgetState.modal.submitStatus === "sending" || widgetState.modal.submitStatus === "pending";
1419
+ if (locked) return;
1420
+ const controller = kit;
1421
+ async function onPaste(event) {
1422
+ const file = firstImageOf(event.clipboardData);
1423
+ if (!file) return;
1424
+ event.preventDefault();
1425
+ const shot = await readImageFile(file);
1426
+ if (shot) await controller.widget.modal.attachFile(shot);
1427
+ }
1428
+ const handler = (event) => void onPaste(event);
1429
+ document.addEventListener("paste", handler);
1430
+ return () => document.removeEventListener("paste", handler);
1431
+ }, [kit, widgetState, pickingState.popup]);
1400
1432
  if (!kit || !widgetState) {
1401
1433
  return /* @__PURE__ */ jsx("div", { ...OWN_UI_PROPS, "data-feedback-kit-loading": "true" });
1402
1434
  }
@@ -1434,6 +1466,14 @@ function FeedbackKit(props) {
1434
1466
  }
1435
1467
  input.value = "";
1436
1468
  }
1469
+ async function attachToModal(transfer) {
1470
+ const file = firstImageOf(transfer);
1471
+ if (!file) return false;
1472
+ const screenshot = await readImageFile(file);
1473
+ if (!screenshot) return false;
1474
+ await activeKit.widget.modal.attachFile(screenshot);
1475
+ return true;
1476
+ }
1437
1477
  async function attachToPopup(transfer) {
1438
1478
  const file = firstImageOf(transfer);
1439
1479
  if (!file) return false;
@@ -1534,6 +1574,14 @@ function FeedbackKit(props) {
1534
1574
  "aria-modal": "true",
1535
1575
  "aria-labelledby": "feedback-kit-dialog-title",
1536
1576
  onKeyDown: handleModalKeyDown,
1577
+ onDragOver: (event) => {
1578
+ if (!draftLocked && firstImageOf(event.dataTransfer)) event.preventDefault();
1579
+ },
1580
+ onDrop: (event) => {
1581
+ if (draftLocked || !firstImageOf(event.dataTransfer)) return;
1582
+ event.preventDefault();
1583
+ void attachToModal(event.dataTransfer);
1584
+ },
1537
1585
  style: {
1538
1586
  width: "min(520px, 100%)",
1539
1587
  maxHeight: "min(760px, calc(100vh - 40px))",