@loupekit/sdk 0.10.1 → 0.10.2-next.43

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
@@ -2428,6 +2428,7 @@ var HttpAdapter = class {
2428
2428
  };
2429
2429
 
2430
2430
  // src/app.ts
2431
+ var SDK_VERSION = true ? "0.10.2" : "dev";
2431
2432
  var DOCK_MODES = ["left", "right", "bottom", "float"];
2432
2433
  var RECORD_MAX_MS = 2e4;
2433
2434
  var MAX_FILES = 10;
@@ -2441,11 +2442,23 @@ function hash(s) {
2441
2442
  }
2442
2443
  function isTouchDevice() {
2443
2444
  try {
2444
- return typeof window.matchMedia === "function" && window.matchMedia("(pointer: coarse)").matches;
2445
+ if (window.matchMedia("(pointer: coarse)").matches) return true;
2446
+ if (window.matchMedia("(hover: none)").matches) return true;
2447
+ } catch {
2448
+ }
2449
+ if (typeof navigator !== "undefined" && (navigator.maxTouchPoints ?? 0) > 0) return true;
2450
+ return typeof window !== "undefined" && "ontouchstart" in window;
2451
+ }
2452
+ function coarsePointer() {
2453
+ try {
2454
+ return window.matchMedia("(pointer: coarse)").matches;
2445
2455
  } catch {
2446
2456
  return false;
2447
2457
  }
2448
2458
  }
2459
+ function canShareScreen() {
2460
+ return typeof navigator.mediaDevices?.getDisplayMedia === "function";
2461
+ }
2449
2462
  function dataUrlToFile(dataUrl, name) {
2450
2463
  const m = /^data:([^;,]+)(;base64)?,([\s\S]*)$/.exec(dataUrl);
2451
2464
  if (!m) return null;
@@ -2827,8 +2840,16 @@ var LoupeApp = class {
2827
2840
  const recordBtn = this.toolBtn(RECORD_ICON, "Record", "record");
2828
2841
  recordBtn.title = isTouchDevice() ? "Record your screen, then describe the issue" : "Drag a box, record a screen video of it, and comment";
2829
2842
  recordBtn.onclick = () => this.setMode(this.mode === "record" ? "off" : "record");
2830
- const canRecord = typeof navigator.mediaDevices?.getDisplayMedia === "function";
2831
- tools.append(inspectBtn, freeBtn, regionBtn, ...canRecord ? [recordBtn] : []);
2843
+ if (canShareScreen()) {
2844
+ tools.append(inspectBtn, freeBtn, regionBtn, recordBtn);
2845
+ } else if (isTouchDevice()) {
2846
+ const cameraBtn = this.toolBtn(CAMERA_ICON, "Camera", "camera");
2847
+ cameraBtn.title = "Record a video with the camera and comment";
2848
+ cameraBtn.onclick = () => this.pickCameraVideo();
2849
+ tools.append(inspectBtn, freeBtn, regionBtn, cameraBtn);
2850
+ } else {
2851
+ tools.append(inspectBtn, freeBtn, regionBtn);
2852
+ }
2832
2853
  const listHead = el("div", "listhead");
2833
2854
  listHead.append(document.createTextNode("Comments"));
2834
2855
  this.countEl = el("span", "count", "0");
@@ -3003,6 +3024,37 @@ var LoupeApp = class {
3003
3024
  file ? [file] : []
3004
3025
  );
3005
3026
  }
3027
+ /**
3028
+ * Touch devices with no screen recorder (every iOS browser) can still report a video:
3029
+ * record one with the camera. Opens the native recorder and hands the clip to the
3030
+ * composer exactly like the screen capture does.
3031
+ */
3032
+ pickCameraVideo() {
3033
+ this.setMode("off");
3034
+ const input = document.createElement("input");
3035
+ input.type = "file";
3036
+ input.accept = "video/*";
3037
+ input.setAttribute("capture", "environment");
3038
+ input.style.display = "none";
3039
+ input.onchange = () => {
3040
+ const file = input.files?.[0];
3041
+ input.remove();
3042
+ if (!file) return;
3043
+ const docX = window.scrollX + window.innerWidth / 2;
3044
+ const docY = window.scrollY + window.innerHeight / 2;
3045
+ const docW = Math.max(1, document.documentElement.scrollWidth);
3046
+ const docH = Math.max(1, document.documentElement.scrollHeight);
3047
+ const offset = { x: clamp(docX / docW), y: clamp(docY / docH) };
3048
+ this.openComposer(
3049
+ { kind: "free", offset, point: { x: docX, y: docY }, label: "Camera video \xB7 attached" },
3050
+ 8,
3051
+ window.innerHeight / 2,
3052
+ [file]
3053
+ );
3054
+ };
3055
+ this.shadow.appendChild(input);
3056
+ input.click();
3057
+ }
3006
3058
  /** Capture the selected viewport rect, then open the composer for a region comment. */
3007
3059
  async finishRegion(vp) {
3008
3060
  this.selbox.style.display = "none";
@@ -3256,8 +3308,16 @@ var LoupeApp = class {
3256
3308
  screenshot,
3257
3309
  recording,
3258
3310
  attachments: await this.uploadAttachments(files),
3259
- // Record the screen the feedback was captured on (desktop / tablet / mobile).
3260
- viewport: { w: window.innerWidth, h: window.innerHeight },
3311
+ // The screen this was captured on, plus enough to tell a stale bundle from a real
3312
+ // bug when someone reports "it still doesn't work" (see Comment.viewport).
3313
+ viewport: {
3314
+ w: window.innerWidth,
3315
+ h: window.innerHeight,
3316
+ v: SDK_VERSION,
3317
+ touch: isTouchDevice(),
3318
+ coarse: coarsePointer(),
3319
+ gdm: canShareScreen()
3320
+ },
3261
3321
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
3262
3322
  };
3263
3323
  await this.store.save(comment);
@@ -3642,6 +3702,7 @@ var I_CLOSE = svg(
3642
3702
  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>`;
3643
3703
  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>`;
3644
3704
  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>`;
3705
+ 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>`;
3645
3706
  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>`;
3646
3707
  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>`;
3647
3708
  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>`;