@loupekit/sdk 0.9.3 → 0.10.0-next.39
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.global.js +64 -6
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +64 -6
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -308,6 +308,14 @@ var STYLES = (
|
|
|
308
308
|
page stays visible and tappable; the list returns when the tool closes. */
|
|
309
309
|
.dock.inspecting { height: auto !important; }
|
|
310
310
|
.dock.inspecting .listhead, .dock.inspecting .list { display: none; }
|
|
311
|
+
/* The composer is a bottom sheet on a phone: full width, thumb-reachable, and it
|
|
312
|
+
scrolls when the on-screen keyboard is up. JS sets its position inline (hence the
|
|
313
|
+
!important) \u2014 on small screens the sheet wins. */
|
|
314
|
+
.composer {
|
|
315
|
+
top: auto !important; left: 0 !important; right: 0 !important; bottom: 0 !important;
|
|
316
|
+
width: auto !important; max-height: 80vh; overflow-y: auto;
|
|
317
|
+
border-radius: 16px 16px 0 0; padding-bottom: 16px;
|
|
318
|
+
}
|
|
311
319
|
}
|
|
312
320
|
`
|
|
313
321
|
);
|
|
@@ -2431,6 +2439,26 @@ function hash(s) {
|
|
|
2431
2439
|
for (let i = 0; i < s.length; i++) h = h * 31 + s.charCodeAt(i) | 0;
|
|
2432
2440
|
return h;
|
|
2433
2441
|
}
|
|
2442
|
+
function isTouchDevice() {
|
|
2443
|
+
try {
|
|
2444
|
+
return typeof window.matchMedia === "function" && window.matchMedia("(pointer: coarse)").matches;
|
|
2445
|
+
} catch {
|
|
2446
|
+
return false;
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
function dataUrlToFile(dataUrl, name) {
|
|
2450
|
+
const m = /^data:([^;,]+)(;base64)?,([\s\S]*)$/.exec(dataUrl);
|
|
2451
|
+
if (!m) return null;
|
|
2452
|
+
const [, type, b64, payload = ""] = m;
|
|
2453
|
+
try {
|
|
2454
|
+
const raw = b64 ? atob(payload) : decodeURIComponent(payload);
|
|
2455
|
+
const bytes = new Uint8Array(raw.length);
|
|
2456
|
+
for (let i = 0; i < raw.length; i++) bytes[i] = raw.charCodeAt(i);
|
|
2457
|
+
return new File([bytes], name, { type: type || "image/png" });
|
|
2458
|
+
} catch {
|
|
2459
|
+
return null;
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2434
2462
|
var LoupeApp = class {
|
|
2435
2463
|
constructor(cfg) {
|
|
2436
2464
|
this.comments = [];
|
|
@@ -2799,7 +2827,7 @@ var LoupeApp = class {
|
|
|
2799
2827
|
const recordBtn = this.toolBtn(RECORD_ICON, "Record", "record");
|
|
2800
2828
|
recordBtn.title = "Drag a box, record a screen video of it, and comment";
|
|
2801
2829
|
recordBtn.onclick = () => this.setMode(this.mode === "record" ? "off" : "record");
|
|
2802
|
-
const canRecord = typeof navigator.mediaDevices?.getDisplayMedia === "function";
|
|
2830
|
+
const canRecord = !isTouchDevice() && typeof navigator.mediaDevices?.getDisplayMedia === "function";
|
|
2803
2831
|
tools.append(inspectBtn, freeBtn, regionBtn, ...canRecord ? [recordBtn] : []);
|
|
2804
2832
|
const listHead = el("div", "listhead");
|
|
2805
2833
|
listHead.append(document.createTextNode("Comments"));
|
|
@@ -2947,6 +2975,34 @@ var LoupeApp = class {
|
|
|
2947
2975
|
const region = { x: vp.x + window.scrollX, y: vp.y + window.scrollY, w: vp.w, h: vp.h, rel };
|
|
2948
2976
|
return { region, element: centerEl };
|
|
2949
2977
|
}
|
|
2978
|
+
/**
|
|
2979
|
+
* Touch path for the Region tool: capture what is on screen right now and open the
|
|
2980
|
+
* composer with it already attached. The reporter scrolls to the part of the page they
|
|
2981
|
+
* mean FIRST, then taps Region — no drag, so nothing fights the page scroll.
|
|
2982
|
+
*/
|
|
2983
|
+
async captureViewportForComposer() {
|
|
2984
|
+
const vp = { x: 0, y: 0, w: window.innerWidth, h: window.innerHeight };
|
|
2985
|
+
const capture = this.cfg.captureRegion ?? captureRegionScreenshot;
|
|
2986
|
+
let shot;
|
|
2987
|
+
try {
|
|
2988
|
+
shot = await capture(vp);
|
|
2989
|
+
} catch {
|
|
2990
|
+
shot = void 0;
|
|
2991
|
+
}
|
|
2992
|
+
const file = shot ? dataUrlToFile(shot, "screenshot.png") : null;
|
|
2993
|
+
const docX = window.scrollX + vp.w / 2;
|
|
2994
|
+
const docY = window.scrollY + vp.h / 2;
|
|
2995
|
+
const docW = Math.max(1, document.documentElement.scrollWidth);
|
|
2996
|
+
const docH = Math.max(1, document.documentElement.scrollHeight);
|
|
2997
|
+
const offset = { x: clamp(docX / docW), y: clamp(docY / docH) };
|
|
2998
|
+
this.setMode("off");
|
|
2999
|
+
this.openComposer(
|
|
3000
|
+
{ kind: "free", offset, point: { x: docX, y: docY }, label: "Screenshot \xB7 attached" },
|
|
3001
|
+
8,
|
|
3002
|
+
window.innerHeight / 2,
|
|
3003
|
+
file ? [file] : []
|
|
3004
|
+
);
|
|
3005
|
+
}
|
|
2950
3006
|
/** Capture the selected viewport rect, then open the composer for a region comment. */
|
|
2951
3007
|
async finishRegion(vp) {
|
|
2952
3008
|
this.selbox.style.display = "none";
|
|
@@ -3030,7 +3086,6 @@ var LoupeApp = class {
|
|
|
3030
3086
|
document.removeEventListener("click", this.onClick, true);
|
|
3031
3087
|
document.removeEventListener("click", this.onFreeClick, true);
|
|
3032
3088
|
document.removeEventListener("pointerdown", this.onRegionDown, true);
|
|
3033
|
-
document.documentElement.style.touchAction = mode === "region" || mode === "record" ? "none" : "";
|
|
3034
3089
|
if (mode === "off") return;
|
|
3035
3090
|
document.addEventListener("keydown", this.onKey, true);
|
|
3036
3091
|
this.closeComposer();
|
|
@@ -3040,12 +3095,14 @@ var LoupeApp = class {
|
|
|
3040
3095
|
document.addEventListener("click", this.onClick, true);
|
|
3041
3096
|
} else if (mode === "free") {
|
|
3042
3097
|
document.addEventListener("click", this.onFreeClick, true);
|
|
3098
|
+
} else if (mode === "region" && isTouchDevice()) {
|
|
3099
|
+
void this.captureViewportForComposer();
|
|
3043
3100
|
} else if (mode === "region" || mode === "record") {
|
|
3044
3101
|
document.addEventListener("pointerdown", this.onRegionDown, true);
|
|
3045
3102
|
}
|
|
3046
3103
|
}
|
|
3047
3104
|
// ---- composer -------------------------------------------------------------
|
|
3048
|
-
openComposer(target, x, y) {
|
|
3105
|
+
openComposer(target, x, y, seedFiles = []) {
|
|
3049
3106
|
this.pending = target;
|
|
3050
3107
|
const isRecording = target.kind === "region" && !!target.recording;
|
|
3051
3108
|
const c = this.composer;
|
|
@@ -3053,14 +3110,14 @@ var LoupeApp = class {
|
|
|
3053
3110
|
const label = el(
|
|
3054
3111
|
"div",
|
|
3055
3112
|
"target",
|
|
3056
|
-
target.kind === "element" ? describe(target.element) : target.kind === "region" ? isRecording ? `\u23FA Recording \xB7 ${Math.round(target.region.w)}\xD7${Math.round(target.region.h)} px` : `Region \xB7 ${Math.round(target.region.w)}\xD7${Math.round(target.region.h)} px` : "Free note \xB7 anywhere on the page"
|
|
3113
|
+
target.kind === "element" ? describe(target.element) : target.kind === "region" ? isRecording ? `\u23FA Recording \xB7 ${Math.round(target.region.w)}\xD7${Math.round(target.region.h)} px` : `Region \xB7 ${Math.round(target.region.w)}\xD7${Math.round(target.region.h)} px` : target.label ?? "Free note \xB7 anywhere on the page"
|
|
3057
3114
|
);
|
|
3058
3115
|
const title = el("input", "title");
|
|
3059
3116
|
title.type = "text";
|
|
3060
3117
|
title.placeholder = "Title \u2014 one line: what's wrong, or what you need";
|
|
3061
3118
|
const ta = el("textarea");
|
|
3062
3119
|
ta.placeholder = isRecording ? "Describe the issue in this recording\u2026" : target.kind === "region" ? "Describe the issue in this area\u2026" : target.kind === "free" ? "Describe this note\u2026" : "Describe what should change here\u2026";
|
|
3063
|
-
const files =
|
|
3120
|
+
const files = seedFiles.slice();
|
|
3064
3121
|
const attach = el("div", "attach");
|
|
3065
3122
|
const pick = el("button", "pick", "\uFF0B Attach images / videos");
|
|
3066
3123
|
const input = document.createElement("input");
|
|
@@ -3102,6 +3159,7 @@ var LoupeApp = class {
|
|
|
3102
3159
|
};
|
|
3103
3160
|
pick.onclick = () => input.click();
|
|
3104
3161
|
attach.append(pick, input, chips, err);
|
|
3162
|
+
if (files.length) drawChips();
|
|
3105
3163
|
const row = el("div", "row");
|
|
3106
3164
|
let box = null;
|
|
3107
3165
|
if (target.kind !== "free" && !isRecording) {
|
|
@@ -3133,7 +3191,7 @@ var LoupeApp = class {
|
|
|
3133
3191
|
const left = Math.min(Math.max(8, x + 12), window.innerWidth - w - 8);
|
|
3134
3192
|
const top = Math.min(Math.max(8, y + 12), window.innerHeight - h - 8);
|
|
3135
3193
|
Object.assign(c.style, { display: "block", left: left + "px", top: top + "px" });
|
|
3136
|
-
ta.focus();
|
|
3194
|
+
if (!isTouchDevice()) ta.focus();
|
|
3137
3195
|
}
|
|
3138
3196
|
closeComposer() {
|
|
3139
3197
|
this.composer.style.display = "none";
|