qapture2 0.7.5 → 0.7.7
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/README.md +16 -7
- package/dist/{chunk-FBFESAG6.js → chunk-FVQKCQC3.js} +382 -109
- package/dist/{chunk-COQKFGKZ.cjs → chunk-WU7QMHPK.cjs} +382 -109
- package/dist/index.cjs +5 -5
- package/dist/index.js +1 -1
- package/dist/next.cjs +4 -4
- package/dist/next.js +1 -1
- package/dist/standalone.cjs +2 -2
- package/dist/standalone.js +1 -1
- package/package.json +3 -2
|
@@ -58,10 +58,10 @@ function openDB(dbName) {
|
|
|
58
58
|
dbCache.set(dbName, promise);
|
|
59
59
|
return promise;
|
|
60
60
|
}
|
|
61
|
-
function run(dbName, store,
|
|
61
|
+
function run(dbName, store, mode2, fn) {
|
|
62
62
|
return openDB(dbName).then(
|
|
63
63
|
(db) => new Promise((resolve, reject) => {
|
|
64
|
-
const tx = db.transaction(store,
|
|
64
|
+
const tx = db.transaction(store, mode2);
|
|
65
65
|
const s = tx.objectStore(store);
|
|
66
66
|
let result;
|
|
67
67
|
const req = fn(s);
|
|
@@ -1762,10 +1762,10 @@ var STR = {
|
|
|
1762
1762
|
copied: "Copied",
|
|
1763
1763
|
copy_failed: "Copy failed",
|
|
1764
1764
|
copy_prompt: "Copy as agent prompt",
|
|
1765
|
-
severity_label: "
|
|
1765
|
+
severity_label: "What is this?",
|
|
1766
1766
|
sev_bug: "Bug",
|
|
1767
|
-
|
|
1768
|
-
|
|
1767
|
+
sev_design: "Design",
|
|
1768
|
+
sev_enhance: "Enhance",
|
|
1769
1769
|
status_open: "Open",
|
|
1770
1770
|
status_verified: "Verified",
|
|
1771
1771
|
context_attached: "{n} runtime events attached",
|
|
@@ -1781,10 +1781,10 @@ var STR = {
|
|
|
1781
1781
|
no_evidence: "ticked, no capture",
|
|
1782
1782
|
expected_label: "Expected",
|
|
1783
1783
|
exact_label: "Pixel-exact shots",
|
|
1784
|
-
exact_hint: "Screenshots become a real photo
|
|
1784
|
+
exact_hint: "Screenshots become a real photo instead of a redraw. Asks once to share this tab (or, in Safari and Firefox, this window); nothing leaves your device.",
|
|
1785
1785
|
redraw_notice: "This is a redraw of the page, not a real photo.",
|
|
1786
1786
|
redraw_action: "Use real screenshots",
|
|
1787
|
-
redraw_no_engine: "This browser cannot photograph
|
|
1787
|
+
redraw_no_engine: "This browser cannot photograph the screen at all, so charts and canvases may come out wrong. Open the app on a desktop browser for true screenshots.",
|
|
1788
1788
|
fab_title: "Qapture \u2014 click to open, drag to move it out of the way",
|
|
1789
1789
|
exact_on: "Pixel-exact screenshots on",
|
|
1790
1790
|
exact_off: "Back to redrawn screenshots",
|
|
@@ -1960,10 +1960,10 @@ var STR = {
|
|
|
1960
1960
|
copied: "\u062A\u0645 \u0627\u0644\u0646\u0633\u062E",
|
|
1961
1961
|
copy_failed: "\u0641\u0634\u0644 \u0627\u0644\u0646\u0633\u062E",
|
|
1962
1962
|
copy_prompt: "\u0646\u0633\u062E \u0643\u0645\u0648\u062C\u0651\u0647 \u0644\u0644\u0648\u0643\u064A\u0644",
|
|
1963
|
-
severity_label: "\u0627\
|
|
1964
|
-
sev_bug: "
|
|
1965
|
-
|
|
1966
|
-
|
|
1963
|
+
severity_label: "\u0645\u0627 \u0647\u0630\u0627\u061F",
|
|
1964
|
+
sev_bug: "Bug",
|
|
1965
|
+
sev_design: "Design",
|
|
1966
|
+
sev_enhance: "Enhance",
|
|
1967
1967
|
status_open: "\u0645\u0641\u062A\u0648\u062D",
|
|
1968
1968
|
status_verified: "\u062A\u0645 \u0627\u0644\u062A\u062D\u0642\u0642",
|
|
1969
1969
|
context_attached: "{n} \u0645\u0646 \u0623\u062D\u062F\u0627\u062B \u0627\u0644\u062A\u0634\u063A\u064A\u0644 \u0645\u0631\u0641\u0642\u0629",
|
|
@@ -1979,10 +1979,10 @@ var STR = {
|
|
|
1979
1979
|
no_evidence: "\u0645\u064F\u0639\u0644\u0651\u0645 \u0628\u062F\u0648\u0646 \u0627\u0644\u062A\u0642\u0627\u0637",
|
|
1980
1980
|
expected_label: "\u0627\u0644\u0645\u062A\u0648\u0642\u0639",
|
|
1981
1981
|
exact_label: "\u0644\u0642\u0637\u0627\u062A \u0645\u0637\u0627\u0628\u0642\u0629 \u062A\u0645\u0627\u0645\u064B\u0627",
|
|
1982
|
-
exact_hint: "\u062A\u0635\u0628\u062D \u0627\u0644\u0644\u0642\u0637\u0629 \u0635\u0648\u0631\u0629 \u062D\u0642\u064A\u0642\u064A\u0629 \
|
|
1982
|
+
exact_hint: "\u062A\u0635\u0628\u062D \u0627\u0644\u0644\u0642\u0637\u0629 \u0635\u0648\u0631\u0629 \u062D\u0642\u064A\u0642\u064A\u0629 \u0628\u062F\u0644 \u0625\u0639\u0627\u062F\u0629 \u0627\u0644\u0631\u0633\u0645. \u064A\u064F\u0637\u0644\u0628 \u0627\u0644\u0625\u0630\u0646 \u0645\u0631\u0629 \u0648\u0627\u062D\u062F\u0629 \u0644\u0645\u0634\u0627\u0631\u0643\u0629 \u0627\u0644\u062A\u0628\u0648\u064A\u0628 (\u0623\u0648 \u0627\u0644\u0646\u0627\u0641\u0630\u0629 \u0641\u064A Safari \u0648Firefox)\u060C \u0648\u0644\u0627 \u0634\u064A\u0621 \u064A\u063A\u0627\u062F\u0631 \u062C\u0647\u0627\u0632\u0643.",
|
|
1983
1983
|
redraw_notice: "\u0647\u0630\u0647 \u0625\u0639\u0627\u062F\u0629 \u0631\u0633\u0645 \u0644\u0644\u0635\u0641\u062D\u0629\u060C \u0648\u0644\u064A\u0633\u062A \u0635\u0648\u0631\u0629 \u062D\u0642\u064A\u0642\u064A\u0629.",
|
|
1984
1984
|
redraw_action: "\u0627\u0633\u062A\u062E\u062F\u0645 \u0644\u0642\u0637\u0627\u062A \u062D\u0642\u064A\u0642\u064A\u0629",
|
|
1985
|
-
redraw_no_engine: "\u0644\u0627 \u064A\u0633\u062A\u0637\u064A\u0639 \u0647\u0630\u0627 \u0627\u0644\u0645\u062A\u0635\u0641\u062D \u062A\u0635\u0648\u064A\u0631 \u0627\u0644\
|
|
1985
|
+
redraw_no_engine: "\u0644\u0627 \u064A\u0633\u062A\u0637\u064A\u0639 \u0647\u0630\u0627 \u0627\u0644\u0645\u062A\u0635\u0641\u062D \u062A\u0635\u0648\u064A\u0631 \u0627\u0644\u0634\u0627\u0634\u0629 \u0625\u0637\u0644\u0627\u0642\u064B\u0627\u060C \u0644\u0630\u0627 \u0642\u062F \u062A\u0638\u0647\u0631 \u0627\u0644\u0645\u062E\u0637\u0637\u0627\u062A \u0648\u0627\u0644\u0644\u0648\u062D\u0627\u062A \u0628\u0634\u0643\u0644 \u062E\u0627\u0637\u0626. \u0627\u0641\u062A\u062D \u0627\u0644\u062A\u0637\u0628\u064A\u0642 \u0639\u0644\u0649 \u0645\u062A\u0635\u0641\u062D \u0633\u0637\u062D \u0645\u0643\u062A\u0628 \u0644\u0644\u062D\u0635\u0648\u0644 \u0639\u0644\u0649 \u0644\u0642\u0637\u0627\u062A \u062D\u0642\u064A\u0642\u064A\u0629.",
|
|
1986
1986
|
fab_title: "Qapture \u2014 \u0627\u0636\u063A\u0637 \u0644\u0644\u0641\u062A\u062D\u060C \u0648\u0627\u0633\u062D\u0628 \u0644\u062A\u062D\u0631\u064A\u0643\u0647 \u0645\u0646 \u0645\u0643\u0627\u0646\u0647",
|
|
1987
1987
|
exact_on: "\u062A\u0645 \u062A\u0641\u0639\u064A\u0644 \u0627\u0644\u0644\u0642\u0637\u0627\u062A \u0627\u0644\u0645\u0637\u0627\u0628\u0642\u0629",
|
|
1988
1988
|
exact_off: "\u0627\u0644\u0639\u0648\u062F\u0629 \u0625\u0644\u0649 \u0627\u0644\u0644\u0642\u0637\u0627\u062A \u0627\u0644\u0645\u064F\u0639\u0627\u062F \u0631\u0633\u0645\u0647\u0627",
|
|
@@ -2182,8 +2182,168 @@ function computeCoverage(journey, guideChecked, skipped) {
|
|
|
2182
2182
|
};
|
|
2183
2183
|
}
|
|
2184
2184
|
|
|
2185
|
+
// src/lib/frameCalibration.ts
|
|
2186
|
+
var MARKER_SIZE = 28;
|
|
2187
|
+
var MARKERS = [
|
|
2188
|
+
{ key: "tl", color: [255, 0, 0] },
|
|
2189
|
+
{ key: "tr", color: [0, 255, 0] },
|
|
2190
|
+
{ key: "bl", color: [0, 0, 255] },
|
|
2191
|
+
{ key: "br", color: [255, 0, 255] }
|
|
2192
|
+
];
|
|
2193
|
+
function markerCentres(vw, vh) {
|
|
2194
|
+
const h = MARKER_SIZE / 2;
|
|
2195
|
+
return {
|
|
2196
|
+
tl: { x: h, y: h },
|
|
2197
|
+
tr: { x: vw - h, y: h },
|
|
2198
|
+
bl: { x: h, y: vh - h },
|
|
2199
|
+
br: { x: vw - h, y: vh - h }
|
|
2200
|
+
};
|
|
2201
|
+
}
|
|
2202
|
+
function dist2(r, g, b, c) {
|
|
2203
|
+
const dr = r - c[0];
|
|
2204
|
+
const dg = g - c[1];
|
|
2205
|
+
const db = b - c[2];
|
|
2206
|
+
return dr * dr + dg * dg + db * db;
|
|
2207
|
+
}
|
|
2208
|
+
var COLOR_TOLERANCE_2 = 90 * 90;
|
|
2209
|
+
var CLUSTER_CELL = 8;
|
|
2210
|
+
var MAX_CANDIDATES = 4;
|
|
2211
|
+
function findMarkerCandidates(data, width, height) {
|
|
2212
|
+
const cols = Math.ceil(width / CLUSTER_CELL);
|
|
2213
|
+
const rows = Math.ceil(height / CLUSTER_CELL);
|
|
2214
|
+
const cells = {};
|
|
2215
|
+
for (const m of MARKERS) cells[m.key] = new Int32Array(cols * rows);
|
|
2216
|
+
for (let y = 0; y < height; y++) {
|
|
2217
|
+
const row = y * width * 4;
|
|
2218
|
+
const cy = y / CLUSTER_CELL | 0;
|
|
2219
|
+
for (let x = 0; x < width; x++) {
|
|
2220
|
+
const i = row + x * 4;
|
|
2221
|
+
if (data[i + 3] < 128) continue;
|
|
2222
|
+
const r = data[i];
|
|
2223
|
+
const g = data[i + 1];
|
|
2224
|
+
const b = data[i + 2];
|
|
2225
|
+
if (Math.max(r, g, b) - Math.min(r, g, b) < 60) continue;
|
|
2226
|
+
for (const m of MARKERS) {
|
|
2227
|
+
if (dist2(r, g, b, m.color) <= COLOR_TOLERANCE_2) {
|
|
2228
|
+
cells[m.key][cy * cols + (x / CLUSTER_CELL | 0)]++;
|
|
2229
|
+
break;
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
const out = {};
|
|
2235
|
+
for (const m of MARKERS) {
|
|
2236
|
+
const grid = cells[m.key];
|
|
2237
|
+
const seen = new Uint8Array(cols * rows);
|
|
2238
|
+
const blobs = [];
|
|
2239
|
+
for (let start = 0; start < grid.length; start++) {
|
|
2240
|
+
if (!grid[start] || seen[start]) continue;
|
|
2241
|
+
const queue = [start];
|
|
2242
|
+
seen[start] = 1;
|
|
2243
|
+
let sumX = 0;
|
|
2244
|
+
let sumY = 0;
|
|
2245
|
+
let total = 0;
|
|
2246
|
+
while (queue.length) {
|
|
2247
|
+
const idx = queue.pop();
|
|
2248
|
+
const n = grid[idx];
|
|
2249
|
+
const cx = idx % cols;
|
|
2250
|
+
const cy = idx / cols | 0;
|
|
2251
|
+
sumX += (cx * CLUSTER_CELL + CLUSTER_CELL / 2) * n;
|
|
2252
|
+
sumY += (cy * CLUSTER_CELL + CLUSTER_CELL / 2) * n;
|
|
2253
|
+
total += n;
|
|
2254
|
+
for (let dy = -1; dy <= 1; dy++) {
|
|
2255
|
+
for (let dx = -1; dx <= 1; dx++) {
|
|
2256
|
+
const nx = cx + dx;
|
|
2257
|
+
const ny = cy + dy;
|
|
2258
|
+
if (nx < 0 || ny < 0 || nx >= cols || ny >= rows) continue;
|
|
2259
|
+
const nIdx = ny * cols + nx;
|
|
2260
|
+
if (seen[nIdx] || !grid[nIdx]) continue;
|
|
2261
|
+
seen[nIdx] = 1;
|
|
2262
|
+
queue.push(nIdx);
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
if (total < 16) continue;
|
|
2267
|
+
blobs.push({ x: sumX / total, y: sumY / total, count: total });
|
|
2268
|
+
}
|
|
2269
|
+
blobs.sort((a, b) => b.count - a.count);
|
|
2270
|
+
out[m.key] = blobs.slice(0, MAX_CANDIDATES);
|
|
2271
|
+
}
|
|
2272
|
+
return out;
|
|
2273
|
+
}
|
|
2274
|
+
var VERIFY_TOLERANCE = 0.02;
|
|
2275
|
+
var SQUARENESS_TOLERANCE = 0.05;
|
|
2276
|
+
function deriveMapping(candidates, vw, vh, frameW, frameH) {
|
|
2277
|
+
const tls = candidates.tl ?? [];
|
|
2278
|
+
const brs = candidates.br ?? [];
|
|
2279
|
+
for (const tl of tls) {
|
|
2280
|
+
for (const br of brs) {
|
|
2281
|
+
const m = solveFrom(tl, br, candidates, vw, vh, frameW, frameH);
|
|
2282
|
+
if (m) return m;
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
return null;
|
|
2286
|
+
}
|
|
2287
|
+
function solveFrom(tl, br, candidates, vw, vh, frameW, frameH) {
|
|
2288
|
+
if (!(vw > MARKER_SIZE) || !(vh > MARKER_SIZE)) return null;
|
|
2289
|
+
const centres = markerCentres(vw, vh);
|
|
2290
|
+
const spanCssX = centres.br.x - centres.tl.x;
|
|
2291
|
+
const spanCssY = centres.br.y - centres.tl.y;
|
|
2292
|
+
if (spanCssX <= 0 || spanCssY <= 0) return null;
|
|
2293
|
+
const scaleX = (br.x - tl.x) / spanCssX;
|
|
2294
|
+
const scaleY = (br.y - tl.y) / spanCssY;
|
|
2295
|
+
if (!(scaleX > 0.05) || !(scaleY > 0.05)) return null;
|
|
2296
|
+
if (!Number.isFinite(scaleX) || !Number.isFinite(scaleY)) return null;
|
|
2297
|
+
if (Math.abs(scaleX - scaleY) / Math.max(scaleX, scaleY) > SQUARENESS_TOLERANCE) return null;
|
|
2298
|
+
const originX = tl.x - centres.tl.x * scaleX;
|
|
2299
|
+
const originY = tl.y - centres.tl.y * scaleY;
|
|
2300
|
+
const mapping2 = { scaleX, scaleY, originX, originY };
|
|
2301
|
+
const farX = originX + vw * scaleX;
|
|
2302
|
+
const farY = originY + vh * scaleY;
|
|
2303
|
+
const slack = Math.max(4, frameW * 0.01);
|
|
2304
|
+
if (originX < -slack || originY < -slack || farX > frameW + slack || farY > frameH + slack) {
|
|
2305
|
+
return null;
|
|
2306
|
+
}
|
|
2307
|
+
const tolerance = Math.max(6, Math.max(frameW, frameH) * VERIFY_TOLERANCE);
|
|
2308
|
+
let verified = 0;
|
|
2309
|
+
for (const key of ["tr", "bl"]) {
|
|
2310
|
+
const hits = candidates[key] ?? [];
|
|
2311
|
+
if (!hits.length) continue;
|
|
2312
|
+
const want = centres[key];
|
|
2313
|
+
const px = originX + want.x * scaleX;
|
|
2314
|
+
const py = originY + want.y * scaleY;
|
|
2315
|
+
if (!hits.some((h) => Math.hypot(px - h.x, py - h.y) <= tolerance)) return null;
|
|
2316
|
+
verified++;
|
|
2317
|
+
}
|
|
2318
|
+
if (verified === 0) return null;
|
|
2319
|
+
return mapping2;
|
|
2320
|
+
}
|
|
2321
|
+
function mapRectToFrame(rect, m, frameW, frameH) {
|
|
2322
|
+
const x0 = m.originX + rect.left * m.scaleX;
|
|
2323
|
+
const y0 = m.originY + rect.top * m.scaleY;
|
|
2324
|
+
const sx = Math.max(0, Math.min(frameW, Math.round(x0)));
|
|
2325
|
+
const sy = Math.max(0, Math.min(frameH, Math.round(y0)));
|
|
2326
|
+
const sw = Math.min(frameW - sx, Math.round(rect.width * m.scaleX));
|
|
2327
|
+
const sh = Math.min(frameH - sy, Math.round(rect.height * m.scaleY));
|
|
2328
|
+
if (sw < 1 || sh < 1) return null;
|
|
2329
|
+
return { sx, sy, sw, sh };
|
|
2330
|
+
}
|
|
2331
|
+
function environmentSignature() {
|
|
2332
|
+
if (typeof window === "undefined") return "";
|
|
2333
|
+
return [
|
|
2334
|
+
window.innerWidth,
|
|
2335
|
+
window.innerHeight,
|
|
2336
|
+
window.outerWidth,
|
|
2337
|
+
window.outerHeight,
|
|
2338
|
+
window.screenX,
|
|
2339
|
+
window.screenY,
|
|
2340
|
+
Math.round((window.devicePixelRatio || 1) * 100)
|
|
2341
|
+
].join("x");
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2185
2344
|
// src/lib/screenCapture.ts
|
|
2186
2345
|
var ASPECT_TOLERANCE = 0.08;
|
|
2346
|
+
var CALIBRATION_SETTLE_MS = 220;
|
|
2187
2347
|
var FRESH_FRAME_TIMEOUT_MS = 500;
|
|
2188
2348
|
var VIDEO_READY_TIMEOUT_MS = 4e3;
|
|
2189
2349
|
var stream = null;
|
|
@@ -2191,22 +2351,14 @@ var track = null;
|
|
|
2191
2351
|
var video = null;
|
|
2192
2352
|
var grabber = null;
|
|
2193
2353
|
var declined = false;
|
|
2194
|
-
function supportsPreferCurrentTab() {
|
|
2195
|
-
const nav = navigator;
|
|
2196
|
-
const brands = nav.userAgentData?.brands;
|
|
2197
|
-
if (Array.isArray(brands) && brands.length) {
|
|
2198
|
-
return brands.some((b) => /chromium/i.test(b.brand));
|
|
2199
|
-
}
|
|
2200
|
-
const ua = navigator.userAgent || "";
|
|
2201
|
-
if (/CriOS|FxiOS|EdgiOS|Firefox/i.test(ua)) return false;
|
|
2202
|
-
return /Chrome|Chromium|Edg\//i.test(ua);
|
|
2203
|
-
}
|
|
2204
2354
|
function isExactCaptureSupported() {
|
|
2205
2355
|
if (typeof navigator === "undefined" || typeof document === "undefined") return false;
|
|
2206
2356
|
const md = navigator.mediaDevices;
|
|
2207
|
-
|
|
2208
|
-
return supportsPreferCurrentTab();
|
|
2357
|
+
return !!md && typeof md.getDisplayMedia === "function";
|
|
2209
2358
|
}
|
|
2359
|
+
var mode = "tab";
|
|
2360
|
+
var mapping = null;
|
|
2361
|
+
var mappingSignature = "";
|
|
2210
2362
|
function getExactCaptureStatus() {
|
|
2211
2363
|
if (!isExactCaptureSupported()) return "unsupported";
|
|
2212
2364
|
if (track && track.readyState === "live") return "live";
|
|
@@ -2234,6 +2386,9 @@ function teardown() {
|
|
|
2234
2386
|
stream = null;
|
|
2235
2387
|
track = null;
|
|
2236
2388
|
grabber = null;
|
|
2389
|
+
mode = "tab";
|
|
2390
|
+
mapping = null;
|
|
2391
|
+
mappingSignature = "";
|
|
2237
2392
|
}
|
|
2238
2393
|
function stopExactCapture() {
|
|
2239
2394
|
teardown();
|
|
@@ -2288,6 +2443,78 @@ function validateFrameMapping(frameW, frameH) {
|
|
|
2288
2443
|
const viewAspect = vw / vh;
|
|
2289
2444
|
return Math.abs(frameAspect - viewAspect) / viewAspect <= ASPECT_TOLERANCE;
|
|
2290
2445
|
}
|
|
2446
|
+
async function grabFrameCanvas() {
|
|
2447
|
+
if (!video) return null;
|
|
2448
|
+
await nextPresentedFrame(video);
|
|
2449
|
+
let source = video;
|
|
2450
|
+
let w = video.videoWidth;
|
|
2451
|
+
let h = video.videoHeight;
|
|
2452
|
+
let bitmap = null;
|
|
2453
|
+
if (grabber) {
|
|
2454
|
+
try {
|
|
2455
|
+
bitmap = await grabber.grabFrame();
|
|
2456
|
+
source = bitmap;
|
|
2457
|
+
w = bitmap.width;
|
|
2458
|
+
h = bitmap.height;
|
|
2459
|
+
} catch {
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
if (!w || !h) {
|
|
2463
|
+
bitmap?.close?.();
|
|
2464
|
+
return null;
|
|
2465
|
+
}
|
|
2466
|
+
const c = document.createElement("canvas");
|
|
2467
|
+
c.width = w;
|
|
2468
|
+
c.height = h;
|
|
2469
|
+
const ctx = c.getContext("2d", { willReadFrequently: true });
|
|
2470
|
+
if (!ctx) {
|
|
2471
|
+
bitmap?.close?.();
|
|
2472
|
+
return null;
|
|
2473
|
+
}
|
|
2474
|
+
ctx.drawImage(source, 0, 0);
|
|
2475
|
+
bitmap?.close?.();
|
|
2476
|
+
return c;
|
|
2477
|
+
}
|
|
2478
|
+
async function calibrate() {
|
|
2479
|
+
if (!video) return false;
|
|
2480
|
+
const vw = window.innerWidth;
|
|
2481
|
+
const vh = window.innerHeight;
|
|
2482
|
+
if (vw <= MARKER_SIZE || vh <= MARKER_SIZE) return false;
|
|
2483
|
+
const card = document.createElement("div");
|
|
2484
|
+
card.setAttribute("data-qa-calibration", "true");
|
|
2485
|
+
card.style.cssText = "position:fixed;inset:0;z-index:2147483647;background:#0a0a0a;pointer-events:none;contain:strict;";
|
|
2486
|
+
const centres = markerCentres(vw, vh);
|
|
2487
|
+
for (const m of MARKERS) {
|
|
2488
|
+
const dot = document.createElement("div");
|
|
2489
|
+
const c = centres[m.key];
|
|
2490
|
+
dot.style.cssText = `position:absolute;width:${MARKER_SIZE}px;height:${MARKER_SIZE}px;left:${c.x - MARKER_SIZE / 2}px;top:${c.y - MARKER_SIZE / 2}px;background:rgb(${m.color[0]},${m.color[1]},${m.color[2]});`;
|
|
2491
|
+
card.appendChild(dot);
|
|
2492
|
+
}
|
|
2493
|
+
document.body.appendChild(card);
|
|
2494
|
+
try {
|
|
2495
|
+
await new Promise((r) => setTimeout(r, CALIBRATION_SETTLE_MS));
|
|
2496
|
+
const frame = await grabFrameCanvas();
|
|
2497
|
+
if (!frame) return false;
|
|
2498
|
+
const ctx = frame.getContext("2d", { willReadFrequently: true });
|
|
2499
|
+
if (!ctx) return false;
|
|
2500
|
+
const { data } = ctx.getImageData(0, 0, frame.width, frame.height);
|
|
2501
|
+
const found = deriveMapping(
|
|
2502
|
+
findMarkerCandidates(data, frame.width, frame.height),
|
|
2503
|
+
vw,
|
|
2504
|
+
vh,
|
|
2505
|
+
frame.width,
|
|
2506
|
+
frame.height
|
|
2507
|
+
);
|
|
2508
|
+
if (!found) return false;
|
|
2509
|
+
mapping = found;
|
|
2510
|
+
mappingSignature = environmentSignature();
|
|
2511
|
+
return true;
|
|
2512
|
+
} catch {
|
|
2513
|
+
return false;
|
|
2514
|
+
} finally {
|
|
2515
|
+
card.remove();
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2291
2518
|
async function startExactCapture() {
|
|
2292
2519
|
if (!isExactCaptureSupported()) return false;
|
|
2293
2520
|
if (track && track.readyState === "live" && video) return true;
|
|
@@ -2317,11 +2544,7 @@ async function startExactCapture() {
|
|
|
2317
2544
|
return false;
|
|
2318
2545
|
}
|
|
2319
2546
|
const settings = t.getSettings();
|
|
2320
|
-
|
|
2321
|
-
for (const x of s.getTracks()) x.stop();
|
|
2322
|
-
declined = true;
|
|
2323
|
-
return false;
|
|
2324
|
-
}
|
|
2547
|
+
const sharedTab = !settings.displaySurface || settings.displaySurface === "browser";
|
|
2325
2548
|
const el = document.createElement("video");
|
|
2326
2549
|
el.setAttribute("data-qa-overlay", "true");
|
|
2327
2550
|
el.muted = true;
|
|
@@ -2334,17 +2557,31 @@ async function startExactCapture() {
|
|
|
2334
2557
|
} catch {
|
|
2335
2558
|
}
|
|
2336
2559
|
const ready = await waitFor(() => el.videoWidth > 0 && el.videoHeight > 0, VIDEO_READY_TIMEOUT_MS);
|
|
2337
|
-
|
|
2560
|
+
const giveUp = () => {
|
|
2338
2561
|
el.remove();
|
|
2339
2562
|
for (const x of s.getTracks()) x.stop();
|
|
2340
2563
|
declined = true;
|
|
2341
2564
|
return false;
|
|
2342
|
-
}
|
|
2565
|
+
};
|
|
2566
|
+
if (!ready) return giveUp();
|
|
2567
|
+
const isDirect = sharedTab && validateFrameMapping(el.videoWidth, el.videoHeight);
|
|
2343
2568
|
stream = s;
|
|
2344
2569
|
track = t;
|
|
2345
2570
|
video = el;
|
|
2346
2571
|
const IC = window.ImageCapture;
|
|
2347
2572
|
grabber = IC ? new IC(t) : null;
|
|
2573
|
+
if (isDirect) {
|
|
2574
|
+
mode = "tab";
|
|
2575
|
+
mapping = null;
|
|
2576
|
+
mappingSignature = "";
|
|
2577
|
+
} else {
|
|
2578
|
+
mode = "surface";
|
|
2579
|
+
if (!await calibrate()) {
|
|
2580
|
+
teardown();
|
|
2581
|
+
declined = true;
|
|
2582
|
+
return false;
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2348
2585
|
t.addEventListener("ended", () => {
|
|
2349
2586
|
teardown();
|
|
2350
2587
|
});
|
|
@@ -2357,6 +2594,11 @@ async function startExactCapture() {
|
|
|
2357
2594
|
}
|
|
2358
2595
|
async function grabExactRegion(rect) {
|
|
2359
2596
|
if (!track || track.readyState !== "live" || !video) return null;
|
|
2597
|
+
if (mode === "surface") {
|
|
2598
|
+
if (!mapping || environmentSignature() !== mappingSignature) {
|
|
2599
|
+
if (!await calibrate()) return null;
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2360
2602
|
await nextPresentedFrame(video);
|
|
2361
2603
|
let source;
|
|
2362
2604
|
let frameW;
|
|
@@ -2379,13 +2621,24 @@ async function grabExactRegion(rect) {
|
|
|
2379
2621
|
frameH = video.videoHeight;
|
|
2380
2622
|
}
|
|
2381
2623
|
try {
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2624
|
+
let sx;
|
|
2625
|
+
let sy;
|
|
2626
|
+
let sw;
|
|
2627
|
+
let sh;
|
|
2628
|
+
if (mode === "surface") {
|
|
2629
|
+
if (!mapping) return null;
|
|
2630
|
+
const mapped = mapRectToFrame(rect, mapping, frameW, frameH);
|
|
2631
|
+
if (!mapped) return null;
|
|
2632
|
+
({ sx, sy, sw, sh } = mapped);
|
|
2633
|
+
} else {
|
|
2634
|
+
if (!validateFrameMapping(frameW, frameH)) return null;
|
|
2635
|
+
const scaleX = frameW / window.innerWidth;
|
|
2636
|
+
const scaleY = frameH / window.innerHeight;
|
|
2637
|
+
sx = Math.max(0, Math.min(frameW, Math.round(rect.left * scaleX)));
|
|
2638
|
+
sy = Math.max(0, Math.min(frameH, Math.round(rect.top * scaleY)));
|
|
2639
|
+
sw = Math.max(1, Math.min(frameW - sx, Math.round(rect.width * scaleX)));
|
|
2640
|
+
sh = Math.max(1, Math.min(frameH - sy, Math.round(rect.height * scaleY)));
|
|
2641
|
+
}
|
|
2389
2642
|
const canvas = document.createElement("canvas");
|
|
2390
2643
|
canvas.width = sw;
|
|
2391
2644
|
canvas.height = sh;
|
|
@@ -3031,15 +3284,15 @@ function fmtPct(n, d) {
|
|
|
3031
3284
|
}
|
|
3032
3285
|
function summariseSession(notes, stamp) {
|
|
3033
3286
|
if (!notes.length) return "No points captured.";
|
|
3034
|
-
let bugs = 0,
|
|
3287
|
+
let bugs = 0, enhancements = 0, designs = 0, verified = 0, awaiting = 0;
|
|
3035
3288
|
const pages = /* @__PURE__ */ new Set();
|
|
3036
3289
|
let earliest = Number.POSITIVE_INFINITY;
|
|
3037
3290
|
let latest = 0;
|
|
3038
3291
|
for (const n of notes) {
|
|
3039
3292
|
const sev = n.severity ?? "bug";
|
|
3040
3293
|
if (sev === "bug") bugs++;
|
|
3041
|
-
else if (sev === "
|
|
3042
|
-
else
|
|
3294
|
+
else if (sev === "enhance") enhancements++;
|
|
3295
|
+
else designs++;
|
|
3043
3296
|
const status = n.status ?? "open";
|
|
3044
3297
|
if (status === "verified") verified++;
|
|
3045
3298
|
else if (status === "fixed") awaiting++;
|
|
@@ -3054,8 +3307,8 @@ function summariseSession(notes, stamp) {
|
|
|
3054
3307
|
`${notes.length} point${notes.length === 1 ? "" : "s"}`,
|
|
3055
3308
|
`${bugs} bug${bugs === 1 ? "" : "s"}`
|
|
3056
3309
|
];
|
|
3057
|
-
if (
|
|
3058
|
-
if (
|
|
3310
|
+
if (enhancements) parts.push(`${enhancements} enhancement${enhancements === 1 ? "" : "s"}`);
|
|
3311
|
+
if (designs) parts.push(`${designs} design`);
|
|
3059
3312
|
if (verified) parts.push(`${verified} verified`);
|
|
3060
3313
|
if (awaiting) parts.push(`${awaiting} awaiting re-test`);
|
|
3061
3314
|
parts.push(`${pages.size} page${pages.size === 1 ? "" : "s"}`);
|
|
@@ -3086,6 +3339,37 @@ Exported: ${stamp}
|
|
|
3086
3339
|
Points: ${noteCount}
|
|
3087
3340
|
|
|
3088
3341
|
**Session:** ${summariseSession(notes, stamp)}`
|
|
3342
|
+
);
|
|
3343
|
+
sections.push(
|
|
3344
|
+
`## What the tags mean
|
|
3345
|
+
|
|
3346
|
+
Every point below is tagged **Bug**, **Design** or **Enhance**. These are not priorities \u2014 they are three different kinds of work. Read the tag before you decide what to do.
|
|
3347
|
+
|
|
3348
|
+
### Bug \u2014 something is broken
|
|
3349
|
+
|
|
3350
|
+
Find the **root cause**, not the symptom. Do not patch over it.
|
|
3351
|
+
|
|
3352
|
+
State the cause in one plain sentence before you change anything \u2014 e.g. "the date was wrong because the server sends UTC and the list formats in local time". **If you cannot write that sentence, you have not found the cause yet.** Keep looking. The real cause is often something small and silly; that is normal, and it is still the cause.
|
|
3353
|
+
|
|
3354
|
+
**The cause is not always in the code.** If the code does exactly what it was asked to do and the result is still wrong, then nothing is broken \u2014 what was *asked for* and what was *meant* did not match. That mismatch is the bug. Realign to what the owner meant and fix it there. If the note already says what they meant ("this isn't what I wanted, I wanted X"), they have told you \u2014 just do X. Do not stop to ask a question they have already answered.
|
|
3355
|
+
|
|
3356
|
+
### Design \u2014 it works, but it looks or feels wrong
|
|
3357
|
+
|
|
3358
|
+
The functionality is fine and is not in question. Do not go hunting for a fault; there isn't one. Think properly about the interface and the experience \u2014 layout, spacing, hierarchy, wording, states, what the person is actually trying to do \u2014 and change how it looks and feels.
|
|
3359
|
+
|
|
3360
|
+
### Enhance \u2014 a new idea, thought of while using the app
|
|
3361
|
+
|
|
3362
|
+
The tester wants something added or changed: a new section, a new feature, a different way of doing it.
|
|
3363
|
+
|
|
3364
|
+
**Plan it before you build it.** Read the surrounding code, work out what it touches and what it might break, and choose an approach that fits how the app is already built. Then build it. You do not need to stop and ask for approval \u2014 plan well, then go.
|
|
3365
|
+
|
|
3366
|
+
Push back only when you have a real reason: it would break something, or it conflicts with an invariant below. Then say so plainly and propose the alternative you would build instead.
|
|
3367
|
+
|
|
3368
|
+
### When the tag and the words disagree
|
|
3369
|
+
|
|
3370
|
+
The tag sets your starting posture; **the words win.** A point tagged Design that says "this crashes" is a bug \u2014 treat it as one, and mention that you changed lane. People pick the wrong chip all the time; that is not a reason to do the wrong work.
|
|
3371
|
+
|
|
3372
|
+
And if a point is phrased as a question rather than a report, **answer it \u2014 do not change code on a maybe.**`
|
|
3089
3373
|
);
|
|
3090
3374
|
const stack = typeof p.stack === "string" && p.stack.trim() ? p.stack.trim() : "(not provided)";
|
|
3091
3375
|
const runArr = toStrings(p.runCommands);
|
|
@@ -3681,12 +3965,12 @@ function campaignJsonText(allNotes) {
|
|
|
3681
3965
|
function reportMarkdownText(allNotes) {
|
|
3682
3966
|
if (!campaign) return "";
|
|
3683
3967
|
const ordered = orderedKnownNotes(allNotes);
|
|
3684
|
-
const counts = { bug: 0,
|
|
3968
|
+
const counts = { bug: 0, design: 0, enhance: 0, verified: 0 };
|
|
3685
3969
|
for (const n of ordered) {
|
|
3686
3970
|
const sev = n.severity ?? "bug";
|
|
3687
3971
|
if (sev === "bug") counts.bug++;
|
|
3688
|
-
else if (sev === "
|
|
3689
|
-
else counts.
|
|
3972
|
+
else if (sev === "enhance") counts.enhance++;
|
|
3973
|
+
else counts.design++;
|
|
3690
3974
|
if (n.status === "verified") counts.verified++;
|
|
3691
3975
|
}
|
|
3692
3976
|
const header = [
|
|
@@ -3695,7 +3979,7 @@ function reportMarkdownText(allNotes) {
|
|
|
3695
3979
|
campaign.tester ? `Tester: ${campaign.tester} ` : "",
|
|
3696
3980
|
`Started: ${campaign.startedAt} `,
|
|
3697
3981
|
`Updated: ${(/* @__PURE__ */ new Date()).toISOString()} `,
|
|
3698
|
-
`Points: ${ordered.length} (${counts.bug} bug, ${counts.
|
|
3982
|
+
`Points: ${ordered.length} (${counts.bug} bug, ${counts.design} design, ${counts.enhance} enhance \xB7 ${counts.verified} verified)`,
|
|
3699
3983
|
"",
|
|
3700
3984
|
getFsSyncEngine() === "download" ? "Saved by Qapture as the campaign was tested. Screenshots are in" : "Written live by Qapture as each point was saved. Screenshots are in",
|
|
3701
3985
|
"`screenshots/`, one Markdown file per point in `notes/`.",
|
|
@@ -4047,6 +4331,12 @@ function QaProvider({
|
|
|
4047
4331
|
for (const id of pendingIds) void idb.delete(id);
|
|
4048
4332
|
storage.setJSON(PENDING_DELETE_KEY, []);
|
|
4049
4333
|
}
|
|
4334
|
+
live = live.map((n) => {
|
|
4335
|
+
const legacy = n.severity;
|
|
4336
|
+
if (legacy === "question") return { ...n, severity: "enhance" };
|
|
4337
|
+
if (legacy === "polish") return { ...n, severity: "design" };
|
|
4338
|
+
return n;
|
|
4339
|
+
});
|
|
4050
4340
|
const sorted = live.slice().sort(
|
|
4051
4341
|
(a, b) => a.timestamp < b.timestamp ? 1 : -1
|
|
4052
4342
|
);
|
|
@@ -4645,8 +4935,8 @@ function QaProvider({
|
|
|
4645
4935
|
const counts = {
|
|
4646
4936
|
all: notes.length,
|
|
4647
4937
|
bug: 0,
|
|
4648
|
-
|
|
4649
|
-
|
|
4938
|
+
design: 0,
|
|
4939
|
+
enhance: 0,
|
|
4650
4940
|
open: 0,
|
|
4651
4941
|
fixed: 0,
|
|
4652
4942
|
verified: 0,
|
|
@@ -4655,8 +4945,8 @@ function QaProvider({
|
|
|
4655
4945
|
for (const n of notes) {
|
|
4656
4946
|
const sev = n.severity ?? "bug";
|
|
4657
4947
|
if (sev === "bug") counts.bug++;
|
|
4658
|
-
else if (sev === "
|
|
4659
|
-
else counts.
|
|
4948
|
+
else if (sev === "enhance") counts.enhance++;
|
|
4949
|
+
else counts.design++;
|
|
4660
4950
|
const status = n.status ?? "open";
|
|
4661
4951
|
if (status === "verified") counts.verified++;
|
|
4662
4952
|
else if (status === "fixed") counts.fixed++;
|
|
@@ -5045,7 +5335,11 @@ function QaProvider({
|
|
|
5045
5335
|
pendingShare,
|
|
5046
5336
|
sharePending,
|
|
5047
5337
|
retestNote,
|
|
5048
|
-
|
|
5338
|
+
// The greeting explains how to capture. Once a note exists, the tester has
|
|
5339
|
+
// either done it or found their own way, so the line has nothing left to
|
|
5340
|
+
// say — take it away rather than making them dismiss it. (The stored flag
|
|
5341
|
+
// still governs a first visit; this only hides it early.)
|
|
5342
|
+
showWelcome: showWelcome && notes.length === 0,
|
|
5049
5343
|
dismissWelcome,
|
|
5050
5344
|
// v0.6
|
|
5051
5345
|
panelSide,
|
|
@@ -5498,8 +5792,8 @@ function QaFab() {
|
|
|
5498
5792
|
}
|
|
5499
5793
|
var SEVERITIES = [
|
|
5500
5794
|
{ value: "bug", labelKey: "sev_bug", icon: "Bug" },
|
|
5501
|
-
{ value: "
|
|
5502
|
-
{ value: "
|
|
5795
|
+
{ value: "enhance", labelKey: "sev_enhance" },
|
|
5796
|
+
{ value: "design", labelKey: "sev_design" }
|
|
5503
5797
|
];
|
|
5504
5798
|
function SeverityChipRow({
|
|
5505
5799
|
value,
|
|
@@ -6244,13 +6538,13 @@ function KindBadge({
|
|
|
6244
6538
|
}
|
|
6245
6539
|
var SEVERITY_CLASS = {
|
|
6246
6540
|
bug: "qa-bg-danger-tint qa-text-danger",
|
|
6247
|
-
|
|
6248
|
-
|
|
6541
|
+
enhance: "qa-bg-warn-tint qa-text-warn",
|
|
6542
|
+
design: "qa-bg-accent-tint qa-text-accent"
|
|
6249
6543
|
};
|
|
6250
6544
|
var SEVERITY_LABEL_KEY = {
|
|
6251
6545
|
bug: "sev_bug",
|
|
6252
|
-
|
|
6253
|
-
|
|
6546
|
+
enhance: "sev_enhance",
|
|
6547
|
+
design: "sev_design"
|
|
6254
6548
|
};
|
|
6255
6549
|
function SeverityChip({ severity, t }) {
|
|
6256
6550
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -6355,7 +6649,7 @@ function NoteItem({
|
|
|
6355
6649
|
notify(t("copy_failed"), { tone: "error" });
|
|
6356
6650
|
}
|
|
6357
6651
|
};
|
|
6358
|
-
const severityDot = severity === "bug" ? "var(--qa-danger)" : severity === "
|
|
6652
|
+
const severityDot = severity === "bug" ? "var(--qa-danger)" : severity === "enhance" ? "var(--qa-warn)" : "var(--qa-accent)";
|
|
6359
6653
|
if (dense && !open && !editing) {
|
|
6360
6654
|
return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6361
6655
|
"button",
|
|
@@ -6898,22 +7192,22 @@ function NoteFilterBar() {
|
|
|
6898
7192
|
onClick: sev("bug")
|
|
6899
7193
|
},
|
|
6900
7194
|
{
|
|
6901
|
-
key: "
|
|
6902
|
-
label: t("
|
|
6903
|
-
count: noteCounts.
|
|
6904
|
-
active: filter.severity === "
|
|
6905
|
-
icon: "
|
|
7195
|
+
key: "enhance",
|
|
7196
|
+
label: t("sev_enhance"),
|
|
7197
|
+
count: noteCounts.enhance,
|
|
7198
|
+
active: filter.severity === "enhance",
|
|
7199
|
+
icon: "Plus",
|
|
6906
7200
|
tone: "warn",
|
|
6907
|
-
onClick: sev("
|
|
7201
|
+
onClick: sev("enhance")
|
|
6908
7202
|
},
|
|
6909
7203
|
{
|
|
6910
|
-
key: "
|
|
6911
|
-
label: t("
|
|
6912
|
-
count: noteCounts.
|
|
6913
|
-
active: filter.severity === "
|
|
7204
|
+
key: "design",
|
|
7205
|
+
label: t("sev_design"),
|
|
7206
|
+
count: noteCounts.design,
|
|
7207
|
+
active: filter.severity === "design",
|
|
6914
7208
|
icon: "Pencil",
|
|
6915
7209
|
tone: "accent",
|
|
6916
|
-
onClick: sev("
|
|
7210
|
+
onClick: sev("design")
|
|
6917
7211
|
},
|
|
6918
7212
|
{
|
|
6919
7213
|
key: "open",
|
|
@@ -7352,46 +7646,25 @@ function SettingsSheet({ onClose }) {
|
|
|
7352
7646
|
);
|
|
7353
7647
|
}
|
|
7354
7648
|
function WelcomeCard() {
|
|
7355
|
-
const { t,
|
|
7649
|
+
const { t, dismissWelcome } = useQa();
|
|
7356
7650
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7357
7651
|
"div",
|
|
7358
7652
|
{
|
|
7359
|
-
className: "qa-rounded-xl qa-border qa-border-subtle qa-bg-1 qa-
|
|
7653
|
+
className: "qa-flex qa-items-center qa-gap-2 qa-rounded-xl qa-border qa-border-subtle qa-bg-1 qa-px-3 qa-py-2",
|
|
7360
7654
|
role: "note",
|
|
7361
7655
|
children: [
|
|
7362
|
-
/* @__PURE__ */ jsxRuntime.
|
|
7363
|
-
|
|
7364
|
-
"span",
|
|
7365
|
-
{
|
|
7366
|
-
"aria-hidden": "true",
|
|
7367
|
-
className: "qa-shrink-0",
|
|
7368
|
-
style: { width: 6, height: 6, background: "var(--qa-accent)" }
|
|
7369
|
-
}
|
|
7370
|
-
),
|
|
7371
|
-
t("welcome_title", { brand: brand.label })
|
|
7372
|
-
] }),
|
|
7373
|
-
/* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "qa-m-0 qa-space-y-1 qa-text-xs qa-text-mid", children: [
|
|
7374
|
-
/* @__PURE__ */ jsxRuntime.jsxs("li", { className: "qa-flex qa-items-start qa-gap-1.5", children: [
|
|
7375
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "Crosshair", size: 12, className: "qa-shrink-0 qa-mt-1" }),
|
|
7376
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: t("welcome_capture") })
|
|
7377
|
-
] }),
|
|
7378
|
-
/* @__PURE__ */ jsxRuntime.jsxs("li", { className: "qa-flex qa-items-start qa-gap-1.5", children: [
|
|
7379
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "Pencil", size: 12, className: "qa-shrink-0 qa-mt-1" }),
|
|
7380
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: t("welcome_say") })
|
|
7381
|
-
] }),
|
|
7382
|
-
/* @__PURE__ */ jsxRuntime.jsxs("li", { className: "qa-flex qa-items-start qa-gap-1.5", children: [
|
|
7383
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "CheckCircle2", size: 12, className: "qa-shrink-0 qa-mt-1" }),
|
|
7384
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: t("welcome_safe") })
|
|
7385
|
-
] })
|
|
7386
|
-
] }),
|
|
7656
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "Crosshair", size: 13, className: "qa-shrink-0 qa-text-accent" }),
|
|
7657
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "qa-flex-1 qa-text-xs qa-text-mid", children: t("welcome_capture") }),
|
|
7387
7658
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7388
7659
|
"button",
|
|
7389
7660
|
{
|
|
7390
7661
|
type: "button",
|
|
7391
7662
|
onClick: dismissWelcome,
|
|
7392
|
-
|
|
7393
|
-
|
|
7394
|
-
|
|
7663
|
+
"aria-label": t("welcome_got_it"),
|
|
7664
|
+
title: t("welcome_got_it"),
|
|
7665
|
+
className: "qa-tap-icon qa-shrink-0 qa-rounded-full qa-text-mid qa-focus-ring",
|
|
7666
|
+
style: { background: "transparent", border: "none", cursor: "pointer", padding: 2 },
|
|
7667
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "X", size: 13 })
|
|
7395
7668
|
}
|
|
7396
7669
|
)
|
|
7397
7670
|
]
|
|
@@ -8858,16 +9131,16 @@ function unlockPageScroll() {
|
|
|
8858
9131
|
var DRAG_THRESHOLD2 = 6;
|
|
8859
9132
|
var TOUCH_DRAG_THRESHOLD = 12;
|
|
8860
9133
|
var MIN_REGION_SIZE = 8;
|
|
8861
|
-
var SEVERITIES2 = ["bug", "
|
|
9134
|
+
var SEVERITIES2 = ["bug", "design", "enhance"];
|
|
8862
9135
|
var SEVERITY_ICON = {
|
|
8863
9136
|
bug: "Bug",
|
|
8864
|
-
|
|
8865
|
-
|
|
9137
|
+
design: "Pencil",
|
|
9138
|
+
enhance: "Plus"
|
|
8866
9139
|
};
|
|
8867
9140
|
var SEVERITY_LABEL_KEY2 = {
|
|
8868
9141
|
bug: "sev_bug",
|
|
8869
|
-
|
|
8870
|
-
|
|
9142
|
+
design: "sev_design",
|
|
9143
|
+
enhance: "sev_enhance"
|
|
8871
9144
|
};
|
|
8872
9145
|
function clampRegionRect(rect) {
|
|
8873
9146
|
const vw = typeof window !== "undefined" ? window.innerWidth : rect.left + rect.width;
|
|
@@ -9821,7 +10094,7 @@ function CaptureMode() {
|
|
|
9821
10094
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "qa-text-11 qa-text-mid qa-me-1", children: t("severity_label") }),
|
|
9822
10095
|
SEVERITIES2.map((sev) => {
|
|
9823
10096
|
const active = severity === sev;
|
|
9824
|
-
const toneClass = sev === "bug" ? "qa-bg-danger-tint qa-text-danger" : sev === "
|
|
10097
|
+
const toneClass = sev === "bug" ? "qa-bg-danger-tint qa-text-danger" : sev === "enhance" ? "qa-bg-warn-tint qa-text-warn" : "qa-bg-accent-tint qa-text-accent";
|
|
9825
10098
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9826
10099
|
"button",
|
|
9827
10100
|
{
|
|
@@ -10101,5 +10374,5 @@ function Qapture({ config }) {
|
|
|
10101
10374
|
exports.Qapture = Qapture;
|
|
10102
10375
|
exports.deleteQaDatabase = deleteQaDatabase;
|
|
10103
10376
|
exports.initQaStudio = initQaStudio;
|
|
10104
|
-
//# sourceMappingURL=chunk-
|
|
10105
|
-
//# sourceMappingURL=chunk-
|
|
10377
|
+
//# sourceMappingURL=chunk-WU7QMHPK.cjs.map
|
|
10378
|
+
//# sourceMappingURL=chunk-WU7QMHPK.cjs.map
|