qapture2 0.7.6 → 0.7.8
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-NM3IDVEL.js → chunk-2JX7YU2P.js} +321 -347
- package/dist/{chunk-CNNNWLTX.cjs → chunk-4LMLW73U.cjs} +321 -347
- 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 +1 -1
|
@@ -1755,10 +1755,10 @@ var STR = {
|
|
|
1755
1755
|
copied: "Copied",
|
|
1756
1756
|
copy_failed: "Copy failed",
|
|
1757
1757
|
copy_prompt: "Copy as agent prompt",
|
|
1758
|
-
severity_label: "
|
|
1758
|
+
severity_label: "What is this?",
|
|
1759
1759
|
sev_bug: "Bug",
|
|
1760
|
-
|
|
1761
|
-
|
|
1760
|
+
sev_design: "Design",
|
|
1761
|
+
sev_enhance: "Enhance",
|
|
1762
1762
|
status_open: "Open",
|
|
1763
1763
|
status_verified: "Verified",
|
|
1764
1764
|
context_attached: "{n} runtime events attached",
|
|
@@ -1862,6 +1862,13 @@ var STR = {
|
|
|
1862
1862
|
retest_not_found: "Can't find that spot on this page \u2014 open the right page first",
|
|
1863
1863
|
before_label: "Before",
|
|
1864
1864
|
after_label: "After",
|
|
1865
|
+
followup_label: "What happened this time",
|
|
1866
|
+
followup_add: "Say what happened",
|
|
1867
|
+
followup_placeholder: "What did it do this time, and how is that different from what you asked for?",
|
|
1868
|
+
followup_hint: "The first note stays as it is. This is added under it as round two.",
|
|
1869
|
+
keep_only_retest: "Keep only these {n}",
|
|
1870
|
+
keep_only_retest_q: "Delete the other {n}?",
|
|
1871
|
+
keep_only_retest_hint: "Delete every note that is not waiting to be re-tested",
|
|
1865
1872
|
welcome_title: "Testing {brand}",
|
|
1866
1873
|
welcome_capture: 'Click "Capture from page", then click whatever looks wrong.',
|
|
1867
1874
|
welcome_say: "Say what you expected in your own words. A screenshot is taken for you.",
|
|
@@ -1953,10 +1960,10 @@ var STR = {
|
|
|
1953
1960
|
copied: "\u062A\u0645 \u0627\u0644\u0646\u0633\u062E",
|
|
1954
1961
|
copy_failed: "\u0641\u0634\u0644 \u0627\u0644\u0646\u0633\u062E",
|
|
1955
1962
|
copy_prompt: "\u0646\u0633\u062E \u0643\u0645\u0648\u062C\u0651\u0647 \u0644\u0644\u0648\u0643\u064A\u0644",
|
|
1956
|
-
severity_label: "\u0627\
|
|
1957
|
-
sev_bug: "
|
|
1958
|
-
|
|
1959
|
-
|
|
1963
|
+
severity_label: "\u0645\u0627 \u0647\u0630\u0627\u061F",
|
|
1964
|
+
sev_bug: "Bug",
|
|
1965
|
+
sev_design: "Design",
|
|
1966
|
+
sev_enhance: "Enhance",
|
|
1960
1967
|
status_open: "\u0645\u0641\u062A\u0648\u062D",
|
|
1961
1968
|
status_verified: "\u062A\u0645 \u0627\u0644\u062A\u062D\u0642\u0642",
|
|
1962
1969
|
context_attached: "{n} \u0645\u0646 \u0623\u062D\u062F\u0627\u062B \u0627\u0644\u062A\u0634\u063A\u064A\u0644 \u0645\u0631\u0641\u0642\u0629",
|
|
@@ -2060,6 +2067,13 @@ var STR = {
|
|
|
2060
2067
|
retest_not_found: "\u062A\u0639\u0630\u0651\u0631 \u0625\u064A\u062C\u0627\u062F \u0630\u0644\u0643 \u0627\u0644\u0645\u0648\u0636\u0639 \u0641\u064A \u0647\u0630\u0647 \u0627\u0644\u0635\u0641\u062D\u0629 \u2014 \u0627\u0641\u062A\u062D \u0627\u0644\u0635\u0641\u062D\u0629 \u0627\u0644\u0635\u062D\u064A\u062D\u0629 \u0623\u0648\u0644\u064B\u0627",
|
|
2061
2068
|
before_label: "\u0642\u0628\u0644",
|
|
2062
2069
|
after_label: "\u0628\u0639\u062F",
|
|
2070
|
+
followup_label: "\u0645\u0627\u0630\u0627 \u062D\u062F\u062B \u0647\u0630\u0647 \u0627\u0644\u0645\u0631\u0629",
|
|
2071
|
+
followup_add: "\u0627\u0643\u062A\u0628 \u0645\u0627\u0630\u0627 \u062D\u062F\u062B",
|
|
2072
|
+
followup_placeholder: "\u0645\u0627\u0630\u0627 \u0641\u0639\u0644 \u0647\u0630\u0647 \u0627\u0644\u0645\u0631\u0629\u060C \u0648\u0643\u064A\u0641 \u064A\u062E\u062A\u0644\u0641 \u0630\u0644\u0643 \u0639\u0645\u0651\u0627 \u0637\u0644\u0628\u062A\u0647\u061F",
|
|
2073
|
+
followup_hint: "\u0627\u0644\u0645\u0644\u0627\u062D\u0638\u0629 \u0627\u0644\u0623\u0648\u0644\u0649 \u062A\u0628\u0642\u0649 \u0643\u0645\u0627 \u0647\u064A. \u0647\u0630\u0647 \u062A\u064F\u0636\u0627\u0641 \u062A\u062D\u062A\u0647\u0627 \u0643\u062C\u0648\u0644\u0629 \u062B\u0627\u0646\u064A\u0629.",
|
|
2074
|
+
keep_only_retest: "\u0623\u0628\u0642\u0650 \u0647\u0630\u0647 \u0627\u0644\u0640 {n} \u0641\u0642\u0637",
|
|
2075
|
+
keep_only_retest_q: "\u062D\u0630\u0641 \u0627\u0644\u0640 {n} \u0627\u0644\u0623\u062E\u0631\u0649\u061F",
|
|
2076
|
+
keep_only_retest_hint: "\u062D\u0630\u0641 \u0643\u0644 \u0645\u0644\u0627\u062D\u0638\u0629 \u0644\u064A\u0633\u062A \u0628\u0627\u0646\u062A\u0638\u0627\u0631 \u0625\u0639\u0627\u062F\u0629 \u0627\u0644\u0627\u062E\u062A\u0628\u0627\u0631",
|
|
2063
2077
|
welcome_title: "\u0627\u062E\u062A\u0628\u0627\u0631 {brand}",
|
|
2064
2078
|
welcome_capture: "\u0627\u0636\u063A\u0637 \xAB\u0627\u0644\u062A\u0642\u0627\u0637 \u0645\u0646 \u0627\u0644\u0635\u0641\u062D\u0629\xBB \u062B\u0645 \u0627\u0636\u063A\u0637 \u0639\u0644\u0649 \u0645\u0627 \u064A\u0628\u062F\u0648 \u062E\u0627\u0637\u0626\u064B\u0627.",
|
|
2065
2079
|
welcome_say: "\u0627\u0643\u062A\u0628 \u0645\u0627 \u0643\u0646\u062A \u062A\u062A\u0648\u0642\u0639\u0647 \u0628\u0643\u0644\u0645\u0627\u062A\u0643. \u062A\u064F\u0644\u062A\u0642\u0637 \u0627\u0644\u0635\u0648\u0631\u0629 \u062A\u0644\u0642\u0627\u0626\u064A\u064B\u0627.",
|
|
@@ -2104,18 +2118,6 @@ function pick(value, lang) {
|
|
|
2104
2118
|
if (typeof value === "string") return value;
|
|
2105
2119
|
return value[lang] ?? value.en ?? "";
|
|
2106
2120
|
}
|
|
2107
|
-
|
|
2108
|
-
// src/lib/coverage.ts
|
|
2109
|
-
var RISK_COLORS = {
|
|
2110
|
-
red: "#FF6B6B",
|
|
2111
|
-
// --qa-danger
|
|
2112
|
-
amber: "#FBBF24",
|
|
2113
|
-
// --qa-warn
|
|
2114
|
-
green: "#34D399",
|
|
2115
|
-
// --qa-success
|
|
2116
|
-
none: "#5B616B"
|
|
2117
|
-
// --qa-neutral
|
|
2118
|
-
};
|
|
2119
2121
|
function computeCoverage(journey, guideChecked, skipped) {
|
|
2120
2122
|
const red = { total: 0, covered: 0 };
|
|
2121
2123
|
const amber = { total: 0, covered: 0 };
|
|
@@ -2969,16 +2971,19 @@ function clipToPaintedArea(el, rect) {
|
|
|
2969
2971
|
let top = rect.top;
|
|
2970
2972
|
let right = rect.left + rect.width;
|
|
2971
2973
|
let bottom = rect.top + rect.height;
|
|
2974
|
+
const doc = el.ownerDocument;
|
|
2975
|
+
const root2 = doc ? doc.documentElement : null;
|
|
2976
|
+
const body = doc ? doc.body : null;
|
|
2977
|
+
const rootStyle = root2 ? safeComputedStyle(root2) : null;
|
|
2978
|
+
const bodyStyle = body ? safeComputedStyle(body) : null;
|
|
2972
2979
|
let node = el.parentElement;
|
|
2973
|
-
while (node) {
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
const clipsX = style.overflowX !== "visible";
|
|
2981
|
-
const clipsY = style.overflowY !== "visible";
|
|
2980
|
+
while (node && node !== root2) {
|
|
2981
|
+
const style = safeComputedStyle(node);
|
|
2982
|
+
if (!style) break;
|
|
2983
|
+
const propagatedX = node === body && rootStyle?.overflowX === "visible";
|
|
2984
|
+
const propagatedY = node === body && rootStyle?.overflowY === "visible";
|
|
2985
|
+
const clipsX = !propagatedX && style.overflowX !== "visible";
|
|
2986
|
+
const clipsY = !propagatedY && style.overflowY !== "visible";
|
|
2982
2987
|
if (clipsX || clipsY) {
|
|
2983
2988
|
const b = node.getBoundingClientRect();
|
|
2984
2989
|
const padLeft = b.left + node.clientLeft;
|
|
@@ -2994,9 +2999,33 @@ function clipToPaintedArea(el, rect) {
|
|
|
2994
2999
|
}
|
|
2995
3000
|
node = node.parentElement;
|
|
2996
3001
|
}
|
|
3002
|
+
const vpX = usedViewportOverflow(rootStyle?.overflowX, bodyStyle?.overflowX);
|
|
3003
|
+
const vpY = usedViewportOverflow(rootStyle?.overflowY, bodyStyle?.overflowY);
|
|
3004
|
+
if (vpX === "hidden" || vpX === "clip" || vpY === "hidden" || vpY === "clip") {
|
|
3005
|
+
const { vw, vh } = viewportSize();
|
|
3006
|
+
if (vpX === "hidden" || vpX === "clip") {
|
|
3007
|
+
left = Math.max(left, 0);
|
|
3008
|
+
right = Math.min(right, vw);
|
|
3009
|
+
}
|
|
3010
|
+
if (vpY === "hidden" || vpY === "clip") {
|
|
3011
|
+
top = Math.max(top, 0);
|
|
3012
|
+
bottom = Math.min(bottom, vh);
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
2997
3015
|
if (right - left < 2 || bottom - top < 2) return rect;
|
|
2998
3016
|
return { left, top, width: right - left, height: bottom - top };
|
|
2999
3017
|
}
|
|
3018
|
+
function safeComputedStyle(node) {
|
|
3019
|
+
try {
|
|
3020
|
+
return getComputedStyle(node);
|
|
3021
|
+
} catch {
|
|
3022
|
+
return null;
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
3025
|
+
function usedViewportOverflow(rootValue, bodyValue) {
|
|
3026
|
+
if (rootValue && rootValue !== "visible") return rootValue;
|
|
3027
|
+
return bodyValue || "visible";
|
|
3028
|
+
}
|
|
3000
3029
|
function intersectViewport(rect) {
|
|
3001
3030
|
const { vw, vh } = viewportSize();
|
|
3002
3031
|
const left = Math.max(0, rect.left);
|
|
@@ -3191,6 +3220,16 @@ function noteToMarkdown(note, opts) {
|
|
|
3191
3220
|
}
|
|
3192
3221
|
lines.push("");
|
|
3193
3222
|
lines.push(oneLine(note.description) ? note.description.trim() : "_(no description)_");
|
|
3223
|
+
if (note.followUp && oneLine(note.followUp)) {
|
|
3224
|
+
lines.push("");
|
|
3225
|
+
lines.push(
|
|
3226
|
+
"> **This one came back \u2014 round 2.** The point above was already worked on once and it is still not right. What follows is the tester re-testing it, so treat it as the current ask and the point above as the history of what was originally wanted."
|
|
3227
|
+
);
|
|
3228
|
+
lines.push("");
|
|
3229
|
+
lines.push(`**What happened this time**${note.followUpAt ? ` (${oneLine(note.followUpAt)})` : ""}`);
|
|
3230
|
+
lines.push("");
|
|
3231
|
+
lines.push(note.followUp.trim());
|
|
3232
|
+
}
|
|
3194
3233
|
const recordedSteps = note.context?.steps ?? [];
|
|
3195
3234
|
if (recordedSteps.length) {
|
|
3196
3235
|
const t0 = Date.parse(note.timestamp) || recordedSteps[recordedSteps.length - 1].t;
|
|
@@ -3277,18 +3316,19 @@ function fmtPct(n, d) {
|
|
|
3277
3316
|
}
|
|
3278
3317
|
function summariseSession(notes, stamp) {
|
|
3279
3318
|
if (!notes.length) return "No points captured.";
|
|
3280
|
-
let bugs = 0,
|
|
3319
|
+
let bugs = 0, enhancements = 0, designs = 0, verified = 0, awaiting = 0, secondRound = 0;
|
|
3281
3320
|
const pages = /* @__PURE__ */ new Set();
|
|
3282
3321
|
let earliest = Number.POSITIVE_INFINITY;
|
|
3283
3322
|
let latest = 0;
|
|
3284
3323
|
for (const n of notes) {
|
|
3285
3324
|
const sev = n.severity ?? "bug";
|
|
3286
3325
|
if (sev === "bug") bugs++;
|
|
3287
|
-
else if (sev === "
|
|
3288
|
-
else
|
|
3326
|
+
else if (sev === "enhance") enhancements++;
|
|
3327
|
+
else designs++;
|
|
3289
3328
|
const status = n.status ?? "open";
|
|
3290
3329
|
if (status === "verified") verified++;
|
|
3291
3330
|
else if (status === "fixed") awaiting++;
|
|
3331
|
+
if (n.followUp && n.followUp.trim()) secondRound++;
|
|
3292
3332
|
pages.add((n.route || "/").split("?")[0]);
|
|
3293
3333
|
const t = Date.parse(n.timestamp);
|
|
3294
3334
|
if (Number.isFinite(t)) {
|
|
@@ -3300,10 +3340,11 @@ function summariseSession(notes, stamp) {
|
|
|
3300
3340
|
`${notes.length} point${notes.length === 1 ? "" : "s"}`,
|
|
3301
3341
|
`${bugs} bug${bugs === 1 ? "" : "s"}`
|
|
3302
3342
|
];
|
|
3303
|
-
if (
|
|
3304
|
-
if (
|
|
3343
|
+
if (enhancements) parts.push(`${enhancements} enhancement${enhancements === 1 ? "" : "s"}`);
|
|
3344
|
+
if (designs) parts.push(`${designs} design`);
|
|
3305
3345
|
if (verified) parts.push(`${verified} verified`);
|
|
3306
3346
|
if (awaiting) parts.push(`${awaiting} awaiting re-test`);
|
|
3347
|
+
if (secondRound) parts.push(`${secondRound} back for a second round`);
|
|
3307
3348
|
parts.push(`${pages.size} page${pages.size === 1 ? "" : "s"}`);
|
|
3308
3349
|
if (Number.isFinite(earliest) && latest > earliest) {
|
|
3309
3350
|
const minutes = Math.max(1, Math.round((latest - earliest) / 6e4));
|
|
@@ -3332,6 +3373,45 @@ Exported: ${stamp}
|
|
|
3332
3373
|
Points: ${noteCount}
|
|
3333
3374
|
|
|
3334
3375
|
**Session:** ${summariseSession(notes, stamp)}`
|
|
3376
|
+
);
|
|
3377
|
+
sections.push(
|
|
3378
|
+
`## What the tags mean
|
|
3379
|
+
|
|
3380
|
+
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.
|
|
3381
|
+
|
|
3382
|
+
### Bug \u2014 something is broken
|
|
3383
|
+
|
|
3384
|
+
Find the **root cause**, not the symptom. Do not patch over it.
|
|
3385
|
+
|
|
3386
|
+
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.
|
|
3387
|
+
|
|
3388
|
+
**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.
|
|
3389
|
+
|
|
3390
|
+
### Design \u2014 it works, but it looks or feels wrong
|
|
3391
|
+
|
|
3392
|
+
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.
|
|
3393
|
+
|
|
3394
|
+
### Enhance \u2014 a new idea, thought of while using the app
|
|
3395
|
+
|
|
3396
|
+
The tester wants something added or changed: a new section, a new feature, a different way of doing it.
|
|
3397
|
+
|
|
3398
|
+
**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.
|
|
3399
|
+
|
|
3400
|
+
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.
|
|
3401
|
+
|
|
3402
|
+
### A point with a "round 2" on it
|
|
3403
|
+
|
|
3404
|
+
Some points carry a **round 2** block: the same finding, already worked on once, re-tested, and still not right. Two things follow from that.
|
|
3405
|
+
|
|
3406
|
+
First, **the round-2 text is the current ask** and the paragraph above it is history \u2014 read the history to understand what was wanted, act on the round 2.
|
|
3407
|
+
|
|
3408
|
+
Second, **the previous attempt is evidence.** Something about the first reading was wrong, so do not simply do it again more carefully. Work out what was misunderstood the first time and say so in one sentence, then fix that. If a "Before"/"After" screenshot pair is attached, the After is what the tester was looking at when they wrote the round 2.
|
|
3409
|
+
|
|
3410
|
+
### When the tag and the words disagree
|
|
3411
|
+
|
|
3412
|
+
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.
|
|
3413
|
+
|
|
3414
|
+
And if a point is phrased as a question rather than a report, **answer it \u2014 do not change code on a maybe.**`
|
|
3335
3415
|
);
|
|
3336
3416
|
const stack = typeof p.stack === "string" && p.stack.trim() ? p.stack.trim() : "(not provided)";
|
|
3337
3417
|
const runArr = toStrings(p.runCommands);
|
|
@@ -3927,12 +4007,12 @@ function campaignJsonText(allNotes) {
|
|
|
3927
4007
|
function reportMarkdownText(allNotes) {
|
|
3928
4008
|
if (!campaign) return "";
|
|
3929
4009
|
const ordered = orderedKnownNotes(allNotes);
|
|
3930
|
-
const counts = { bug: 0,
|
|
4010
|
+
const counts = { bug: 0, design: 0, enhance: 0, verified: 0 };
|
|
3931
4011
|
for (const n of ordered) {
|
|
3932
4012
|
const sev = n.severity ?? "bug";
|
|
3933
4013
|
if (sev === "bug") counts.bug++;
|
|
3934
|
-
else if (sev === "
|
|
3935
|
-
else counts.
|
|
4014
|
+
else if (sev === "enhance") counts.enhance++;
|
|
4015
|
+
else counts.design++;
|
|
3936
4016
|
if (n.status === "verified") counts.verified++;
|
|
3937
4017
|
}
|
|
3938
4018
|
const header = [
|
|
@@ -3941,7 +4021,7 @@ function reportMarkdownText(allNotes) {
|
|
|
3941
4021
|
campaign.tester ? `Tester: ${campaign.tester} ` : "",
|
|
3942
4022
|
`Started: ${campaign.startedAt} `,
|
|
3943
4023
|
`Updated: ${(/* @__PURE__ */ new Date()).toISOString()} `,
|
|
3944
|
-
`Points: ${ordered.length} (${counts.bug} bug, ${counts.
|
|
4024
|
+
`Points: ${ordered.length} (${counts.bug} bug, ${counts.design} design, ${counts.enhance} enhance \xB7 ${counts.verified} verified)`,
|
|
3945
4025
|
"",
|
|
3946
4026
|
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",
|
|
3947
4027
|
"`screenshots/`, one Markdown file per point in `notes/`.",
|
|
@@ -4293,6 +4373,12 @@ function QaProvider({
|
|
|
4293
4373
|
for (const id of pendingIds) void idb.delete(id);
|
|
4294
4374
|
storage.setJSON(PENDING_DELETE_KEY, []);
|
|
4295
4375
|
}
|
|
4376
|
+
live = live.map((n) => {
|
|
4377
|
+
const legacy = n.severity;
|
|
4378
|
+
if (legacy === "question") return { ...n, severity: "enhance" };
|
|
4379
|
+
if (legacy === "polish") return { ...n, severity: "design" };
|
|
4380
|
+
return n;
|
|
4381
|
+
});
|
|
4296
4382
|
const sorted = live.slice().sort(
|
|
4297
4383
|
(a, b) => a.timestamp < b.timestamp ? 1 : -1
|
|
4298
4384
|
);
|
|
@@ -4565,6 +4651,11 @@ function QaProvider({
|
|
|
4565
4651
|
}
|
|
4566
4652
|
if (patch.severity !== void 0) updated.severity = patch.severity;
|
|
4567
4653
|
if (patch.status !== void 0) updated.status = patch.status;
|
|
4654
|
+
if (patch.followUp !== void 0) {
|
|
4655
|
+
const text = patch.followUp.trim();
|
|
4656
|
+
updated.followUp = text || void 0;
|
|
4657
|
+
updated.followUpAt = text ? nowIso() : void 0;
|
|
4658
|
+
}
|
|
4568
4659
|
applyNotes((prev) => prev.map((n) => n.id === id ? updated : n));
|
|
4569
4660
|
const persisted = await idb.put(updated);
|
|
4570
4661
|
if (!persisted) {
|
|
@@ -4891,8 +4982,8 @@ function QaProvider({
|
|
|
4891
4982
|
const counts = {
|
|
4892
4983
|
all: notes.length,
|
|
4893
4984
|
bug: 0,
|
|
4894
|
-
|
|
4895
|
-
|
|
4985
|
+
design: 0,
|
|
4986
|
+
enhance: 0,
|
|
4896
4987
|
open: 0,
|
|
4897
4988
|
fixed: 0,
|
|
4898
4989
|
verified: 0,
|
|
@@ -4901,8 +4992,8 @@ function QaProvider({
|
|
|
4901
4992
|
for (const n of notes) {
|
|
4902
4993
|
const sev = n.severity ?? "bug";
|
|
4903
4994
|
if (sev === "bug") counts.bug++;
|
|
4904
|
-
else if (sev === "
|
|
4905
|
-
else counts.
|
|
4995
|
+
else if (sev === "enhance") counts.enhance++;
|
|
4996
|
+
else counts.design++;
|
|
4906
4997
|
const status = n.status ?? "open";
|
|
4907
4998
|
if (status === "verified") counts.verified++;
|
|
4908
4999
|
else if (status === "fixed") counts.fixed++;
|
|
@@ -5291,7 +5382,11 @@ function QaProvider({
|
|
|
5291
5382
|
pendingShare,
|
|
5292
5383
|
sharePending,
|
|
5293
5384
|
retestNote,
|
|
5294
|
-
|
|
5385
|
+
// The greeting explains how to capture. Once a note exists, the tester has
|
|
5386
|
+
// either done it or found their own way, so the line has nothing left to
|
|
5387
|
+
// say — take it away rather than making them dismiss it. (The stored flag
|
|
5388
|
+
// still governs a first visit; this only hides it early.)
|
|
5389
|
+
showWelcome: showWelcome && notes.length === 0,
|
|
5295
5390
|
dismissWelcome,
|
|
5296
5391
|
// v0.6
|
|
5297
5392
|
panelSide,
|
|
@@ -5744,8 +5839,8 @@ function QaFab() {
|
|
|
5744
5839
|
}
|
|
5745
5840
|
var SEVERITIES = [
|
|
5746
5841
|
{ value: "bug", labelKey: "sev_bug", icon: "Bug" },
|
|
5747
|
-
{ value: "
|
|
5748
|
-
{ value: "
|
|
5842
|
+
{ value: "enhance", labelKey: "sev_enhance" },
|
|
5843
|
+
{ value: "design", labelKey: "sev_design" }
|
|
5749
5844
|
];
|
|
5750
5845
|
function SeverityChipRow({
|
|
5751
5846
|
value,
|
|
@@ -6490,13 +6585,13 @@ function KindBadge({
|
|
|
6490
6585
|
}
|
|
6491
6586
|
var SEVERITY_CLASS = {
|
|
6492
6587
|
bug: "qa-bg-danger-tint qa-text-danger",
|
|
6493
|
-
|
|
6494
|
-
|
|
6588
|
+
enhance: "qa-bg-warn-tint qa-text-warn",
|
|
6589
|
+
design: "qa-bg-accent-tint qa-text-accent"
|
|
6495
6590
|
};
|
|
6496
6591
|
var SEVERITY_LABEL_KEY = {
|
|
6497
6592
|
bug: "sev_bug",
|
|
6498
|
-
|
|
6499
|
-
|
|
6593
|
+
enhance: "sev_enhance",
|
|
6594
|
+
design: "sev_design"
|
|
6500
6595
|
};
|
|
6501
6596
|
function SeverityChip({ severity, t }) {
|
|
6502
6597
|
return /* @__PURE__ */ jsxs(
|
|
@@ -6556,6 +6651,8 @@ function NoteItem({
|
|
|
6556
6651
|
const thumbUrl = useObjectUrl(editing ? img ?? void 0 : note.screenshot);
|
|
6557
6652
|
const afterUrl = useObjectUrl(note.afterScreenshot);
|
|
6558
6653
|
const [retesting, setRetesting] = useState(false);
|
|
6654
|
+
const [editingFollowUp, setEditingFollowUp] = useState(false);
|
|
6655
|
+
const [followUpDraft, setFollowUpDraft] = useState(note.followUp ?? "");
|
|
6559
6656
|
const [open, setOpen] = useState(false);
|
|
6560
6657
|
const severity = note.severity ?? "bug";
|
|
6561
6658
|
const status = note.status ?? "open";
|
|
@@ -6601,7 +6698,7 @@ function NoteItem({
|
|
|
6601
6698
|
notify(t("copy_failed"), { tone: "error" });
|
|
6602
6699
|
}
|
|
6603
6700
|
};
|
|
6604
|
-
const severityDot = severity === "bug" ? "var(--qa-danger)" : severity === "
|
|
6701
|
+
const severityDot = severity === "bug" ? "var(--qa-danger)" : severity === "enhance" ? "var(--qa-warn)" : "var(--qa-accent)";
|
|
6605
6702
|
if (dense && !open && !editing) {
|
|
6606
6703
|
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
6607
6704
|
"button",
|
|
@@ -6889,7 +6986,96 @@ function NoteItem({
|
|
|
6889
6986
|
style: { borderColor: "var(--qa-success)" }
|
|
6890
6987
|
}
|
|
6891
6988
|
)
|
|
6892
|
-
] })
|
|
6989
|
+
] }),
|
|
6990
|
+
(status === "fixed" || note.followUp) && /* @__PURE__ */ jsx(
|
|
6991
|
+
"div",
|
|
6992
|
+
{
|
|
6993
|
+
className: "qa-mt-2 qa-rounded-lg qa-border qa-border-subtle qa-bg-2 qa-p-2",
|
|
6994
|
+
"data-qa-followup": "true",
|
|
6995
|
+
children: editingFollowUp ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6996
|
+
/* @__PURE__ */ jsx("label", { className: "qa-text-10 qa-text-lo", htmlFor: `qa-fu-${note.id}`, children: t("followup_label") }),
|
|
6997
|
+
/* @__PURE__ */ jsx(
|
|
6998
|
+
"textarea",
|
|
6999
|
+
{
|
|
7000
|
+
id: `qa-fu-${note.id}`,
|
|
7001
|
+
value: followUpDraft,
|
|
7002
|
+
onChange: (e) => setFollowUpDraft(e.target.value),
|
|
7003
|
+
placeholder: t("followup_placeholder"),
|
|
7004
|
+
rows: 3,
|
|
7005
|
+
className: "qa-mt-1 qa-w-full qa-rounded-lg qa-border qa-border-subtle qa-bg-1 qa-p-2 qa-text-xs qa-text-hi",
|
|
7006
|
+
style: { outline: "none", resize: "vertical" }
|
|
7007
|
+
}
|
|
7008
|
+
),
|
|
7009
|
+
/* @__PURE__ */ jsx("p", { className: "qa-mt-1 qa-text-10 qa-text-lo", children: t("followup_hint") }),
|
|
7010
|
+
/* @__PURE__ */ jsxs("div", { className: "qa-mt-1.5 qa-flex qa-gap-2", children: [
|
|
7011
|
+
/* @__PURE__ */ jsxs(
|
|
7012
|
+
"button",
|
|
7013
|
+
{
|
|
7014
|
+
type: "button",
|
|
7015
|
+
onClick: () => {
|
|
7016
|
+
void updateNote(note.id, { followUp: followUpDraft });
|
|
7017
|
+
setEditingFollowUp(false);
|
|
7018
|
+
},
|
|
7019
|
+
className: "qa-tap qa-inline-flex qa-items-center qa-gap-1 qa-rounded-lg qa-bg-accent qa-px-2 qa-py-1 qa-text-11 qa-font-semibold",
|
|
7020
|
+
style: { border: "none", cursor: "pointer" },
|
|
7021
|
+
children: [
|
|
7022
|
+
/* @__PURE__ */ jsx(Icon, { name: "Check", size: 12 }),
|
|
7023
|
+
t("save")
|
|
7024
|
+
]
|
|
7025
|
+
}
|
|
7026
|
+
),
|
|
7027
|
+
/* @__PURE__ */ jsx(
|
|
7028
|
+
"button",
|
|
7029
|
+
{
|
|
7030
|
+
type: "button",
|
|
7031
|
+
onClick: () => {
|
|
7032
|
+
setFollowUpDraft(note.followUp ?? "");
|
|
7033
|
+
setEditingFollowUp(false);
|
|
7034
|
+
},
|
|
7035
|
+
className: "qa-tap qa-rounded-lg qa-border qa-border-subtle qa-px-2 qa-py-1 qa-text-11 qa-text-mid",
|
|
7036
|
+
style: { background: "transparent", cursor: "pointer" },
|
|
7037
|
+
children: t("cancel")
|
|
7038
|
+
}
|
|
7039
|
+
)
|
|
7040
|
+
] })
|
|
7041
|
+
] }) : note.followUp ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7042
|
+
/* @__PURE__ */ jsxs("div", { className: "qa-flex qa-items-center qa-gap-1", children: [
|
|
7043
|
+
/* @__PURE__ */ jsx(Icon, { name: "RotateCcw", size: 11, className: "qa-shrink-0 qa-text-warn" }),
|
|
7044
|
+
/* @__PURE__ */ jsx("span", { className: "qa-text-10 qa-text-warn", children: t("followup_label") }),
|
|
7045
|
+
/* @__PURE__ */ jsx(
|
|
7046
|
+
"button",
|
|
7047
|
+
{
|
|
7048
|
+
type: "button",
|
|
7049
|
+
onClick: () => {
|
|
7050
|
+
setFollowUpDraft(note.followUp ?? "");
|
|
7051
|
+
setEditingFollowUp(true);
|
|
7052
|
+
},
|
|
7053
|
+
"aria-label": t("edit"),
|
|
7054
|
+
className: "qa-tap-icon qa-ms-auto qa-text-lo",
|
|
7055
|
+
style: { background: "transparent", border: "none", cursor: "pointer" },
|
|
7056
|
+
children: /* @__PURE__ */ jsx(Icon, { name: "Pencil", size: 11 })
|
|
7057
|
+
}
|
|
7058
|
+
)
|
|
7059
|
+
] }),
|
|
7060
|
+
/* @__PURE__ */ jsx("p", { className: "qa-mt-1 qa-whitespace-pre-wrap qa-break-words qa-text-xs qa-text-hi", children: note.followUp })
|
|
7061
|
+
] }) : /* @__PURE__ */ jsxs(
|
|
7062
|
+
"button",
|
|
7063
|
+
{
|
|
7064
|
+
type: "button",
|
|
7065
|
+
onClick: () => {
|
|
7066
|
+
setFollowUpDraft("");
|
|
7067
|
+
setEditingFollowUp(true);
|
|
7068
|
+
},
|
|
7069
|
+
className: "qa-tap qa-inline-flex qa-items-center qa-gap-1 qa-text-11 qa-font-medium qa-text-accent",
|
|
7070
|
+
style: { background: "transparent", border: "none", cursor: "pointer" },
|
|
7071
|
+
children: [
|
|
7072
|
+
/* @__PURE__ */ jsx(Icon, { name: "Plus", size: 12 }),
|
|
7073
|
+
t("followup_add")
|
|
7074
|
+
]
|
|
7075
|
+
}
|
|
7076
|
+
)
|
|
7077
|
+
}
|
|
7078
|
+
)
|
|
6893
7079
|
] })
|
|
6894
7080
|
]
|
|
6895
7081
|
}
|
|
@@ -7122,8 +7308,11 @@ function FilterChip({ chip }) {
|
|
|
7122
7308
|
);
|
|
7123
7309
|
}
|
|
7124
7310
|
function NoteFilterBar() {
|
|
7125
|
-
const { filter, setFilter, noteCounts, notes, t } = useQa();
|
|
7311
|
+
const { filter, setFilter, noteCounts, notes, deleteNotes, t } = useQa();
|
|
7312
|
+
const [confirmPrune, setConfirmPrune] = useState(false);
|
|
7126
7313
|
if (notes.length < 3) return null;
|
|
7314
|
+
const others = notes.filter((n) => (n.status ?? "open") !== "fixed");
|
|
7315
|
+
const showPrune = filter.status === "fixed" && noteCounts.fixed > 0 && others.length > 0;
|
|
7127
7316
|
const sev = (key) => () => setFilter({ severity: filter.severity === key ? "all" : key });
|
|
7128
7317
|
const stat = (key) => () => setFilter({ status: filter.status === key ? "all" : key });
|
|
7129
7318
|
const chips = [
|
|
@@ -7144,22 +7333,22 @@ function NoteFilterBar() {
|
|
|
7144
7333
|
onClick: sev("bug")
|
|
7145
7334
|
},
|
|
7146
7335
|
{
|
|
7147
|
-
key: "
|
|
7148
|
-
label: t("
|
|
7149
|
-
count: noteCounts.
|
|
7150
|
-
active: filter.severity === "
|
|
7151
|
-
icon: "
|
|
7336
|
+
key: "enhance",
|
|
7337
|
+
label: t("sev_enhance"),
|
|
7338
|
+
count: noteCounts.enhance,
|
|
7339
|
+
active: filter.severity === "enhance",
|
|
7340
|
+
icon: "Plus",
|
|
7152
7341
|
tone: "warn",
|
|
7153
|
-
onClick: sev("
|
|
7342
|
+
onClick: sev("enhance")
|
|
7154
7343
|
},
|
|
7155
7344
|
{
|
|
7156
|
-
key: "
|
|
7157
|
-
label: t("
|
|
7158
|
-
count: noteCounts.
|
|
7159
|
-
active: filter.severity === "
|
|
7345
|
+
key: "design",
|
|
7346
|
+
label: t("sev_design"),
|
|
7347
|
+
count: noteCounts.design,
|
|
7348
|
+
active: filter.severity === "design",
|
|
7160
7349
|
icon: "Pencil",
|
|
7161
7350
|
tone: "accent",
|
|
7162
|
-
onClick: sev("
|
|
7351
|
+
onClick: sev("design")
|
|
7163
7352
|
},
|
|
7164
7353
|
{
|
|
7165
7354
|
key: "open",
|
|
@@ -7228,7 +7417,48 @@ function NoteFilterBar() {
|
|
|
7228
7417
|
]
|
|
7229
7418
|
}
|
|
7230
7419
|
),
|
|
7231
|
-
/* @__PURE__ */ jsx("div", { className: "qa-flex qa-flex-wrap qa-gap-1", children: chips.map((chip) => /* @__PURE__ */ jsx(FilterChip, { chip }, chip.key)) })
|
|
7420
|
+
/* @__PURE__ */ jsx("div", { className: "qa-flex qa-flex-wrap qa-gap-1", children: chips.map((chip) => /* @__PURE__ */ jsx(FilterChip, { chip }, chip.key)) }),
|
|
7421
|
+
showPrune && /* @__PURE__ */ jsx("div", { className: "qa-text-11", "data-qa-prune": "true", children: confirmPrune ? /* @__PURE__ */ jsxs("span", { className: "qa-text-mid", children: [
|
|
7422
|
+
t("keep_only_retest_q", { n: others.length }),
|
|
7423
|
+
" ",
|
|
7424
|
+
/* @__PURE__ */ jsx(
|
|
7425
|
+
"button",
|
|
7426
|
+
{
|
|
7427
|
+
type: "button",
|
|
7428
|
+
onClick: () => {
|
|
7429
|
+
void deleteNotes(others.map((n) => n.id));
|
|
7430
|
+
setConfirmPrune(false);
|
|
7431
|
+
},
|
|
7432
|
+
className: "qa-font-semibold qa-text-danger qa-tap",
|
|
7433
|
+
style: { background: "transparent", border: "none", cursor: "pointer" },
|
|
7434
|
+
children: t("yes")
|
|
7435
|
+
}
|
|
7436
|
+
),
|
|
7437
|
+
" / ",
|
|
7438
|
+
/* @__PURE__ */ jsx(
|
|
7439
|
+
"button",
|
|
7440
|
+
{
|
|
7441
|
+
type: "button",
|
|
7442
|
+
onClick: () => setConfirmPrune(false),
|
|
7443
|
+
className: "qa-text-accent qa-tap",
|
|
7444
|
+
style: { background: "transparent", border: "none", cursor: "pointer" },
|
|
7445
|
+
children: t("no")
|
|
7446
|
+
}
|
|
7447
|
+
)
|
|
7448
|
+
] }) : /* @__PURE__ */ jsxs(
|
|
7449
|
+
"button",
|
|
7450
|
+
{
|
|
7451
|
+
type: "button",
|
|
7452
|
+
onClick: () => setConfirmPrune(true),
|
|
7453
|
+
title: t("keep_only_retest_hint"),
|
|
7454
|
+
className: "qa-tap qa-inline-flex qa-items-center qa-gap-1 qa-text-lo",
|
|
7455
|
+
style: { background: "transparent", border: "none", cursor: "pointer" },
|
|
7456
|
+
children: [
|
|
7457
|
+
/* @__PURE__ */ jsx(Icon, { name: "Trash", size: 11 }),
|
|
7458
|
+
t("keep_only_retest", { n: noteCounts.fixed })
|
|
7459
|
+
]
|
|
7460
|
+
}
|
|
7461
|
+
) })
|
|
7232
7462
|
] });
|
|
7233
7463
|
}
|
|
7234
7464
|
function Section({
|
|
@@ -7598,46 +7828,25 @@ function SettingsSheet({ onClose }) {
|
|
|
7598
7828
|
);
|
|
7599
7829
|
}
|
|
7600
7830
|
function WelcomeCard() {
|
|
7601
|
-
const { t,
|
|
7831
|
+
const { t, dismissWelcome } = useQa();
|
|
7602
7832
|
return /* @__PURE__ */ jsxs(
|
|
7603
7833
|
"div",
|
|
7604
7834
|
{
|
|
7605
|
-
className: "qa-rounded-xl qa-border qa-border-subtle qa-bg-1 qa-
|
|
7835
|
+
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",
|
|
7606
7836
|
role: "note",
|
|
7607
7837
|
children: [
|
|
7608
|
-
/* @__PURE__ */
|
|
7609
|
-
|
|
7610
|
-
"span",
|
|
7611
|
-
{
|
|
7612
|
-
"aria-hidden": "true",
|
|
7613
|
-
className: "qa-shrink-0",
|
|
7614
|
-
style: { width: 6, height: 6, background: "var(--qa-accent)" }
|
|
7615
|
-
}
|
|
7616
|
-
),
|
|
7617
|
-
t("welcome_title", { brand: brand.label })
|
|
7618
|
-
] }),
|
|
7619
|
-
/* @__PURE__ */ jsxs("ul", { className: "qa-m-0 qa-space-y-1 qa-text-xs qa-text-mid", children: [
|
|
7620
|
-
/* @__PURE__ */ jsxs("li", { className: "qa-flex qa-items-start qa-gap-1.5", children: [
|
|
7621
|
-
/* @__PURE__ */ jsx(Icon, { name: "Crosshair", size: 12, className: "qa-shrink-0 qa-mt-1" }),
|
|
7622
|
-
/* @__PURE__ */ jsx("span", { children: t("welcome_capture") })
|
|
7623
|
-
] }),
|
|
7624
|
-
/* @__PURE__ */ jsxs("li", { className: "qa-flex qa-items-start qa-gap-1.5", children: [
|
|
7625
|
-
/* @__PURE__ */ jsx(Icon, { name: "Pencil", size: 12, className: "qa-shrink-0 qa-mt-1" }),
|
|
7626
|
-
/* @__PURE__ */ jsx("span", { children: t("welcome_say") })
|
|
7627
|
-
] }),
|
|
7628
|
-
/* @__PURE__ */ jsxs("li", { className: "qa-flex qa-items-start qa-gap-1.5", children: [
|
|
7629
|
-
/* @__PURE__ */ jsx(Icon, { name: "CheckCircle2", size: 12, className: "qa-shrink-0 qa-mt-1" }),
|
|
7630
|
-
/* @__PURE__ */ jsx("span", { children: t("welcome_safe") })
|
|
7631
|
-
] })
|
|
7632
|
-
] }),
|
|
7838
|
+
/* @__PURE__ */ jsx(Icon, { name: "Crosshair", size: 13, className: "qa-shrink-0 qa-text-accent" }),
|
|
7839
|
+
/* @__PURE__ */ jsx("span", { className: "qa-flex-1 qa-text-xs qa-text-mid", children: t("welcome_capture") }),
|
|
7633
7840
|
/* @__PURE__ */ jsx(
|
|
7634
7841
|
"button",
|
|
7635
7842
|
{
|
|
7636
7843
|
type: "button",
|
|
7637
7844
|
onClick: dismissWelcome,
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7845
|
+
"aria-label": t("welcome_got_it"),
|
|
7846
|
+
title: t("welcome_got_it"),
|
|
7847
|
+
className: "qa-tap-icon qa-shrink-0 qa-rounded-full qa-text-mid qa-focus-ring",
|
|
7848
|
+
style: { background: "transparent", border: "none", cursor: "pointer", padding: 2 },
|
|
7849
|
+
children: /* @__PURE__ */ jsx(Icon, { name: "X", size: 13 })
|
|
7641
7850
|
}
|
|
7642
7851
|
)
|
|
7643
7852
|
]
|
|
@@ -7785,249 +7994,9 @@ function CredentialsSection() {
|
|
|
7785
7994
|
})
|
|
7786
7995
|
] });
|
|
7787
7996
|
}
|
|
7788
|
-
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
group,
|
|
7792
|
-
checked,
|
|
7793
|
-
toggle,
|
|
7794
|
-
pick: pick2,
|
|
7795
|
-
onWalkFrom
|
|
7796
|
-
}) {
|
|
7797
|
-
const { lang, t, guideFailed, evidenceByStep } = useQa();
|
|
7798
|
-
const { id, color = DEFAULT_LANE_COLOR, steps: steps2 } = group;
|
|
7799
|
-
const done = steps2.filter((s) => checked.has(keyOf(id, s.path))).length;
|
|
7800
|
-
const pct = steps2.length > 0 ? Math.round(done / steps2.length * 100) : 0;
|
|
7801
|
-
const uncoveredRedCount = steps2.filter(
|
|
7802
|
-
(s) => s.risk === "red" && !checked.has(keyOf(id, s.path))
|
|
7803
|
-
).length;
|
|
7804
|
-
return /* @__PURE__ */ jsxs("div", { className: "qa-rounded-xl qa-border qa-border-subtle qa-bg-2 qa-p-3 qa-elev-1", children: [
|
|
7805
|
-
/* @__PURE__ */ jsxs("div", { className: "qa-mb-2 qa-flex qa-items-center qa-gap-2", children: [
|
|
7806
|
-
/* @__PURE__ */ jsx(
|
|
7807
|
-
"span",
|
|
7808
|
-
{
|
|
7809
|
-
className: "qa-h-2.5 qa-w-2.5 qa-rounded-full",
|
|
7810
|
-
style: { background: color }
|
|
7811
|
-
}
|
|
7812
|
-
),
|
|
7813
|
-
/* @__PURE__ */ jsx("span", { className: "qa-text-sm qa-font-bold qa-text-hi", children: pick2(group.role) }),
|
|
7814
|
-
/* @__PURE__ */ jsxs("span", { className: "qa-ms-auto qa-text-11 qa-font-medium qa-text-slate-400", children: [
|
|
7815
|
-
done,
|
|
7816
|
-
"/",
|
|
7817
|
-
steps2.length
|
|
7818
|
-
] }),
|
|
7819
|
-
uncoveredRedCount > 0 && /* @__PURE__ */ jsx(
|
|
7820
|
-
"span",
|
|
7821
|
-
{
|
|
7822
|
-
className: "qa-bg-danger-tint qa-text-danger qa-rounded qa-px-1 qa-text-10 qa-font-medium",
|
|
7823
|
-
title: lang === "ar" ? `${uncoveredRedCount} \u0645\u0646\u0637\u0642\u0629 \u062D\u0645\u0631\u0627\u0621 \u063A\u064A\u0631 \u0645\u063A\u0637\u0627\u0629` : `${uncoveredRedCount} uncovered red zone(s)`,
|
|
7824
|
-
children: lang === "ar" ? `\u0623\u062D\u0645\u0631: ${uncoveredRedCount}` : `red: ${uncoveredRedCount}`
|
|
7825
|
-
}
|
|
7826
|
-
)
|
|
7827
|
-
] }),
|
|
7828
|
-
/* @__PURE__ */ jsx(
|
|
7829
|
-
"div",
|
|
7830
|
-
{
|
|
7831
|
-
className: "qa-mb-3 qa-h-1.5 qa-overflow-hidden qa-rounded-full",
|
|
7832
|
-
style: { background: `${color}22` },
|
|
7833
|
-
children: /* @__PURE__ */ jsx(
|
|
7834
|
-
"div",
|
|
7835
|
-
{
|
|
7836
|
-
className: "qa-h-full qa-rounded-full qa-transition-all",
|
|
7837
|
-
style: { width: `${pct}%`, background: color }
|
|
7838
|
-
}
|
|
7839
|
-
)
|
|
7840
|
-
}
|
|
7841
|
-
),
|
|
7842
|
-
/* @__PURE__ */ jsxs("ol", { className: "qa-relative qa-ms-1.5", children: [
|
|
7843
|
-
/* @__PURE__ */ jsx(
|
|
7844
|
-
"span",
|
|
7845
|
-
{
|
|
7846
|
-
className: "qa-absolute qa-top-1 qa-bottom-0 qa-w-px",
|
|
7847
|
-
style: { insetInlineStart: "7px", background: `${color}40`, bottom: "4px" }
|
|
7848
|
-
}
|
|
7849
|
-
),
|
|
7850
|
-
steps2.map((s, i) => {
|
|
7851
|
-
const k = keyOf(id, s.path);
|
|
7852
|
-
const on = checked.has(k);
|
|
7853
|
-
const failed = guideFailed.has(k);
|
|
7854
|
-
const evidence = evidenceByStep.get(k);
|
|
7855
|
-
const evidenceCount = evidence ? evidence.length : 0;
|
|
7856
|
-
const riskColor = s.risk ? RISK_COLORS[s.risk] : RISK_COLORS.none;
|
|
7857
|
-
const dotTitle = !s.risk ? lang === "ar" ? "\u0644\u0645 \u064A\u062A\u0645 \u062A\u0642\u064A\u064A\u0645 \u0627\u0644\u0645\u062E\u0627\u0637\u0631 \u0628\u0639\u062F" : "not graded yet" : s.riskWhy ?? s.risk;
|
|
7858
|
-
return /* @__PURE__ */ jsxs("li", { className: "qa-relative qa-mb-2 qa-last-mb-0", children: [
|
|
7859
|
-
/* @__PURE__ */ jsx(
|
|
7860
|
-
"button",
|
|
7861
|
-
{
|
|
7862
|
-
onClick: () => onWalkFrom(k),
|
|
7863
|
-
title: t("walk_take_me", { path: s.path }),
|
|
7864
|
-
"aria-label": t("walk_take_me", { path: s.path }),
|
|
7865
|
-
className: "qa-tap-icon qa-absolute qa-rounded-lg qa-text-mid qa-hover-text-slate-600",
|
|
7866
|
-
style: {
|
|
7867
|
-
top: 0,
|
|
7868
|
-
insetInlineEnd: 0,
|
|
7869
|
-
background: "transparent",
|
|
7870
|
-
border: "none",
|
|
7871
|
-
cursor: "pointer",
|
|
7872
|
-
zIndex: 2
|
|
7873
|
-
},
|
|
7874
|
-
children: /* @__PURE__ */ jsx(Icon, { name: "Play", size: 12 })
|
|
7875
|
-
}
|
|
7876
|
-
),
|
|
7877
|
-
/* @__PURE__ */ jsxs(
|
|
7878
|
-
"button",
|
|
7879
|
-
{
|
|
7880
|
-
onClick: () => toggle(k),
|
|
7881
|
-
className: `qa-flex qa-w-full qa-items-start qa-gap-2.5 qa-rounded-lg qa-p-1 qa-text-start qa-hover-bg-black-3${failed ? " qa-bg-danger-tint" : ""}`,
|
|
7882
|
-
style: { background: failed ? void 0 : "transparent", border: "none", cursor: "pointer" },
|
|
7883
|
-
children: [
|
|
7884
|
-
/* @__PURE__ */ jsxs(
|
|
7885
|
-
"span",
|
|
7886
|
-
{
|
|
7887
|
-
className: "qa-relative qa-z-1 qa-mt-0.5 qa-flex qa-h-4 qa-w-4 qa-shrink-0 qa-items-center qa-justify-center qa-rounded-full qa-border-2 qa-transition",
|
|
7888
|
-
style: {
|
|
7889
|
-
borderColor: failed ? "var(--qa-danger)" : color,
|
|
7890
|
-
background: on ? color : failed ? "var(--qa-danger-tint)" : "var(--qa-surface-1)",
|
|
7891
|
-
zIndex: 1
|
|
7892
|
-
},
|
|
7893
|
-
children: [
|
|
7894
|
-
on && /* @__PURE__ */ jsx(Icon, { name: "Check", size: 10, strokeWidth: 3, className: "qa-text-white" }),
|
|
7895
|
-
!on && failed && /* @__PURE__ */ jsx(Icon, { name: "AlertTriangle", size: 9, strokeWidth: 2.5, className: "qa-text-danger" })
|
|
7896
|
-
]
|
|
7897
|
-
}
|
|
7898
|
-
),
|
|
7899
|
-
/* @__PURE__ */ jsxs("span", { className: "qa-min-w-0", children: [
|
|
7900
|
-
/* @__PURE__ */ jsxs("span", { className: "qa-flex qa-items-center qa-gap-1", children: [
|
|
7901
|
-
/* @__PURE__ */ jsx(
|
|
7902
|
-
"code",
|
|
7903
|
-
{
|
|
7904
|
-
className: "qa-rounded qa-px-1 qa-text-11 qa-font-semibold qa-dir-ltr qa-text-hi",
|
|
7905
|
-
style: {
|
|
7906
|
-
background: failed ? "var(--qa-danger-tint)" : `${color}14`,
|
|
7907
|
-
textDecoration: on ? "line-through" : "none",
|
|
7908
|
-
opacity: on ? 0.55 : 1
|
|
7909
|
-
},
|
|
7910
|
-
children: s.path
|
|
7911
|
-
}
|
|
7912
|
-
),
|
|
7913
|
-
/* @__PURE__ */ jsx(
|
|
7914
|
-
"span",
|
|
7915
|
-
{
|
|
7916
|
-
className: "qa-inline-block qa-rounded-full qa-shrink-0",
|
|
7917
|
-
style: {
|
|
7918
|
-
width: "6px",
|
|
7919
|
-
height: "6px",
|
|
7920
|
-
background: riskColor,
|
|
7921
|
-
flexShrink: 0
|
|
7922
|
-
},
|
|
7923
|
-
title: dotTitle
|
|
7924
|
-
}
|
|
7925
|
-
)
|
|
7926
|
-
] }),
|
|
7927
|
-
/* @__PURE__ */ jsx(
|
|
7928
|
-
"span",
|
|
7929
|
-
{
|
|
7930
|
-
className: "qa-mt-0.5 qa-block qa-text-11 qa-leading-relaxed qa-text-slate-500",
|
|
7931
|
-
style: { opacity: on ? 0.5 : 1 },
|
|
7932
|
-
children: pick2(s.what)
|
|
7933
|
-
}
|
|
7934
|
-
),
|
|
7935
|
-
evidenceCount > 0 && /* @__PURE__ */ jsx("span", { className: "qa-mt-1 qa-inline-flex qa-items-center qa-rounded qa-bg-accent-tint qa-text-accent qa-px-1 qa-text-10 qa-font-medium", children: t("evidence_n", { n: evidenceCount }) }),
|
|
7936
|
-
evidenceCount === 0 && on && /* @__PURE__ */ jsx("span", { className: "qa-mt-1 qa-inline-flex qa-items-center qa-rounded qa-bg-warn-tint qa-text-warn qa-px-1 qa-text-10 qa-font-medium", children: t("no_evidence") })
|
|
7937
|
-
] })
|
|
7938
|
-
]
|
|
7939
|
-
}
|
|
7940
|
-
)
|
|
7941
|
-
] }, `${k}-${i}`);
|
|
7942
|
-
})
|
|
7943
|
-
] })
|
|
7944
|
-
] });
|
|
7945
|
-
}
|
|
7946
|
-
function GuideSection() {
|
|
7947
|
-
const {
|
|
7948
|
-
guideChecked,
|
|
7949
|
-
toggleGuide,
|
|
7950
|
-
t,
|
|
7951
|
-
journey,
|
|
7952
|
-
journeyIsGeneric,
|
|
7953
|
-
pick: pick2,
|
|
7954
|
-
lang,
|
|
7955
|
-
startTestAlong,
|
|
7956
|
-
startWalk,
|
|
7957
|
-
testAlongSteps
|
|
7958
|
-
} = useQa();
|
|
7959
|
-
const all = journey.flatMap((g) => g.steps.map((s) => keyOf(g.id, s.path)));
|
|
7960
|
-
const done = all.filter((k) => guideChecked.has(k)).length;
|
|
7961
|
-
const pct = all.length > 0 ? Math.round(done / all.length * 100) : 0;
|
|
7962
|
-
const coverage = computeCoverage(journey, guideChecked);
|
|
7963
|
-
return /* @__PURE__ */ jsxs("div", { className: "qa-space-y-3", children: [
|
|
7964
|
-
journeyIsGeneric && /* @__PURE__ */ jsx(
|
|
7965
|
-
"p",
|
|
7966
|
-
{
|
|
7967
|
-
className: "qa-m-0 qa-rounded-xl qa-border qa-border-dashed qa-border-subtle qa-p-2 qa-text-10 qa-text-mid qa-leading-relaxed",
|
|
7968
|
-
"data-qa-generic-plan": "true",
|
|
7969
|
-
children: t("journey_generic")
|
|
7970
|
-
}
|
|
7971
|
-
),
|
|
7972
|
-
/* @__PURE__ */ jsxs("div", { className: "qa-rounded-xl qa-border qa-border-accent qa-bg-accent-tint qa-p-3 qa-elev-1", children: [
|
|
7973
|
-
coverage.red.total > 0 && /* @__PURE__ */ jsxs("div", { className: "qa-mb-1 qa-flex qa-items-center qa-gap-1.5 qa-text-11", children: [
|
|
7974
|
-
/* @__PURE__ */ jsx("span", { className: "qa-bg-danger-tint qa-text-danger qa-rounded qa-px-1 qa-font-bold", children: lang === "ar" ? "\u0623\u062D\u0645\u0631" : "RED" }),
|
|
7975
|
-
/* @__PURE__ */ jsxs("span", { className: "qa-dir-ltr qa-font-semibold qa-text-hi", children: [
|
|
7976
|
-
coverage.red.covered,
|
|
7977
|
-
"/",
|
|
7978
|
-
coverage.red.total,
|
|
7979
|
-
" ",
|
|
7980
|
-
lang === "ar" ? "\u0645\u063A\u0637\u0649" : "covered"
|
|
7981
|
-
] })
|
|
7982
|
-
] }),
|
|
7983
|
-
/* @__PURE__ */ jsxs("div", { className: "qa-flex qa-items-center qa-justify-between qa-text-sm qa-font-semibold qa-text-hi", children: [
|
|
7984
|
-
/* @__PURE__ */ jsx("span", { children: t("journey_title") }),
|
|
7985
|
-
/* @__PURE__ */ jsxs("span", { className: "qa-dir-ltr", children: [
|
|
7986
|
-
done,
|
|
7987
|
-
"/",
|
|
7988
|
-
all.length,
|
|
7989
|
-
" \xB7 ",
|
|
7990
|
-
pct,
|
|
7991
|
-
"%"
|
|
7992
|
-
] })
|
|
7993
|
-
] }),
|
|
7994
|
-
/* @__PURE__ */ jsx("div", { className: "qa-mt-2 qa-h-2 qa-overflow-hidden qa-rounded-full qa-bg-3", children: /* @__PURE__ */ jsx(
|
|
7995
|
-
"div",
|
|
7996
|
-
{
|
|
7997
|
-
className: "qa-h-full qa-rounded-full qa-bg-accent qa-transition-all",
|
|
7998
|
-
style: { width: `${pct}%` }
|
|
7999
|
-
}
|
|
8000
|
-
) }),
|
|
8001
|
-
/* @__PURE__ */ jsx("div", { className: "qa-mt-2 qa-flex qa-justify-end", children: /* @__PURE__ */ jsxs(
|
|
8002
|
-
"button",
|
|
8003
|
-
{
|
|
8004
|
-
type: "button",
|
|
8005
|
-
onClick: startTestAlong,
|
|
8006
|
-
disabled: all.length === 0,
|
|
8007
|
-
className: "qa-tap qa-inline-flex qa-items-center qa-gap-1.5 qa-rounded-full qa-bg-accent qa-border-0 qa-cursor-pointer qa-px-3 qa-py-1 qa-text-xs qa-font-semibold qa-focus-ring",
|
|
8008
|
-
children: [
|
|
8009
|
-
/* @__PURE__ */ jsx(Icon, { name: "Play", size: 13 }),
|
|
8010
|
-
t("start_walkthrough")
|
|
8011
|
-
]
|
|
8012
|
-
}
|
|
8013
|
-
) })
|
|
8014
|
-
] }),
|
|
8015
|
-
journey.map((g) => /* @__PURE__ */ jsx(
|
|
8016
|
-
Lane,
|
|
8017
|
-
{
|
|
8018
|
-
group: g,
|
|
8019
|
-
checked: guideChecked,
|
|
8020
|
-
toggle: toggleGuide,
|
|
8021
|
-
onWalkFrom: (key) => {
|
|
8022
|
-
const at = testAlongSteps.findIndex((step) => step.key === key);
|
|
8023
|
-
startWalk("plan", at === -1 ? 0 : at);
|
|
8024
|
-
},
|
|
8025
|
-
pick: pick2
|
|
8026
|
-
},
|
|
8027
|
-
g.id
|
|
8028
|
-
))
|
|
8029
|
-
] });
|
|
8030
|
-
}
|
|
7997
|
+
|
|
7998
|
+
// src/lib/features.ts
|
|
7999
|
+
var GUIDE_TAB_ENABLED = false;
|
|
8031
8000
|
function isCoarsePointer() {
|
|
8032
8001
|
if (typeof window === "undefined") return false;
|
|
8033
8002
|
try {
|
|
@@ -8051,11 +8020,15 @@ function useCoarsePointer() {
|
|
|
8051
8020
|
}, []);
|
|
8052
8021
|
return coarse;
|
|
8053
8022
|
}
|
|
8054
|
-
var
|
|
8023
|
+
var ALL_TABS = [
|
|
8055
8024
|
{ key: "notes", labelKey: "tab_notes", icon: "StickyNote" },
|
|
8056
8025
|
{ key: "logins", labelKey: "tab_logins", icon: "KeyRound" },
|
|
8057
8026
|
{ key: "guide", labelKey: "tab_guide", icon: "Map" }
|
|
8058
8027
|
];
|
|
8028
|
+
var TABS = ALL_TABS.filter((tab) => tab.key !== "guide" || GUIDE_TAB_ENABLED);
|
|
8029
|
+
function visibleTab(tab) {
|
|
8030
|
+
return tab === "guide" && !GUIDE_TAB_ENABLED ? "notes" : tab;
|
|
8031
|
+
}
|
|
8059
8032
|
function todayName() {
|
|
8060
8033
|
return `qa-notes-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
|
|
8061
8034
|
}
|
|
@@ -8090,7 +8063,7 @@ var PANEL_TRANSITION_WITH_LIFT = "opacity 200ms cubic-bezier(0.4,0,0.2,1), trans
|
|
|
8090
8063
|
function QaPanel() {
|
|
8091
8064
|
const {
|
|
8092
8065
|
isOpen,
|
|
8093
|
-
activeTab,
|
|
8066
|
+
activeTab: storedTab,
|
|
8094
8067
|
setActiveTab,
|
|
8095
8068
|
notes,
|
|
8096
8069
|
exportZip,
|
|
@@ -8121,6 +8094,7 @@ function QaPanel() {
|
|
|
8121
8094
|
panelCollapsed,
|
|
8122
8095
|
setPanelCollapsed
|
|
8123
8096
|
} = useQa();
|
|
8097
|
+
const activeTab = visibleTab(storedTab);
|
|
8124
8098
|
const [confirmClear, setConfirmClear] = useState(false);
|
|
8125
8099
|
const [naming, setNaming] = useState(false);
|
|
8126
8100
|
const [filename, setFilename] = useState("");
|
|
@@ -8488,7 +8462,7 @@ function QaPanel() {
|
|
|
8488
8462
|
) })
|
|
8489
8463
|
] }),
|
|
8490
8464
|
!simpleMode && activeTab === "logins" && /* @__PURE__ */ jsx(CredentialsSection, {}),
|
|
8491
|
-
!simpleMode &&
|
|
8465
|
+
!simpleMode && GUIDE_TAB_ENABLED
|
|
8492
8466
|
] })
|
|
8493
8467
|
] }),
|
|
8494
8468
|
settingsOpen && /* @__PURE__ */ jsx(SettingsSheet, { onClose: () => setSettingsOpen(false) }),
|
|
@@ -9104,16 +9078,16 @@ function unlockPageScroll() {
|
|
|
9104
9078
|
var DRAG_THRESHOLD2 = 6;
|
|
9105
9079
|
var TOUCH_DRAG_THRESHOLD = 12;
|
|
9106
9080
|
var MIN_REGION_SIZE = 8;
|
|
9107
|
-
var SEVERITIES2 = ["bug", "
|
|
9081
|
+
var SEVERITIES2 = ["bug", "design", "enhance"];
|
|
9108
9082
|
var SEVERITY_ICON = {
|
|
9109
9083
|
bug: "Bug",
|
|
9110
|
-
|
|
9111
|
-
|
|
9084
|
+
design: "Pencil",
|
|
9085
|
+
enhance: "Plus"
|
|
9112
9086
|
};
|
|
9113
9087
|
var SEVERITY_LABEL_KEY2 = {
|
|
9114
9088
|
bug: "sev_bug",
|
|
9115
|
-
|
|
9116
|
-
|
|
9089
|
+
design: "sev_design",
|
|
9090
|
+
enhance: "sev_enhance"
|
|
9117
9091
|
};
|
|
9118
9092
|
function clampRegionRect(rect) {
|
|
9119
9093
|
const vw = typeof window !== "undefined" ? window.innerWidth : rect.left + rect.width;
|
|
@@ -10067,7 +10041,7 @@ function CaptureMode() {
|
|
|
10067
10041
|
/* @__PURE__ */ jsx("span", { className: "qa-text-11 qa-text-mid qa-me-1", children: t("severity_label") }),
|
|
10068
10042
|
SEVERITIES2.map((sev) => {
|
|
10069
10043
|
const active = severity === sev;
|
|
10070
|
-
const toneClass = sev === "bug" ? "qa-bg-danger-tint qa-text-danger" : sev === "
|
|
10044
|
+
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";
|
|
10071
10045
|
return /* @__PURE__ */ jsxs(
|
|
10072
10046
|
"button",
|
|
10073
10047
|
{
|
|
@@ -10345,5 +10319,5 @@ function Qapture({ config }) {
|
|
|
10345
10319
|
}
|
|
10346
10320
|
|
|
10347
10321
|
export { Qapture, deleteQaDatabase, initQaStudio };
|
|
10348
|
-
//# sourceMappingURL=chunk-
|
|
10349
|
-
//# sourceMappingURL=chunk-
|
|
10322
|
+
//# sourceMappingURL=chunk-2JX7YU2P.js.map
|
|
10323
|
+
//# sourceMappingURL=chunk-2JX7YU2P.js.map
|