@loupekit/sdk 0.10.0 → 0.10.1-next.42

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.
@@ -2455,6 +2455,7 @@ var Loupe = (() => {
2455
2455
  };
2456
2456
 
2457
2457
  // src/app.ts
2458
+ var SDK_VERSION = true ? "0.10.1" : "dev";
2458
2459
  var DOCK_MODES = ["left", "right", "bottom", "float"];
2459
2460
  var RECORD_MAX_MS = 2e4;
2460
2461
  var MAX_FILES = 10;
@@ -2468,11 +2469,23 @@ var Loupe = (() => {
2468
2469
  }
2469
2470
  function isTouchDevice() {
2470
2471
  try {
2471
- return typeof window.matchMedia === "function" && window.matchMedia("(pointer: coarse)").matches;
2472
+ if (window.matchMedia("(pointer: coarse)").matches) return true;
2473
+ if (window.matchMedia("(hover: none)").matches) return true;
2474
+ } catch {
2475
+ }
2476
+ if (typeof navigator !== "undefined" && (navigator.maxTouchPoints ?? 0) > 0) return true;
2477
+ return typeof window !== "undefined" && "ontouchstart" in window;
2478
+ }
2479
+ function coarsePointer() {
2480
+ try {
2481
+ return window.matchMedia("(pointer: coarse)").matches;
2472
2482
  } catch {
2473
2483
  return false;
2474
2484
  }
2475
2485
  }
2486
+ function canShareScreen() {
2487
+ return typeof navigator.mediaDevices?.getDisplayMedia === "function";
2488
+ }
2476
2489
  function dataUrlToFile(dataUrl, name) {
2477
2490
  const m = /^data:([^;,]+)(;base64)?,([\s\S]*)$/.exec(dataUrl);
2478
2491
  if (!m) return null;
@@ -2852,10 +2865,18 @@ var Loupe = (() => {
2852
2865
  regionBtn.title = "Drag a free-size box, screenshot it, and comment";
2853
2866
  regionBtn.onclick = () => this.setMode(this.mode === "region" ? "off" : "region");
2854
2867
  const recordBtn = this.toolBtn(RECORD_ICON, "Record", "record");
2855
- recordBtn.title = "Drag a box, record a screen video of it, and comment";
2868
+ recordBtn.title = isTouchDevice() ? "Record your screen, then describe the issue" : "Drag a box, record a screen video of it, and comment";
2856
2869
  recordBtn.onclick = () => this.setMode(this.mode === "record" ? "off" : "record");
2857
- const canRecord = !isTouchDevice() && typeof navigator.mediaDevices?.getDisplayMedia === "function";
2858
- tools.append(inspectBtn, freeBtn, regionBtn, ...canRecord ? [recordBtn] : []);
2870
+ if (canShareScreen()) {
2871
+ tools.append(inspectBtn, freeBtn, regionBtn, recordBtn);
2872
+ } else if (isTouchDevice()) {
2873
+ const cameraBtn = this.toolBtn(CAMERA_ICON, "Camera", "camera");
2874
+ cameraBtn.title = "Record a video with the camera and comment";
2875
+ cameraBtn.onclick = () => this.pickCameraVideo();
2876
+ tools.append(inspectBtn, freeBtn, regionBtn, cameraBtn);
2877
+ } else {
2878
+ tools.append(inspectBtn, freeBtn, regionBtn);
2879
+ }
2859
2880
  const listHead = el("div", "listhead");
2860
2881
  listHead.append(document.createTextNode("Comments"));
2861
2882
  this.countEl = el("span", "count", "0");
@@ -3030,6 +3051,37 @@ var Loupe = (() => {
3030
3051
  file ? [file] : []
3031
3052
  );
3032
3053
  }
3054
+ /**
3055
+ * Touch devices with no screen recorder (every iOS browser) can still report a video:
3056
+ * record one with the camera. Opens the native recorder and hands the clip to the
3057
+ * composer exactly like the screen capture does.
3058
+ */
3059
+ pickCameraVideo() {
3060
+ this.setMode("off");
3061
+ const input = document.createElement("input");
3062
+ input.type = "file";
3063
+ input.accept = "video/*";
3064
+ input.setAttribute("capture", "environment");
3065
+ input.style.display = "none";
3066
+ input.onchange = () => {
3067
+ const file = input.files?.[0];
3068
+ input.remove();
3069
+ if (!file) return;
3070
+ const docX = window.scrollX + window.innerWidth / 2;
3071
+ const docY = window.scrollY + window.innerHeight / 2;
3072
+ const docW = Math.max(1, document.documentElement.scrollWidth);
3073
+ const docH = Math.max(1, document.documentElement.scrollHeight);
3074
+ const offset = { x: clamp(docX / docW), y: clamp(docY / docH) };
3075
+ this.openComposer(
3076
+ { kind: "free", offset, point: { x: docX, y: docY }, label: "Camera video \xB7 attached" },
3077
+ 8,
3078
+ window.innerHeight / 2,
3079
+ [file]
3080
+ );
3081
+ };
3082
+ this.shadow.appendChild(input);
3083
+ input.click();
3084
+ }
3033
3085
  /** Capture the selected viewport rect, then open the composer for a region comment. */
3034
3086
  async finishRegion(vp) {
3035
3087
  this.selbox.style.display = "none";
@@ -3124,6 +3176,10 @@ var Loupe = (() => {
3124
3176
  document.addEventListener("click", this.onFreeClick, true);
3125
3177
  } else if (mode === "region" && isTouchDevice()) {
3126
3178
  void this.captureViewportForComposer();
3179
+ } else if (mode === "record" && isTouchDevice()) {
3180
+ const vp = { x: 0, y: 0, w: window.innerWidth, h: window.innerHeight };
3181
+ this.setMode("off");
3182
+ void this.finishRecording(vp);
3127
3183
  } else if (mode === "region" || mode === "record") {
3128
3184
  document.addEventListener("pointerdown", this.onRegionDown, true);
3129
3185
  }
@@ -3279,8 +3335,16 @@ var Loupe = (() => {
3279
3335
  screenshot,
3280
3336
  recording,
3281
3337
  attachments: await this.uploadAttachments(files),
3282
- // Record the screen the feedback was captured on (desktop / tablet / mobile).
3283
- viewport: { w: window.innerWidth, h: window.innerHeight },
3338
+ // The screen this was captured on, plus enough to tell a stale bundle from a real
3339
+ // bug when someone reports "it still doesn't work" (see Comment.viewport).
3340
+ viewport: {
3341
+ w: window.innerWidth,
3342
+ h: window.innerHeight,
3343
+ v: SDK_VERSION,
3344
+ touch: isTouchDevice(),
3345
+ coarse: coarsePointer(),
3346
+ gdm: canShareScreen()
3347
+ },
3284
3348
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
3285
3349
  };
3286
3350
  await this.store.save(comment);
@@ -3665,6 +3729,7 @@ var Loupe = (() => {
3665
3729
  var REGION_ICON = `<svg width="15" height="15" viewBox="0 0 15 15" fill="none" aria-hidden="true"><rect x="1.5" y="2.5" width="12" height="10" rx="1.5" stroke="currentColor" stroke-width="1.4" stroke-dasharray="2.4 1.8"/></svg>`;
3666
3730
  var NOTE_ICON = `<svg width="15" height="15" viewBox="0 0 15 15" fill="none" aria-hidden="true"><path d="M2 2.5h11v7.5H6l-3 2.5v-2.5H2z" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round"/></svg>`;
3667
3731
  var RECORD_ICON = `<svg width="15" height="15" viewBox="0 0 15 15" fill="none" aria-hidden="true"><rect x="1.5" y="2.5" width="12" height="10" rx="1.5" stroke="currentColor" stroke-width="1.4" stroke-dasharray="2.4 1.8"/><circle cx="7.5" cy="7.5" r="2.4" fill="currentColor"/></svg>`;
3732
+ var CAMERA_ICON = `<svg width="15" height="15" viewBox="0 0 15 15" fill="none" aria-hidden="true"><rect x="1.2" y="3.8" width="9.2" height="7.4" rx="1.6" stroke="currentColor" stroke-width="1.3"/><path d="M10.4 7.3l3.4-2v4.4l-3.4-2z" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round"/></svg>`;
3668
3733
  var I_GITHUB = `<svg width="18" height="18" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 .2a8 8 0 0 0-2.5 15.6c.4.07.55-.17.55-.38v-1.3c-2.2.48-2.67-1.07-2.67-1.07-.36-.92-.88-1.16-.88-1.16-.72-.5.05-.48.05-.48.8.056 1.22.82 1.22.82.71 1.22 1.87.87 2.33.66.07-.52.28-.87.5-1.07-1.76-.2-3.6-.88-3.6-3.9 0-.86.3-1.57.82-2.12-.08-.2-.36-1 .08-2.1 0 0 .67-.21 2.2.8a7.6 7.6 0 0 1 4 0c1.53-1.02 2.2-.8 2.2-.8.44 1.1.16 1.9.08 2.1.5.55.82 1.26.82 2.12 0 3.03-1.85 3.7-3.61 3.9.28.24.54.72.54 1.46v2.16c0 .21.14.46.55.38A8 8 0 0 0 8 .2z"/></svg>`;
3669
3734
  var I_SLACK = `<svg width="18" height="18" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M3.4 10.1a1.6 1.6 0 1 1-1.6-1.6h1.6v1.6zm.8 0a1.6 1.6 0 0 1 3.2 0v4a1.6 1.6 0 1 1-3.2 0v-4zM5.8 3.4a1.6 1.6 0 1 1 1.6-1.6v1.6H5.8zm0 .8a1.6 1.6 0 0 1 0 3.2h-4a1.6 1.6 0 1 1 0-3.2h4zm6.7 1.6a1.6 1.6 0 1 1 1.6 1.6h-1.6V5.8zm-.8 0a1.6 1.6 0 0 1-3.2 0v-4a1.6 1.6 0 1 1 3.2 0v4zm-1.6 6.7a1.6 1.6 0 1 1-1.6 1.6v-1.6h1.6zm0-.8a1.6 1.6 0 0 1 0-3.2h4a1.6 1.6 0 1 1 0 3.2h-4z"/></svg>`;
3670
3735
  var I_TELEGRAM = `<svg width="18" height="18" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0zm3.7 5.4-1.24 5.85c-.09.41-.34.51-.69.32l-1.9-1.4-.92.88c-.1.1-.19.19-.38.19l.14-1.93 3.5-3.17c.15-.13-.03-.2-.24-.07l-4.32 2.72-1.86-.58c-.4-.13-.41-.4.09-.6l7.26-2.8c.34-.12.63.08.52.6z"/></svg>`;