@loupekit/sdk 0.10.1 → 0.10.2-next.44

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.2" : "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;
@@ -2854,8 +2867,16 @@ var Loupe = (() => {
2854
2867
  const recordBtn = this.toolBtn(RECORD_ICON, "Record", "record");
2855
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 = 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 videoBtn = this.toolBtn(VIDEO_ICON, "Video", "video");
2874
+ videoBtn.title = "Attach a video \u2014 record your screen with your phone, then pick it here";
2875
+ videoBtn.onclick = () => this.pickVideo();
2876
+ tools.append(inspectBtn, freeBtn, regionBtn, videoBtn);
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
+ * Attach a video on a phone, where the page cannot record the screen: the reporter
3056
+ * records it with the phone's own screen recorder first, then picks the clip here. No
3057
+ * `capture` attribute on purpose — that would force the camera open, and a video of the
3058
+ * room is not a screen recording.
3059
+ */
3060
+ pickVideo() {
3061
+ this.setMode("off");
3062
+ const input = document.createElement("input");
3063
+ input.type = "file";
3064
+ input.accept = "video/*";
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: "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";
@@ -3283,8 +3335,16 @@ var Loupe = (() => {
3283
3335
  screenshot,
3284
3336
  recording,
3285
3337
  attachments: await this.uploadAttachments(files),
3286
- // Record the screen the feedback was captured on (desktop / tablet / mobile).
3287
- 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
+ },
3288
3348
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
3289
3349
  };
3290
3350
  await this.store.save(comment);
@@ -3669,6 +3729,7 @@ var Loupe = (() => {
3669
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>`;
3670
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>`;
3671
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 VIDEO_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"/><path d="M6.3 5.5l3.9 2.2-3.9 2.2z" fill="currentColor"/></svg>`;
3672
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>`;
3673
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>`;
3674
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>`;