qapture2 0.9.0 → 0.9.1
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/{chunk-B22X5Y6U.js → chunk-HEHVZCSV.js} +139 -311
- package/dist/{chunk-5EWNGJ2N.cjs → chunk-J77SGE5E.cjs} +139 -311
- package/dist/index.cjs +5 -5
- package/dist/index.d.cts +25 -0
- package/dist/index.d.ts +25 -0
- 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 +1 -1
|
@@ -170,6 +170,7 @@ var DEFAULTS = {
|
|
|
170
170
|
loginField: { en: "Username", ar: "\u0627\u0633\u0645 \u0627\u0644\u0645\u0633\u062A\u062E\u062F\u0645" },
|
|
171
171
|
rtl: false,
|
|
172
172
|
beta: false,
|
|
173
|
+
collector: null,
|
|
173
174
|
visible: void 0,
|
|
174
175
|
alwaysVisible: false,
|
|
175
176
|
hotkey: "shift+alt+q",
|
|
@@ -342,6 +343,7 @@ function validateConfig(input) {
|
|
|
342
343
|
preamble: null,
|
|
343
344
|
rtl: DEFAULTS.rtl,
|
|
344
345
|
beta: DEFAULTS.beta,
|
|
346
|
+
collector: null,
|
|
345
347
|
visible: DEFAULTS.visible,
|
|
346
348
|
alwaysVisible: DEFAULTS.alwaysVisible,
|
|
347
349
|
hotkey: DEFAULTS.hotkey,
|
|
@@ -363,6 +365,7 @@ function validateConfig(input) {
|
|
|
363
365
|
preamble: null,
|
|
364
366
|
rtl: DEFAULTS.rtl,
|
|
365
367
|
beta: DEFAULTS.beta,
|
|
368
|
+
collector: null,
|
|
366
369
|
visible: DEFAULTS.visible,
|
|
367
370
|
alwaysVisible: DEFAULTS.alwaysVisible,
|
|
368
371
|
hotkey: DEFAULTS.hotkey,
|
|
@@ -399,6 +402,14 @@ function validateConfig(input) {
|
|
|
399
402
|
const preamble = raw["preamble"] !== void 0 ? coercePreamble(raw["preamble"]) : null;
|
|
400
403
|
const rtl = typeof raw["rtl"] === "boolean" ? raw["rtl"] : DEFAULTS.rtl;
|
|
401
404
|
const beta = typeof raw["beta"] === "boolean" ? raw["beta"] : DEFAULTS.beta;
|
|
405
|
+
const rawCol = raw["collector"];
|
|
406
|
+
const collector = rawCol && typeof rawCol["url"] === "string" && typeof rawCol["token"] === "string" && typeof rawCol["project"] === "string" ? {
|
|
407
|
+
url: rawCol["url"],
|
|
408
|
+
token: rawCol["token"],
|
|
409
|
+
project: rawCol["project"],
|
|
410
|
+
campaign: typeof rawCol["campaign"] === "string" ? rawCol["campaign"] : void 0,
|
|
411
|
+
tester: typeof rawCol["tester"] === "string" ? rawCol["tester"] : void 0
|
|
412
|
+
} : DEFAULTS.collector;
|
|
402
413
|
const alwaysVisible = typeof raw["alwaysVisible"] === "boolean" ? raw["alwaysVisible"] : DEFAULTS.alwaysVisible;
|
|
403
414
|
const hotkey = isNonEmptyString(raw["hotkey"]) ? raw["hotkey"].trim() : DEFAULTS.hotkey;
|
|
404
415
|
const captureHotkey = isNonEmptyString(raw["captureHotkey"]) ? raw["captureHotkey"].trim() : DEFAULTS.captureHotkey;
|
|
@@ -422,6 +433,7 @@ function validateConfig(input) {
|
|
|
422
433
|
preamble,
|
|
423
434
|
rtl,
|
|
424
435
|
beta,
|
|
436
|
+
collector,
|
|
425
437
|
visible,
|
|
426
438
|
alwaysVisible,
|
|
427
439
|
hotkey,
|
|
@@ -1699,6 +1711,98 @@ function createStorage(namespace) {
|
|
|
1699
1711
|
return { getItem, setItem, getJSON, setJSON };
|
|
1700
1712
|
}
|
|
1701
1713
|
|
|
1714
|
+
// src/lib/faultLog.ts
|
|
1715
|
+
var LIMIT = 40;
|
|
1716
|
+
var faults = [];
|
|
1717
|
+
function recordFault(where, err) {
|
|
1718
|
+
const what = err instanceof Error ? `${err.name}: ${err.message}` : typeof err === "string" ? err : (() => {
|
|
1719
|
+
try {
|
|
1720
|
+
return JSON.stringify(err);
|
|
1721
|
+
} catch {
|
|
1722
|
+
return String(err);
|
|
1723
|
+
}
|
|
1724
|
+
})();
|
|
1725
|
+
faults.push({ at: Date.now(), where, what: what.slice(0, 500) });
|
|
1726
|
+
if (faults.length > LIMIT) faults.splice(0, faults.length - LIMIT);
|
|
1727
|
+
console.warn(`[QA] ${where}:`, err);
|
|
1728
|
+
}
|
|
1729
|
+
function readFaults() {
|
|
1730
|
+
return [...faults].reverse();
|
|
1731
|
+
}
|
|
1732
|
+
function clearFaults() {
|
|
1733
|
+
faults.length = 0;
|
|
1734
|
+
}
|
|
1735
|
+
function faultsAsText(version) {
|
|
1736
|
+
if (!faults.length) return "No faults recorded.";
|
|
1737
|
+
const head = [
|
|
1738
|
+
`qapture ${version}`,
|
|
1739
|
+
typeof navigator !== "undefined" ? navigator.userAgent : "",
|
|
1740
|
+
typeof location !== "undefined" ? location.href.split("?")[0] : "",
|
|
1741
|
+
""
|
|
1742
|
+
].filter(Boolean).join("\n");
|
|
1743
|
+
return head + readFaults().map((f) => `${new Date(f.at).toISOString()} [${f.where}] ${f.what}`).join("\n");
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
// src/lib/collector.ts
|
|
1747
|
+
var TIMEOUT_MS = 8e3;
|
|
1748
|
+
var MAX_SHOT_BYTES = 6 * 1024 * 1024;
|
|
1749
|
+
function blobToDataUrl(blob) {
|
|
1750
|
+
return new Promise((resolve) => {
|
|
1751
|
+
try {
|
|
1752
|
+
const reader = new FileReader();
|
|
1753
|
+
reader.onload = () => resolve(typeof reader.result === "string" ? reader.result : null);
|
|
1754
|
+
reader.onerror = () => resolve(null);
|
|
1755
|
+
reader.readAsDataURL(blob);
|
|
1756
|
+
} catch {
|
|
1757
|
+
resolve(null);
|
|
1758
|
+
}
|
|
1759
|
+
});
|
|
1760
|
+
}
|
|
1761
|
+
async function sendToCollector(note, cfg) {
|
|
1762
|
+
if (typeof fetch === "undefined" || !cfg?.url || !cfg.token || !cfg.project) return false;
|
|
1763
|
+
let shot;
|
|
1764
|
+
if (note.screenshot && note.screenshot.size <= MAX_SHOT_BYTES) {
|
|
1765
|
+
shot = await blobToDataUrl(note.screenshot) ?? void 0;
|
|
1766
|
+
}
|
|
1767
|
+
const controller = new AbortController();
|
|
1768
|
+
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
1769
|
+
try {
|
|
1770
|
+
const res = await fetch(`${cfg.url.replace(/\/$/, "")}/notes`, {
|
|
1771
|
+
method: "POST",
|
|
1772
|
+
signal: controller.signal,
|
|
1773
|
+
headers: {
|
|
1774
|
+
"content-type": "application/json",
|
|
1775
|
+
authorization: `Bearer ${cfg.token}`
|
|
1776
|
+
},
|
|
1777
|
+
body: JSON.stringify({
|
|
1778
|
+
project: cfg.project,
|
|
1779
|
+
// A campaign per day is the shape that matches how testing actually
|
|
1780
|
+
// happens, and it means nobody has to name anything.
|
|
1781
|
+
campaign: cfg.campaign || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
1782
|
+
tester: cfg.tester,
|
|
1783
|
+
id: note.id,
|
|
1784
|
+
route: note.route,
|
|
1785
|
+
description: note.description,
|
|
1786
|
+
wanted: note.wanted,
|
|
1787
|
+
why: note.why,
|
|
1788
|
+
severity: note.severity,
|
|
1789
|
+
origin: note.origin,
|
|
1790
|
+
shot
|
|
1791
|
+
})
|
|
1792
|
+
});
|
|
1793
|
+
if (!res.ok) {
|
|
1794
|
+
recordFault("collector", `server answered ${res.status}`);
|
|
1795
|
+
return false;
|
|
1796
|
+
}
|
|
1797
|
+
return true;
|
|
1798
|
+
} catch (err) {
|
|
1799
|
+
recordFault("collector", err);
|
|
1800
|
+
return false;
|
|
1801
|
+
} finally {
|
|
1802
|
+
clearTimeout(timer);
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1702
1806
|
// src/lib/strings.ts
|
|
1703
1807
|
var STR = {
|
|
1704
1808
|
en: {
|
|
@@ -2874,38 +2978,6 @@ function neutralizeDocumentColors(doc, aggressive = false) {
|
|
|
2874
2978
|
return touched;
|
|
2875
2979
|
}
|
|
2876
2980
|
|
|
2877
|
-
// src/lib/faultLog.ts
|
|
2878
|
-
var LIMIT = 40;
|
|
2879
|
-
var faults = [];
|
|
2880
|
-
function recordFault(where, err) {
|
|
2881
|
-
const what = err instanceof Error ? `${err.name}: ${err.message}` : typeof err === "string" ? err : (() => {
|
|
2882
|
-
try {
|
|
2883
|
-
return JSON.stringify(err);
|
|
2884
|
-
} catch {
|
|
2885
|
-
return String(err);
|
|
2886
|
-
}
|
|
2887
|
-
})();
|
|
2888
|
-
faults.push({ at: Date.now(), where, what: what.slice(0, 500) });
|
|
2889
|
-
if (faults.length > LIMIT) faults.splice(0, faults.length - LIMIT);
|
|
2890
|
-
console.warn(`[QA] ${where}:`, err);
|
|
2891
|
-
}
|
|
2892
|
-
function readFaults() {
|
|
2893
|
-
return [...faults].reverse();
|
|
2894
|
-
}
|
|
2895
|
-
function clearFaults() {
|
|
2896
|
-
faults.length = 0;
|
|
2897
|
-
}
|
|
2898
|
-
function faultsAsText(version) {
|
|
2899
|
-
if (!faults.length) return "No faults recorded.";
|
|
2900
|
-
const head = [
|
|
2901
|
-
`qapture ${version}`,
|
|
2902
|
-
typeof navigator !== "undefined" ? navigator.userAgent : "",
|
|
2903
|
-
typeof location !== "undefined" ? location.href.split("?")[0] : "",
|
|
2904
|
-
""
|
|
2905
|
-
].filter(Boolean).join("\n");
|
|
2906
|
-
return head + readFaults().map((f) => `${new Date(f.at).toISOString()} [${f.where}] ${f.what}`).join("\n");
|
|
2907
|
-
}
|
|
2908
|
-
|
|
2909
2981
|
// src/lib/capture.ts
|
|
2910
2982
|
var HTML2CANVAS_TIMEOUT_MS = 1e4;
|
|
2911
2983
|
var CHUNK_TIMEOUT_MS = 8e3;
|
|
@@ -3348,7 +3420,7 @@ function noteCheckLine(note, index) {
|
|
|
3348
3420
|
const where = oneLine(note.route) || "/";
|
|
3349
3421
|
const wanted = oneLine(note.wanted);
|
|
3350
3422
|
const seen = oneLine(note.description) || "(not described)";
|
|
3351
|
-
const claim = wanted ||
|
|
3423
|
+
const claim = wanted || seen;
|
|
3352
3424
|
const trimmed = claim.length > 180 ? `${claim.slice(0, 177)}...` : claim;
|
|
3353
3425
|
return `- [ ] **check-${index}** (\`${where}\`) \u2014 ${trimmed}`;
|
|
3354
3426
|
}
|
|
@@ -3398,12 +3470,12 @@ function noteToMarkdown(note, opts) {
|
|
|
3398
3470
|
lines.push("### Observed");
|
|
3399
3471
|
lines.push("");
|
|
3400
3472
|
lines.push(oneLine(note.description) ? note.description.trim() : "_(not described)_");
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3473
|
+
if (note.wanted && oneLine(note.wanted)) {
|
|
3474
|
+
lines.push("");
|
|
3475
|
+
lines.push("### Expected");
|
|
3476
|
+
lines.push("");
|
|
3477
|
+
lines.push(note.wanted.trim());
|
|
3478
|
+
}
|
|
3407
3479
|
if (note.why && oneLine(note.why)) {
|
|
3408
3480
|
lines.push("");
|
|
3409
3481
|
lines.push("### Why it matters");
|
|
@@ -3693,7 +3765,8 @@ Hand \`verify.md\` back with the work.
|
|
|
3693
3765
|
|
|
3694
3766
|
### What is in this archive
|
|
3695
3767
|
|
|
3696
|
-
- \`notes.md\` \u2014 the points themselves
|
|
3768
|
+
- \`notes.md\` \u2014 the points themselves, under an **Observed** heading. Most carry only that: the tester writes one sentence about what is wrong, which is the right amount to ask of somebody who is not an engineer. An **Expected** heading appears where somebody stated one.
|
|
3769
|
+
Where a point is ambiguous, **ask \u2014 do not pick a reading and commit to it.** That failure mode is specific to you: a person would stop and check, and an agent tends to fill the gap in and carry on. One question costs a message. A confident fix to the wrong problem costs the round.
|
|
3697
3770
|
- \`verify.md\` \u2014 the checklist, one unticked box per point.
|
|
3698
3771
|
- \`screenshots/\` \u2014 one per point. A point marked with a screenshot caveat was re-drawn rather than photographed, so canvases, charts and maps may be blank in it; trust the words over the picture there.
|
|
3699
3772
|
- \`context/\` \u2014 console, network, environment and element forensics, one file per point. Deliberately **not** in \`notes.md\`: a longer report measurably lowers the chance of the right thing getting fixed, because the two sentences that matter get buried. Open these only when something is genuinely unresolved.
|
|
@@ -4518,18 +4591,6 @@ async function requestPersistentStorage() {
|
|
|
4518
4591
|
return false;
|
|
4519
4592
|
}
|
|
4520
4593
|
}
|
|
4521
|
-
function formatBytes(bytes) {
|
|
4522
|
-
if (!Number.isFinite(bytes) || bytes <= 0) return "0 KB";
|
|
4523
|
-
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
4524
|
-
let value = bytes;
|
|
4525
|
-
let i = 0;
|
|
4526
|
-
while (value >= 1024 && i < units.length - 1) {
|
|
4527
|
-
value /= 1024;
|
|
4528
|
-
i++;
|
|
4529
|
-
}
|
|
4530
|
-
const decimals = value < 10 && i > 1 ? 1 : 0;
|
|
4531
|
-
return `${value.toFixed(decimals)} ${units[i]}`;
|
|
4532
|
-
}
|
|
4533
4594
|
function estimateOwnBytes(notes) {
|
|
4534
4595
|
let total = 0;
|
|
4535
4596
|
for (const n of notes) {
|
|
@@ -5031,8 +5092,11 @@ function QaProvider({
|
|
|
5031
5092
|
});
|
|
5032
5093
|
}
|
|
5033
5094
|
await syncNoteThrough(note);
|
|
5095
|
+
if (config.collector) {
|
|
5096
|
+
void sendToCollector(note, config.collector);
|
|
5097
|
+
}
|
|
5034
5098
|
},
|
|
5035
|
-
[idb, config.journey, config.captureContext, testAlong, testAlongSteps, notify, t, syncNoteThrough, applyNotes]
|
|
5099
|
+
[idb, config.journey, config.captureContext, config.collector, testAlong, testAlongSteps, notify, t, syncNoteThrough, applyNotes]
|
|
5036
5100
|
);
|
|
5037
5101
|
const updateNote = useCallback(
|
|
5038
5102
|
async (id, patch) => {
|
|
@@ -8143,7 +8207,7 @@ function upgradeHint(latest) {
|
|
|
8143
8207
|
}
|
|
8144
8208
|
|
|
8145
8209
|
// src/version.ts
|
|
8146
|
-
var QA_VERSION = "0.9.
|
|
8210
|
+
var QA_VERSION = "0.9.1" ;
|
|
8147
8211
|
function Section({
|
|
8148
8212
|
icon,
|
|
8149
8213
|
title,
|
|
@@ -8249,8 +8313,8 @@ function SettingsSheet({ onClose }) {
|
|
|
8249
8313
|
const syncing = sync.state === "syncing";
|
|
8250
8314
|
const viaZip = sync.engine === "download";
|
|
8251
8315
|
const quotaKnown = storageHealth.supported && storageHealth.quotaBytes > 0;
|
|
8252
|
-
|
|
8253
|
-
|
|
8316
|
+
quotaKnown ? Math.min(100, Math.round(storageHealth.ratio * 100)) : 0;
|
|
8317
|
+
storageHealth.level === "critical" ? "var(--qa-danger)" : storageHealth.level === "warn" ? "var(--qa-warn)" : "var(--qa-accent)";
|
|
8254
8318
|
return /* @__PURE__ */ jsxs(
|
|
8255
8319
|
"div",
|
|
8256
8320
|
{
|
|
@@ -8379,77 +8443,6 @@ function SettingsSheet({ onClose }) {
|
|
|
8379
8443
|
)
|
|
8380
8444
|
] }),
|
|
8381
8445
|
/* @__PURE__ */ jsx("div", { className: "qa-h-px qa-bg-3 qa-mt-3 qa-mb-4" }),
|
|
8382
|
-
/* @__PURE__ */ jsxs(Section, { icon: "HardDrive", title: t("storage_title"), children: [
|
|
8383
|
-
/* @__PURE__ */ jsx("p", { className: "qa-m-0 qa-text-10 qa-text-lo qa-leading-relaxed", children: t("storage_explain") }),
|
|
8384
|
-
quotaKnown && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
8385
|
-
/* @__PURE__ */ jsx(
|
|
8386
|
-
"div",
|
|
8387
|
-
{
|
|
8388
|
-
className: "qa-w-full qa-rounded-full qa-overflow-hidden qa-bg-3",
|
|
8389
|
-
role: "img",
|
|
8390
|
-
"aria-label": t("storage_used", {
|
|
8391
|
-
used: formatBytes(storageHealth.usageBytes),
|
|
8392
|
-
quota: formatBytes(storageHealth.quotaBytes)
|
|
8393
|
-
}),
|
|
8394
|
-
style: { height: 6 },
|
|
8395
|
-
children: /* @__PURE__ */ jsx("div", { style: { width: `${usedPct}%`, height: "100%", background: meterColor } })
|
|
8396
|
-
}
|
|
8397
|
-
),
|
|
8398
|
-
/* @__PURE__ */ jsxs("p", { className: "qa-m-0 qa-text-10 qa-text-mid", children: [
|
|
8399
|
-
t("storage_used", {
|
|
8400
|
-
used: formatBytes(storageHealth.usageBytes),
|
|
8401
|
-
quota: formatBytes(storageHealth.quotaBytes)
|
|
8402
|
-
}),
|
|
8403
|
-
" \xB7 ",
|
|
8404
|
-
/* @__PURE__ */ jsxs("span", { className: "qa-text-lo", children: [
|
|
8405
|
-
"Qapture ",
|
|
8406
|
-
formatBytes(storageHealth.ownBytes)
|
|
8407
|
-
] })
|
|
8408
|
-
] })
|
|
8409
|
-
] }),
|
|
8410
|
-
/* @__PURE__ */ jsxs("label", { className: "qa-flex qa-items-start qa-gap-2 qa-text-xs qa-text-hi", style: { cursor: "pointer" }, children: [
|
|
8411
|
-
/* @__PURE__ */ jsx(
|
|
8412
|
-
"input",
|
|
8413
|
-
{
|
|
8414
|
-
type: "checkbox",
|
|
8415
|
-
checked: autoBackup,
|
|
8416
|
-
onChange: (e) => setAutoBackup(e.target.checked),
|
|
8417
|
-
style: { marginTop: 2 }
|
|
8418
|
-
}
|
|
8419
|
-
),
|
|
8420
|
-
/* @__PURE__ */ jsxs("span", { children: [
|
|
8421
|
-
t("autosave_label"),
|
|
8422
|
-
/* @__PURE__ */ jsx("span", { className: "qa-block qa-text-10 qa-text-lo qa-leading-relaxed", children: t("autosave_hint", { n: autoBackupEvery }) })
|
|
8423
|
-
] })
|
|
8424
|
-
] }),
|
|
8425
|
-
/* @__PURE__ */ jsxs("div", { className: "qa-flex qa-flex-wrap qa-gap-2", children: [
|
|
8426
|
-
storageHealth.persisted ? /* @__PURE__ */ jsxs("span", { className: "qa-inline-flex qa-items-center qa-gap-1 qa-rounded-full qa-bg-success-tint qa-text-success qa-px-2 qa-py-0.5 qa-text-10", children: [
|
|
8427
|
-
/* @__PURE__ */ jsx(Icon, { name: "CheckCircle2", size: 11 }),
|
|
8428
|
-
t("persist_on")
|
|
8429
|
-
] }) : /* @__PURE__ */ jsx(
|
|
8430
|
-
"button",
|
|
8431
|
-
{
|
|
8432
|
-
type: "button",
|
|
8433
|
-
disabled: busy,
|
|
8434
|
-
onClick: () => void run2(requestPersistentStorage2),
|
|
8435
|
-
className: "qa-tap qa-rounded-lg qa-border qa-border-subtle qa-px-2 qa-py-1 qa-text-10 qa-text-mid",
|
|
8436
|
-
style: { background: "transparent", cursor: "pointer" },
|
|
8437
|
-
children: t("persist_keep")
|
|
8438
|
-
}
|
|
8439
|
-
),
|
|
8440
|
-
notes.some((n) => n.screenshot) && /* @__PURE__ */ jsx(
|
|
8441
|
-
"button",
|
|
8442
|
-
{
|
|
8443
|
-
type: "button",
|
|
8444
|
-
disabled: busy,
|
|
8445
|
-
onClick: () => void run2(dropAllScreenshots),
|
|
8446
|
-
className: "qa-tap qa-rounded-lg qa-border qa-border-subtle qa-px-2 qa-py-1 qa-text-10 qa-text-mid",
|
|
8447
|
-
style: { background: "transparent", cursor: "pointer" },
|
|
8448
|
-
children: t("drop_shots")
|
|
8449
|
-
}
|
|
8450
|
-
)
|
|
8451
|
-
] })
|
|
8452
|
-
] }),
|
|
8453
8446
|
/* @__PURE__ */ jsx("div", { className: "qa-h-px qa-bg-3 qa-mt-3 qa-mb-4" }),
|
|
8454
8447
|
/* @__PURE__ */ jsxs(Section, { icon: "Camera", title: t("exact_label"), children: [
|
|
8455
8448
|
/* @__PURE__ */ jsx("p", { className: "qa-m-0 qa-text-10 qa-text-lo qa-leading-relaxed", children: t("exact_hint") }),
|
|
@@ -10014,98 +10007,6 @@ function findDuplicate(text, selector, existing) {
|
|
|
10014
10007
|
}
|
|
10015
10008
|
return null;
|
|
10016
10009
|
}
|
|
10017
|
-
function recogniser() {
|
|
10018
|
-
if (typeof window === "undefined") return null;
|
|
10019
|
-
const w = window;
|
|
10020
|
-
return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;
|
|
10021
|
-
}
|
|
10022
|
-
function VoiceButton({
|
|
10023
|
-
onText,
|
|
10024
|
-
onInterim
|
|
10025
|
-
}) {
|
|
10026
|
-
const { lang, t } = useQa();
|
|
10027
|
-
const [listening, setListening] = useState(false);
|
|
10028
|
-
const [failed, setFailed] = useState(null);
|
|
10029
|
-
const ref = useRef(null);
|
|
10030
|
-
useEffect(() => () => {
|
|
10031
|
-
ref.current?.abort();
|
|
10032
|
-
ref.current = null;
|
|
10033
|
-
}, []);
|
|
10034
|
-
const Ctor = recogniser();
|
|
10035
|
-
if (!Ctor) return null;
|
|
10036
|
-
const stop = () => {
|
|
10037
|
-
ref.current?.stop();
|
|
10038
|
-
ref.current = null;
|
|
10039
|
-
setListening(false);
|
|
10040
|
-
onInterim?.("");
|
|
10041
|
-
};
|
|
10042
|
-
const start = () => {
|
|
10043
|
-
if (listening) {
|
|
10044
|
-
stop();
|
|
10045
|
-
return;
|
|
10046
|
-
}
|
|
10047
|
-
setFailed(null);
|
|
10048
|
-
let rec;
|
|
10049
|
-
try {
|
|
10050
|
-
rec = new Ctor();
|
|
10051
|
-
} catch {
|
|
10052
|
-
setFailed(t("voice_failed"));
|
|
10053
|
-
return;
|
|
10054
|
-
}
|
|
10055
|
-
rec.lang = lang === "ar" ? "ar-IQ" : "en-US";
|
|
10056
|
-
rec.continuous = true;
|
|
10057
|
-
rec.interimResults = true;
|
|
10058
|
-
rec.onresult = (e) => {
|
|
10059
|
-
let done = "";
|
|
10060
|
-
let partial = "";
|
|
10061
|
-
for (let i = e.resultIndex; i < e.results.length; i++) {
|
|
10062
|
-
const r = e.results[i];
|
|
10063
|
-
const text = r[0]?.transcript ?? "";
|
|
10064
|
-
if (r.isFinal) done += text;
|
|
10065
|
-
else partial += text;
|
|
10066
|
-
}
|
|
10067
|
-
if (done.trim()) onText(done.trim());
|
|
10068
|
-
onInterim?.(partial);
|
|
10069
|
-
};
|
|
10070
|
-
rec.onerror = (e) => {
|
|
10071
|
-
if (e.error && e.error !== "no-speech" && e.error !== "aborted") {
|
|
10072
|
-
setFailed(e.error === "not-allowed" ? t("voice_denied") : t("voice_failed"));
|
|
10073
|
-
}
|
|
10074
|
-
setListening(false);
|
|
10075
|
-
onInterim?.("");
|
|
10076
|
-
};
|
|
10077
|
-
rec.onend = () => {
|
|
10078
|
-
setListening(false);
|
|
10079
|
-
onInterim?.("");
|
|
10080
|
-
};
|
|
10081
|
-
try {
|
|
10082
|
-
rec.start();
|
|
10083
|
-
ref.current = rec;
|
|
10084
|
-
setListening(true);
|
|
10085
|
-
} catch {
|
|
10086
|
-
setFailed(t("voice_failed"));
|
|
10087
|
-
}
|
|
10088
|
-
};
|
|
10089
|
-
return /* @__PURE__ */ jsxs("span", { className: "qa-inline-flex qa-items-center qa-gap-1.5", children: [
|
|
10090
|
-
/* @__PURE__ */ jsxs(
|
|
10091
|
-
"button",
|
|
10092
|
-
{
|
|
10093
|
-
type: "button",
|
|
10094
|
-
onClick: start,
|
|
10095
|
-
"aria-pressed": listening,
|
|
10096
|
-
title: listening ? t("voice_stop") : t("voice_start"),
|
|
10097
|
-
"data-qa-voice": listening ? "listening" : "idle",
|
|
10098
|
-
className: `qa-tap qa-inline-flex qa-items-center qa-gap-1 qa-rounded-full qa-border qa-border-subtle qa-px-2 qa-py-1 qa-text-11 qa-focus-ring ${listening ? "qa-bg-danger-tint qa-text-danger" : "qa-bg-2 qa-text-mid"}`,
|
|
10099
|
-
style: { cursor: "pointer" },
|
|
10100
|
-
children: [
|
|
10101
|
-
/* @__PURE__ */ jsx(Icon, { name: listening ? "Square" : "Mic", size: 12 }),
|
|
10102
|
-
listening ? t("voice_stop") : t("voice_start")
|
|
10103
|
-
]
|
|
10104
|
-
}
|
|
10105
|
-
),
|
|
10106
|
-
failed && /* @__PURE__ */ jsx("span", { className: "qa-text-10 qa-text-danger", children: failed })
|
|
10107
|
-
] });
|
|
10108
|
-
}
|
|
10109
10010
|
var DRAG_THRESHOLD2 = 6;
|
|
10110
10011
|
var TOUCH_DRAG_THRESHOLD = 12;
|
|
10111
10012
|
var MIN_REGION_SIZE = 8;
|
|
@@ -10177,11 +10078,7 @@ function CaptureMode() {
|
|
|
10177
10078
|
const [shot, setShot] = useState(null);
|
|
10178
10079
|
const stillRef = useRef(null);
|
|
10179
10080
|
const [notesFromThisShot, setNotesFromThisShot] = useState(0);
|
|
10180
|
-
const [wanted, setWanted] = useState("");
|
|
10181
|
-
const [why, setWhy] = useState("");
|
|
10182
|
-
const [fixHint, setFixHint] = useState("");
|
|
10183
10081
|
const [origin, setOrigin] = useState(void 0);
|
|
10184
|
-
const [spoken, setSpoken] = useState("");
|
|
10185
10082
|
const [shotEngine, setShotEngine] = useState(null);
|
|
10186
10083
|
const [shotUrl, setShotUrl] = useState(null);
|
|
10187
10084
|
const [capturing, setCapturing] = useState(false);
|
|
@@ -10501,9 +10398,6 @@ function CaptureMode() {
|
|
|
10501
10398
|
setShot(null);
|
|
10502
10399
|
setShotEngine(null);
|
|
10503
10400
|
setDescription("");
|
|
10504
|
-
setWanted("");
|
|
10505
|
-
setWhy("");
|
|
10506
|
-
setFixHint("");
|
|
10507
10401
|
setSeverity("bug");
|
|
10508
10402
|
setTargetForensics(void 0);
|
|
10509
10403
|
setOrigin(void 0);
|
|
@@ -10526,9 +10420,6 @@ function CaptureMode() {
|
|
|
10526
10420
|
};
|
|
10527
10421
|
await addNote({
|
|
10528
10422
|
description,
|
|
10529
|
-
wanted,
|
|
10530
|
-
why,
|
|
10531
|
-
fixHint,
|
|
10532
10423
|
screenshot: shot ?? void 0,
|
|
10533
10424
|
shotEngine: shotEngine ?? void 0,
|
|
10534
10425
|
target,
|
|
@@ -11201,87 +11092,24 @@ function CaptureMode() {
|
|
|
11201
11092
|
twin.description.length > 70 ? "\u2026" : "",
|
|
11202
11093
|
"\u201D"
|
|
11203
11094
|
] }),
|
|
11204
|
-
/* @__PURE__ */
|
|
11205
|
-
|
|
11206
|
-
|
|
11207
|
-
|
|
11208
|
-
|
|
11209
|
-
|
|
11210
|
-
|
|
11211
|
-
|
|
11095
|
+
/* @__PURE__ */ jsx(
|
|
11096
|
+
"textarea",
|
|
11097
|
+
{
|
|
11098
|
+
ref: taRef,
|
|
11099
|
+
value: description,
|
|
11100
|
+
onChange: (e) => setDescription(e.target.value),
|
|
11101
|
+
onKeyDown: (e) => {
|
|
11102
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) void save(e.shiftKey);
|
|
11103
|
+
if ((e.altKey || e.metaKey || e.ctrlKey) && SEVERITIES2[Number(e.key) - 1]) {
|
|
11104
|
+
e.preventDefault();
|
|
11105
|
+
setSeverity(SEVERITIES2[Number(e.key) - 1]);
|
|
11212
11106
|
}
|
|
11213
|
-
|
|
11214
|
-
|
|
11215
|
-
|
|
11216
|
-
"
|
|
11217
|
-
|
|
11218
|
-
|
|
11219
|
-
value: description + (spoken ? ` ${spoken}` : ""),
|
|
11220
|
-
onChange: (e) => setDescription(e.target.value),
|
|
11221
|
-
onKeyDown: (e) => {
|
|
11222
|
-
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) void save(e.shiftKey);
|
|
11223
|
-
if ((e.altKey || e.metaKey || e.ctrlKey) && SEVERITIES2[Number(e.key) - 1]) {
|
|
11224
|
-
e.preventDefault();
|
|
11225
|
-
setSeverity(SEVERITIES2[Number(e.key) - 1]);
|
|
11226
|
-
}
|
|
11227
|
-
},
|
|
11228
|
-
rows: 2,
|
|
11229
|
-
placeholder: t("q_observed_hint"),
|
|
11230
|
-
className: "qa-w-full qa-resize-y qa-rounded-lg qa-border qa-border-subtle qa-bg-0 qa-text-hi qa-px-2 qa-py-1.5 qa-text-sm qa-focus-ring"
|
|
11231
|
-
}
|
|
11232
|
-
)
|
|
11233
|
-
] }),
|
|
11234
|
-
/* @__PURE__ */ jsxs("label", { className: "qa-flex qa-flex-col qa-gap-1", children: [
|
|
11235
|
-
/* @__PURE__ */ jsxs("span", { className: "qa-flex qa-items-center qa-justify-between qa-gap-2", children: [
|
|
11236
|
-
/* @__PURE__ */ jsx("span", { className: "qa-text-11 qa-font-semibold qa-text-hi", children: t("q_wanted") }),
|
|
11237
|
-
/* @__PURE__ */ jsx(VoiceButton, { onText: (x) => setWanted((d) => d ? `${d} ${x}` : x) })
|
|
11238
|
-
] }),
|
|
11239
|
-
/* @__PURE__ */ jsx(
|
|
11240
|
-
"textarea",
|
|
11241
|
-
{
|
|
11242
|
-
value: wanted,
|
|
11243
|
-
onChange: (e) => setWanted(e.target.value),
|
|
11244
|
-
onKeyDown: (e) => {
|
|
11245
|
-
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) void save(e.shiftKey);
|
|
11246
|
-
},
|
|
11247
|
-
rows: 2,
|
|
11248
|
-
placeholder: t("q_wanted_hint"),
|
|
11249
|
-
className: "qa-w-full qa-resize-y qa-rounded-lg qa-border qa-border-subtle qa-bg-0 qa-text-hi qa-px-2 qa-py-1.5 qa-text-sm qa-focus-ring"
|
|
11250
|
-
}
|
|
11251
|
-
)
|
|
11252
|
-
] }),
|
|
11253
|
-
/* @__PURE__ */ jsxs("label", { className: "qa-flex qa-flex-col qa-gap-1", children: [
|
|
11254
|
-
/* @__PURE__ */ jsxs("span", { className: "qa-flex qa-items-center qa-justify-between qa-gap-2", children: [
|
|
11255
|
-
/* @__PURE__ */ jsx("span", { className: "qa-text-11 qa-text-mid", children: t("q_why") }),
|
|
11256
|
-
/* @__PURE__ */ jsx(VoiceButton, { onText: (x) => setWhy((d) => d ? `${d} ${x}` : x) })
|
|
11257
|
-
] }),
|
|
11258
|
-
/* @__PURE__ */ jsx(
|
|
11259
|
-
"textarea",
|
|
11260
|
-
{
|
|
11261
|
-
value: why,
|
|
11262
|
-
onChange: (e) => setWhy(e.target.value),
|
|
11263
|
-
onKeyDown: (e) => {
|
|
11264
|
-
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) void save(e.shiftKey);
|
|
11265
|
-
},
|
|
11266
|
-
rows: 1,
|
|
11267
|
-
placeholder: t("q_why_hint"),
|
|
11268
|
-
className: "qa-w-full qa-resize-y qa-rounded-lg qa-border qa-border-subtle qa-bg-0 qa-text-hi qa-px-2 qa-py-1.5 qa-text-sm qa-focus-ring"
|
|
11269
|
-
}
|
|
11270
|
-
)
|
|
11271
|
-
] }),
|
|
11272
|
-
developerMode && /* @__PURE__ */ jsxs("label", { className: "qa-flex qa-flex-col qa-gap-1", children: [
|
|
11273
|
-
/* @__PURE__ */ jsx("span", { className: "qa-text-11 qa-text-mid", children: t("q_fix") }),
|
|
11274
|
-
/* @__PURE__ */ jsx(
|
|
11275
|
-
"textarea",
|
|
11276
|
-
{
|
|
11277
|
-
value: fixHint,
|
|
11278
|
-
onChange: (e) => setFixHint(e.target.value),
|
|
11279
|
-
rows: 1,
|
|
11280
|
-
placeholder: t("q_fix_hint"),
|
|
11281
|
-
className: "qa-w-full qa-resize-y qa-rounded-lg qa-border qa-border-subtle qa-bg-0 qa-text-hi qa-px-2 qa-py-1.5 qa-text-sm qa-focus-ring"
|
|
11282
|
-
}
|
|
11283
|
-
)
|
|
11284
|
-
] }),
|
|
11107
|
+
},
|
|
11108
|
+
rows: 3,
|
|
11109
|
+
placeholder: t("annotate_placeholder"),
|
|
11110
|
+
className: "qa-w-full qa-resize-y qa-rounded-lg qa-border qa-border-subtle qa-bg-0 qa-text-hi qa-px-2 qa-py-1.5 qa-text-sm qa-focus-ring"
|
|
11111
|
+
}
|
|
11112
|
+
),
|
|
11285
11113
|
developerMode && origin && (origin.component || origin.file) && /* @__PURE__ */ jsxs("p", { className: "qa-text-10 qa-text-mid", "data-qa-origin": "true", children: [
|
|
11286
11114
|
origin.component ?? "\u2014",
|
|
11287
11115
|
origin.file ? ` \xB7 ${origin.file}${origin.line ? `:${origin.line}` : ""}` : ""
|
|
@@ -11533,5 +11361,5 @@ function Qapture({ config }) {
|
|
|
11533
11361
|
}
|
|
11534
11362
|
|
|
11535
11363
|
export { Qapture, deleteQaDatabase, initQaStudio };
|
|
11536
|
-
//# sourceMappingURL=chunk-
|
|
11537
|
-
//# sourceMappingURL=chunk-
|
|
11364
|
+
//# sourceMappingURL=chunk-HEHVZCSV.js.map
|
|
11365
|
+
//# sourceMappingURL=chunk-HEHVZCSV.js.map
|