@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.cjs CHANGED
@@ -1009,7 +1009,7 @@ var import_feedback_kit_core3 = require("@solhun/feedback-kit-core");
1009
1009
  var import_react = require("react");
1010
1010
 
1011
1011
  // src/version.ts
1012
- var VERSION = true ? "0.6.0" : "dev";
1012
+ var VERSION = true ? "0.6.1" : "dev";
1013
1013
 
1014
1014
  // src/feedback-kit.tsx
1015
1015
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -1072,26 +1072,41 @@ async function transcodeToJpeg(dataUrl) {
1072
1072
  return null;
1073
1073
  }
1074
1074
  }
1075
+ function looksLikeImageName(name) {
1076
+ return /\.(png|jpe?g|gif|webp|bmp|avif|heic|heif)$/i.test(name);
1077
+ }
1078
+ function isImageFile(file) {
1079
+ if (file.type.startsWith("image/")) return true;
1080
+ return (file.type === "" || file.type === "application/octet-stream") && looksLikeImageName(file.name);
1081
+ }
1082
+ function contentTypeOf(file) {
1083
+ if (file.type !== "") return file.type;
1084
+ const name = file instanceof File ? file.name : "";
1085
+ if (/\.png$/i.test(name)) return "image/png";
1086
+ if (/\.jpe?g$/i.test(name)) return "image/jpeg";
1087
+ return "";
1088
+ }
1075
1089
  async function readImageFile(file) {
1076
- if (!file.type.startsWith("image/")) return null;
1090
+ const named = file instanceof File ? file : null;
1091
+ if (!file.type.startsWith("image/") && !(named !== null && isImageFile(named))) return null;
1077
1092
  const dataUrl = await readDataUrl(file);
1078
1093
  if (!dataUrl) return null;
1079
- if (file.type === "image/png" || file.type === "image/jpeg") {
1094
+ const type = contentTypeOf(file);
1095
+ if (type === "image/png" || type === "image/jpeg") {
1080
1096
  const base64 = base64Of(dataUrl);
1081
- return base64 ? { base64, contentType: file.type } : null;
1097
+ return base64 ? { base64, contentType: type } : null;
1082
1098
  }
1083
1099
  return transcodeToJpeg(dataUrl);
1084
1100
  }
1085
1101
  function firstImageOf(transfer) {
1086
1102
  if (!transfer) return null;
1087
1103
  for (const item of Array.from(transfer.items ?? [])) {
1088
- if (item.kind === "file" && item.type.startsWith("image/")) {
1089
- const file = item.getAsFile();
1090
- if (file) return file;
1091
- }
1104
+ if (item.kind !== "file") continue;
1105
+ const file = item.getAsFile();
1106
+ if (file && isImageFile(file)) return file;
1092
1107
  }
1093
1108
  for (const file of Array.from(transfer.files ?? [])) {
1094
- if (file.type.startsWith("image/")) return file;
1109
+ if (isImageFile(file)) return file;
1095
1110
  }
1096
1111
  return null;
1097
1112
  }
@@ -1433,6 +1448,23 @@ function FeedbackKit(props) {
1433
1448
  document.addEventListener("keydown", onKeyDownCapture, true);
1434
1449
  return () => document.removeEventListener("keydown", onKeyDownCapture, true);
1435
1450
  }, [kit, widgetState, pickingState.popup]);
1451
+ (0, import_react.useEffect)(() => {
1452
+ if (!kit || !widgetState?.modal.open) return;
1453
+ if (pickingState.popup) return;
1454
+ const locked = widgetState.modal.submitStatus === "sending" || widgetState.modal.submitStatus === "pending";
1455
+ if (locked) return;
1456
+ const controller = kit;
1457
+ async function onPaste(event) {
1458
+ const file = firstImageOf(event.clipboardData);
1459
+ if (!file) return;
1460
+ event.preventDefault();
1461
+ const shot = await readImageFile(file);
1462
+ if (shot) await controller.widget.modal.attachFile(shot);
1463
+ }
1464
+ const handler = (event) => void onPaste(event);
1465
+ document.addEventListener("paste", handler);
1466
+ return () => document.removeEventListener("paste", handler);
1467
+ }, [kit, widgetState, pickingState.popup]);
1436
1468
  if (!kit || !widgetState) {
1437
1469
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ...OWN_UI_PROPS, "data-feedback-kit-loading": "true" });
1438
1470
  }
@@ -1470,6 +1502,14 @@ function FeedbackKit(props) {
1470
1502
  }
1471
1503
  input.value = "";
1472
1504
  }
1505
+ async function attachToModal(transfer) {
1506
+ const file = firstImageOf(transfer);
1507
+ if (!file) return false;
1508
+ const screenshot = await readImageFile(file);
1509
+ if (!screenshot) return false;
1510
+ await activeKit.widget.modal.attachFile(screenshot);
1511
+ return true;
1512
+ }
1473
1513
  async function attachToPopup(transfer) {
1474
1514
  const file = firstImageOf(transfer);
1475
1515
  if (!file) return false;
@@ -1570,6 +1610,14 @@ function FeedbackKit(props) {
1570
1610
  "aria-modal": "true",
1571
1611
  "aria-labelledby": "feedback-kit-dialog-title",
1572
1612
  onKeyDown: handleModalKeyDown,
1613
+ onDragOver: (event) => {
1614
+ if (!draftLocked && firstImageOf(event.dataTransfer)) event.preventDefault();
1615
+ },
1616
+ onDrop: (event) => {
1617
+ if (draftLocked || !firstImageOf(event.dataTransfer)) return;
1618
+ event.preventDefault();
1619
+ void attachToModal(event.dataTransfer);
1620
+ },
1573
1621
  style: {
1574
1622
  width: "min(520px, 100%)",
1575
1623
  maxHeight: "min(760px, calc(100vh - 40px))",