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
|
@@ -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",
|
|
@@ -1869,6 +1869,13 @@ var STR = {
|
|
|
1869
1869
|
retest_not_found: "Can't find that spot on this page \u2014 open the right page first",
|
|
1870
1870
|
before_label: "Before",
|
|
1871
1871
|
after_label: "After",
|
|
1872
|
+
followup_label: "What happened this time",
|
|
1873
|
+
followup_add: "Say what happened",
|
|
1874
|
+
followup_placeholder: "What did it do this time, and how is that different from what you asked for?",
|
|
1875
|
+
followup_hint: "The first note stays as it is. This is added under it as round two.",
|
|
1876
|
+
keep_only_retest: "Keep only these {n}",
|
|
1877
|
+
keep_only_retest_q: "Delete the other {n}?",
|
|
1878
|
+
keep_only_retest_hint: "Delete every note that is not waiting to be re-tested",
|
|
1872
1879
|
welcome_title: "Testing {brand}",
|
|
1873
1880
|
welcome_capture: 'Click "Capture from page", then click whatever looks wrong.',
|
|
1874
1881
|
welcome_say: "Say what you expected in your own words. A screenshot is taken for you.",
|
|
@@ -1960,10 +1967,10 @@ var STR = {
|
|
|
1960
1967
|
copied: "\u062A\u0645 \u0627\u0644\u0646\u0633\u062E",
|
|
1961
1968
|
copy_failed: "\u0641\u0634\u0644 \u0627\u0644\u0646\u0633\u062E",
|
|
1962
1969
|
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
|
-
|
|
1970
|
+
severity_label: "\u0645\u0627 \u0647\u0630\u0627\u061F",
|
|
1971
|
+
sev_bug: "Bug",
|
|
1972
|
+
sev_design: "Design",
|
|
1973
|
+
sev_enhance: "Enhance",
|
|
1967
1974
|
status_open: "\u0645\u0641\u062A\u0648\u062D",
|
|
1968
1975
|
status_verified: "\u062A\u0645 \u0627\u0644\u062A\u062D\u0642\u0642",
|
|
1969
1976
|
context_attached: "{n} \u0645\u0646 \u0623\u062D\u062F\u0627\u062B \u0627\u0644\u062A\u0634\u063A\u064A\u0644 \u0645\u0631\u0641\u0642\u0629",
|
|
@@ -2067,6 +2074,13 @@ var STR = {
|
|
|
2067
2074
|
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",
|
|
2068
2075
|
before_label: "\u0642\u0628\u0644",
|
|
2069
2076
|
after_label: "\u0628\u0639\u062F",
|
|
2077
|
+
followup_label: "\u0645\u0627\u0630\u0627 \u062D\u062F\u062B \u0647\u0630\u0647 \u0627\u0644\u0645\u0631\u0629",
|
|
2078
|
+
followup_add: "\u0627\u0643\u062A\u0628 \u0645\u0627\u0630\u0627 \u062D\u062F\u062B",
|
|
2079
|
+
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",
|
|
2080
|
+
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.",
|
|
2081
|
+
keep_only_retest: "\u0623\u0628\u0642\u0650 \u0647\u0630\u0647 \u0627\u0644\u0640 {n} \u0641\u0642\u0637",
|
|
2082
|
+
keep_only_retest_q: "\u062D\u0630\u0641 \u0627\u0644\u0640 {n} \u0627\u0644\u0623\u062E\u0631\u0649\u061F",
|
|
2083
|
+
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",
|
|
2070
2084
|
welcome_title: "\u0627\u062E\u062A\u0628\u0627\u0631 {brand}",
|
|
2071
2085
|
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.",
|
|
2072
2086
|
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.",
|
|
@@ -2111,18 +2125,6 @@ function pick(value, lang) {
|
|
|
2111
2125
|
if (typeof value === "string") return value;
|
|
2112
2126
|
return value[lang] ?? value.en ?? "";
|
|
2113
2127
|
}
|
|
2114
|
-
|
|
2115
|
-
// src/lib/coverage.ts
|
|
2116
|
-
var RISK_COLORS = {
|
|
2117
|
-
red: "#FF6B6B",
|
|
2118
|
-
// --qa-danger
|
|
2119
|
-
amber: "#FBBF24",
|
|
2120
|
-
// --qa-warn
|
|
2121
|
-
green: "#34D399",
|
|
2122
|
-
// --qa-success
|
|
2123
|
-
none: "#5B616B"
|
|
2124
|
-
// --qa-neutral
|
|
2125
|
-
};
|
|
2126
2128
|
function computeCoverage(journey, guideChecked, skipped) {
|
|
2127
2129
|
const red = { total: 0, covered: 0 };
|
|
2128
2130
|
const amber = { total: 0, covered: 0 };
|
|
@@ -2976,16 +2978,19 @@ function clipToPaintedArea(el, rect) {
|
|
|
2976
2978
|
let top = rect.top;
|
|
2977
2979
|
let right = rect.left + rect.width;
|
|
2978
2980
|
let bottom = rect.top + rect.height;
|
|
2981
|
+
const doc = el.ownerDocument;
|
|
2982
|
+
const root2 = doc ? doc.documentElement : null;
|
|
2983
|
+
const body = doc ? doc.body : null;
|
|
2984
|
+
const rootStyle = root2 ? safeComputedStyle(root2) : null;
|
|
2985
|
+
const bodyStyle = body ? safeComputedStyle(body) : null;
|
|
2979
2986
|
let node = el.parentElement;
|
|
2980
|
-
while (node) {
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
const clipsX = style.overflowX !== "visible";
|
|
2988
|
-
const clipsY = style.overflowY !== "visible";
|
|
2987
|
+
while (node && node !== root2) {
|
|
2988
|
+
const style = safeComputedStyle(node);
|
|
2989
|
+
if (!style) break;
|
|
2990
|
+
const propagatedX = node === body && rootStyle?.overflowX === "visible";
|
|
2991
|
+
const propagatedY = node === body && rootStyle?.overflowY === "visible";
|
|
2992
|
+
const clipsX = !propagatedX && style.overflowX !== "visible";
|
|
2993
|
+
const clipsY = !propagatedY && style.overflowY !== "visible";
|
|
2989
2994
|
if (clipsX || clipsY) {
|
|
2990
2995
|
const b = node.getBoundingClientRect();
|
|
2991
2996
|
const padLeft = b.left + node.clientLeft;
|
|
@@ -3001,9 +3006,33 @@ function clipToPaintedArea(el, rect) {
|
|
|
3001
3006
|
}
|
|
3002
3007
|
node = node.parentElement;
|
|
3003
3008
|
}
|
|
3009
|
+
const vpX = usedViewportOverflow(rootStyle?.overflowX, bodyStyle?.overflowX);
|
|
3010
|
+
const vpY = usedViewportOverflow(rootStyle?.overflowY, bodyStyle?.overflowY);
|
|
3011
|
+
if (vpX === "hidden" || vpX === "clip" || vpY === "hidden" || vpY === "clip") {
|
|
3012
|
+
const { vw, vh } = viewportSize();
|
|
3013
|
+
if (vpX === "hidden" || vpX === "clip") {
|
|
3014
|
+
left = Math.max(left, 0);
|
|
3015
|
+
right = Math.min(right, vw);
|
|
3016
|
+
}
|
|
3017
|
+
if (vpY === "hidden" || vpY === "clip") {
|
|
3018
|
+
top = Math.max(top, 0);
|
|
3019
|
+
bottom = Math.min(bottom, vh);
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3004
3022
|
if (right - left < 2 || bottom - top < 2) return rect;
|
|
3005
3023
|
return { left, top, width: right - left, height: bottom - top };
|
|
3006
3024
|
}
|
|
3025
|
+
function safeComputedStyle(node) {
|
|
3026
|
+
try {
|
|
3027
|
+
return getComputedStyle(node);
|
|
3028
|
+
} catch {
|
|
3029
|
+
return null;
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
function usedViewportOverflow(rootValue, bodyValue) {
|
|
3033
|
+
if (rootValue && rootValue !== "visible") return rootValue;
|
|
3034
|
+
return bodyValue || "visible";
|
|
3035
|
+
}
|
|
3007
3036
|
function intersectViewport(rect) {
|
|
3008
3037
|
const { vw, vh } = viewportSize();
|
|
3009
3038
|
const left = Math.max(0, rect.left);
|
|
@@ -3198,6 +3227,16 @@ function noteToMarkdown(note, opts) {
|
|
|
3198
3227
|
}
|
|
3199
3228
|
lines.push("");
|
|
3200
3229
|
lines.push(oneLine(note.description) ? note.description.trim() : "_(no description)_");
|
|
3230
|
+
if (note.followUp && oneLine(note.followUp)) {
|
|
3231
|
+
lines.push("");
|
|
3232
|
+
lines.push(
|
|
3233
|
+
"> **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."
|
|
3234
|
+
);
|
|
3235
|
+
lines.push("");
|
|
3236
|
+
lines.push(`**What happened this time**${note.followUpAt ? ` (${oneLine(note.followUpAt)})` : ""}`);
|
|
3237
|
+
lines.push("");
|
|
3238
|
+
lines.push(note.followUp.trim());
|
|
3239
|
+
}
|
|
3201
3240
|
const recordedSteps = note.context?.steps ?? [];
|
|
3202
3241
|
if (recordedSteps.length) {
|
|
3203
3242
|
const t0 = Date.parse(note.timestamp) || recordedSteps[recordedSteps.length - 1].t;
|
|
@@ -3284,18 +3323,19 @@ function fmtPct(n, d) {
|
|
|
3284
3323
|
}
|
|
3285
3324
|
function summariseSession(notes, stamp) {
|
|
3286
3325
|
if (!notes.length) return "No points captured.";
|
|
3287
|
-
let bugs = 0,
|
|
3326
|
+
let bugs = 0, enhancements = 0, designs = 0, verified = 0, awaiting = 0, secondRound = 0;
|
|
3288
3327
|
const pages = /* @__PURE__ */ new Set();
|
|
3289
3328
|
let earliest = Number.POSITIVE_INFINITY;
|
|
3290
3329
|
let latest = 0;
|
|
3291
3330
|
for (const n of notes) {
|
|
3292
3331
|
const sev = n.severity ?? "bug";
|
|
3293
3332
|
if (sev === "bug") bugs++;
|
|
3294
|
-
else if (sev === "
|
|
3295
|
-
else
|
|
3333
|
+
else if (sev === "enhance") enhancements++;
|
|
3334
|
+
else designs++;
|
|
3296
3335
|
const status = n.status ?? "open";
|
|
3297
3336
|
if (status === "verified") verified++;
|
|
3298
3337
|
else if (status === "fixed") awaiting++;
|
|
3338
|
+
if (n.followUp && n.followUp.trim()) secondRound++;
|
|
3299
3339
|
pages.add((n.route || "/").split("?")[0]);
|
|
3300
3340
|
const t = Date.parse(n.timestamp);
|
|
3301
3341
|
if (Number.isFinite(t)) {
|
|
@@ -3307,10 +3347,11 @@ function summariseSession(notes, stamp) {
|
|
|
3307
3347
|
`${notes.length} point${notes.length === 1 ? "" : "s"}`,
|
|
3308
3348
|
`${bugs} bug${bugs === 1 ? "" : "s"}`
|
|
3309
3349
|
];
|
|
3310
|
-
if (
|
|
3311
|
-
if (
|
|
3350
|
+
if (enhancements) parts.push(`${enhancements} enhancement${enhancements === 1 ? "" : "s"}`);
|
|
3351
|
+
if (designs) parts.push(`${designs} design`);
|
|
3312
3352
|
if (verified) parts.push(`${verified} verified`);
|
|
3313
3353
|
if (awaiting) parts.push(`${awaiting} awaiting re-test`);
|
|
3354
|
+
if (secondRound) parts.push(`${secondRound} back for a second round`);
|
|
3314
3355
|
parts.push(`${pages.size} page${pages.size === 1 ? "" : "s"}`);
|
|
3315
3356
|
if (Number.isFinite(earliest) && latest > earliest) {
|
|
3316
3357
|
const minutes = Math.max(1, Math.round((latest - earliest) / 6e4));
|
|
@@ -3339,6 +3380,45 @@ Exported: ${stamp}
|
|
|
3339
3380
|
Points: ${noteCount}
|
|
3340
3381
|
|
|
3341
3382
|
**Session:** ${summariseSession(notes, stamp)}`
|
|
3383
|
+
);
|
|
3384
|
+
sections.push(
|
|
3385
|
+
`## What the tags mean
|
|
3386
|
+
|
|
3387
|
+
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.
|
|
3388
|
+
|
|
3389
|
+
### Bug \u2014 something is broken
|
|
3390
|
+
|
|
3391
|
+
Find the **root cause**, not the symptom. Do not patch over it.
|
|
3392
|
+
|
|
3393
|
+
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.
|
|
3394
|
+
|
|
3395
|
+
**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.
|
|
3396
|
+
|
|
3397
|
+
### Design \u2014 it works, but it looks or feels wrong
|
|
3398
|
+
|
|
3399
|
+
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.
|
|
3400
|
+
|
|
3401
|
+
### Enhance \u2014 a new idea, thought of while using the app
|
|
3402
|
+
|
|
3403
|
+
The tester wants something added or changed: a new section, a new feature, a different way of doing it.
|
|
3404
|
+
|
|
3405
|
+
**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.
|
|
3406
|
+
|
|
3407
|
+
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.
|
|
3408
|
+
|
|
3409
|
+
### A point with a "round 2" on it
|
|
3410
|
+
|
|
3411
|
+
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.
|
|
3412
|
+
|
|
3413
|
+
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.
|
|
3414
|
+
|
|
3415
|
+
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.
|
|
3416
|
+
|
|
3417
|
+
### When the tag and the words disagree
|
|
3418
|
+
|
|
3419
|
+
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.
|
|
3420
|
+
|
|
3421
|
+
And if a point is phrased as a question rather than a report, **answer it \u2014 do not change code on a maybe.**`
|
|
3342
3422
|
);
|
|
3343
3423
|
const stack = typeof p.stack === "string" && p.stack.trim() ? p.stack.trim() : "(not provided)";
|
|
3344
3424
|
const runArr = toStrings(p.runCommands);
|
|
@@ -3934,12 +4014,12 @@ function campaignJsonText(allNotes) {
|
|
|
3934
4014
|
function reportMarkdownText(allNotes) {
|
|
3935
4015
|
if (!campaign) return "";
|
|
3936
4016
|
const ordered = orderedKnownNotes(allNotes);
|
|
3937
|
-
const counts = { bug: 0,
|
|
4017
|
+
const counts = { bug: 0, design: 0, enhance: 0, verified: 0 };
|
|
3938
4018
|
for (const n of ordered) {
|
|
3939
4019
|
const sev = n.severity ?? "bug";
|
|
3940
4020
|
if (sev === "bug") counts.bug++;
|
|
3941
|
-
else if (sev === "
|
|
3942
|
-
else counts.
|
|
4021
|
+
else if (sev === "enhance") counts.enhance++;
|
|
4022
|
+
else counts.design++;
|
|
3943
4023
|
if (n.status === "verified") counts.verified++;
|
|
3944
4024
|
}
|
|
3945
4025
|
const header = [
|
|
@@ -3948,7 +4028,7 @@ function reportMarkdownText(allNotes) {
|
|
|
3948
4028
|
campaign.tester ? `Tester: ${campaign.tester} ` : "",
|
|
3949
4029
|
`Started: ${campaign.startedAt} `,
|
|
3950
4030
|
`Updated: ${(/* @__PURE__ */ new Date()).toISOString()} `,
|
|
3951
|
-
`Points: ${ordered.length} (${counts.bug} bug, ${counts.
|
|
4031
|
+
`Points: ${ordered.length} (${counts.bug} bug, ${counts.design} design, ${counts.enhance} enhance \xB7 ${counts.verified} verified)`,
|
|
3952
4032
|
"",
|
|
3953
4033
|
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",
|
|
3954
4034
|
"`screenshots/`, one Markdown file per point in `notes/`.",
|
|
@@ -4300,6 +4380,12 @@ function QaProvider({
|
|
|
4300
4380
|
for (const id of pendingIds) void idb.delete(id);
|
|
4301
4381
|
storage.setJSON(PENDING_DELETE_KEY, []);
|
|
4302
4382
|
}
|
|
4383
|
+
live = live.map((n) => {
|
|
4384
|
+
const legacy = n.severity;
|
|
4385
|
+
if (legacy === "question") return { ...n, severity: "enhance" };
|
|
4386
|
+
if (legacy === "polish") return { ...n, severity: "design" };
|
|
4387
|
+
return n;
|
|
4388
|
+
});
|
|
4303
4389
|
const sorted = live.slice().sort(
|
|
4304
4390
|
(a, b) => a.timestamp < b.timestamp ? 1 : -1
|
|
4305
4391
|
);
|
|
@@ -4572,6 +4658,11 @@ function QaProvider({
|
|
|
4572
4658
|
}
|
|
4573
4659
|
if (patch.severity !== void 0) updated.severity = patch.severity;
|
|
4574
4660
|
if (patch.status !== void 0) updated.status = patch.status;
|
|
4661
|
+
if (patch.followUp !== void 0) {
|
|
4662
|
+
const text = patch.followUp.trim();
|
|
4663
|
+
updated.followUp = text || void 0;
|
|
4664
|
+
updated.followUpAt = text ? nowIso() : void 0;
|
|
4665
|
+
}
|
|
4575
4666
|
applyNotes((prev) => prev.map((n) => n.id === id ? updated : n));
|
|
4576
4667
|
const persisted = await idb.put(updated);
|
|
4577
4668
|
if (!persisted) {
|
|
@@ -4898,8 +4989,8 @@ function QaProvider({
|
|
|
4898
4989
|
const counts = {
|
|
4899
4990
|
all: notes.length,
|
|
4900
4991
|
bug: 0,
|
|
4901
|
-
|
|
4902
|
-
|
|
4992
|
+
design: 0,
|
|
4993
|
+
enhance: 0,
|
|
4903
4994
|
open: 0,
|
|
4904
4995
|
fixed: 0,
|
|
4905
4996
|
verified: 0,
|
|
@@ -4908,8 +4999,8 @@ function QaProvider({
|
|
|
4908
4999
|
for (const n of notes) {
|
|
4909
5000
|
const sev = n.severity ?? "bug";
|
|
4910
5001
|
if (sev === "bug") counts.bug++;
|
|
4911
|
-
else if (sev === "
|
|
4912
|
-
else counts.
|
|
5002
|
+
else if (sev === "enhance") counts.enhance++;
|
|
5003
|
+
else counts.design++;
|
|
4913
5004
|
const status = n.status ?? "open";
|
|
4914
5005
|
if (status === "verified") counts.verified++;
|
|
4915
5006
|
else if (status === "fixed") counts.fixed++;
|
|
@@ -5298,7 +5389,11 @@ function QaProvider({
|
|
|
5298
5389
|
pendingShare,
|
|
5299
5390
|
sharePending,
|
|
5300
5391
|
retestNote,
|
|
5301
|
-
|
|
5392
|
+
// The greeting explains how to capture. Once a note exists, the tester has
|
|
5393
|
+
// either done it or found their own way, so the line has nothing left to
|
|
5394
|
+
// say — take it away rather than making them dismiss it. (The stored flag
|
|
5395
|
+
// still governs a first visit; this only hides it early.)
|
|
5396
|
+
showWelcome: showWelcome && notes.length === 0,
|
|
5302
5397
|
dismissWelcome,
|
|
5303
5398
|
// v0.6
|
|
5304
5399
|
panelSide,
|
|
@@ -5751,8 +5846,8 @@ function QaFab() {
|
|
|
5751
5846
|
}
|
|
5752
5847
|
var SEVERITIES = [
|
|
5753
5848
|
{ value: "bug", labelKey: "sev_bug", icon: "Bug" },
|
|
5754
|
-
{ value: "
|
|
5755
|
-
{ value: "
|
|
5849
|
+
{ value: "enhance", labelKey: "sev_enhance" },
|
|
5850
|
+
{ value: "design", labelKey: "sev_design" }
|
|
5756
5851
|
];
|
|
5757
5852
|
function SeverityChipRow({
|
|
5758
5853
|
value,
|
|
@@ -6497,13 +6592,13 @@ function KindBadge({
|
|
|
6497
6592
|
}
|
|
6498
6593
|
var SEVERITY_CLASS = {
|
|
6499
6594
|
bug: "qa-bg-danger-tint qa-text-danger",
|
|
6500
|
-
|
|
6501
|
-
|
|
6595
|
+
enhance: "qa-bg-warn-tint qa-text-warn",
|
|
6596
|
+
design: "qa-bg-accent-tint qa-text-accent"
|
|
6502
6597
|
};
|
|
6503
6598
|
var SEVERITY_LABEL_KEY = {
|
|
6504
6599
|
bug: "sev_bug",
|
|
6505
|
-
|
|
6506
|
-
|
|
6600
|
+
enhance: "sev_enhance",
|
|
6601
|
+
design: "sev_design"
|
|
6507
6602
|
};
|
|
6508
6603
|
function SeverityChip({ severity, t }) {
|
|
6509
6604
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -6563,6 +6658,8 @@ function NoteItem({
|
|
|
6563
6658
|
const thumbUrl = useObjectUrl(editing ? img ?? void 0 : note.screenshot);
|
|
6564
6659
|
const afterUrl = useObjectUrl(note.afterScreenshot);
|
|
6565
6660
|
const [retesting, setRetesting] = React.useState(false);
|
|
6661
|
+
const [editingFollowUp, setEditingFollowUp] = React.useState(false);
|
|
6662
|
+
const [followUpDraft, setFollowUpDraft] = React.useState(note.followUp ?? "");
|
|
6566
6663
|
const [open, setOpen] = React.useState(false);
|
|
6567
6664
|
const severity = note.severity ?? "bug";
|
|
6568
6665
|
const status = note.status ?? "open";
|
|
@@ -6608,7 +6705,7 @@ function NoteItem({
|
|
|
6608
6705
|
notify(t("copy_failed"), { tone: "error" });
|
|
6609
6706
|
}
|
|
6610
6707
|
};
|
|
6611
|
-
const severityDot = severity === "bug" ? "var(--qa-danger)" : severity === "
|
|
6708
|
+
const severityDot = severity === "bug" ? "var(--qa-danger)" : severity === "enhance" ? "var(--qa-warn)" : "var(--qa-accent)";
|
|
6612
6709
|
if (dense && !open && !editing) {
|
|
6613
6710
|
return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6614
6711
|
"button",
|
|
@@ -6896,7 +6993,96 @@ function NoteItem({
|
|
|
6896
6993
|
style: { borderColor: "var(--qa-success)" }
|
|
6897
6994
|
}
|
|
6898
6995
|
)
|
|
6899
|
-
] })
|
|
6996
|
+
] }),
|
|
6997
|
+
(status === "fixed" || note.followUp) && /* @__PURE__ */ jsxRuntime.jsx(
|
|
6998
|
+
"div",
|
|
6999
|
+
{
|
|
7000
|
+
className: "qa-mt-2 qa-rounded-lg qa-border qa-border-subtle qa-bg-2 qa-p-2",
|
|
7001
|
+
"data-qa-followup": "true",
|
|
7002
|
+
children: editingFollowUp ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7003
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "qa-text-10 qa-text-lo", htmlFor: `qa-fu-${note.id}`, children: t("followup_label") }),
|
|
7004
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7005
|
+
"textarea",
|
|
7006
|
+
{
|
|
7007
|
+
id: `qa-fu-${note.id}`,
|
|
7008
|
+
value: followUpDraft,
|
|
7009
|
+
onChange: (e) => setFollowUpDraft(e.target.value),
|
|
7010
|
+
placeholder: t("followup_placeholder"),
|
|
7011
|
+
rows: 3,
|
|
7012
|
+
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",
|
|
7013
|
+
style: { outline: "none", resize: "vertical" }
|
|
7014
|
+
}
|
|
7015
|
+
),
|
|
7016
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "qa-mt-1 qa-text-10 qa-text-lo", children: t("followup_hint") }),
|
|
7017
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "qa-mt-1.5 qa-flex qa-gap-2", children: [
|
|
7018
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
7019
|
+
"button",
|
|
7020
|
+
{
|
|
7021
|
+
type: "button",
|
|
7022
|
+
onClick: () => {
|
|
7023
|
+
void updateNote(note.id, { followUp: followUpDraft });
|
|
7024
|
+
setEditingFollowUp(false);
|
|
7025
|
+
},
|
|
7026
|
+
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",
|
|
7027
|
+
style: { border: "none", cursor: "pointer" },
|
|
7028
|
+
children: [
|
|
7029
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "Check", size: 12 }),
|
|
7030
|
+
t("save")
|
|
7031
|
+
]
|
|
7032
|
+
}
|
|
7033
|
+
),
|
|
7034
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7035
|
+
"button",
|
|
7036
|
+
{
|
|
7037
|
+
type: "button",
|
|
7038
|
+
onClick: () => {
|
|
7039
|
+
setFollowUpDraft(note.followUp ?? "");
|
|
7040
|
+
setEditingFollowUp(false);
|
|
7041
|
+
},
|
|
7042
|
+
className: "qa-tap qa-rounded-lg qa-border qa-border-subtle qa-px-2 qa-py-1 qa-text-11 qa-text-mid",
|
|
7043
|
+
style: { background: "transparent", cursor: "pointer" },
|
|
7044
|
+
children: t("cancel")
|
|
7045
|
+
}
|
|
7046
|
+
)
|
|
7047
|
+
] })
|
|
7048
|
+
] }) : note.followUp ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7049
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "qa-flex qa-items-center qa-gap-1", children: [
|
|
7050
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "RotateCcw", size: 11, className: "qa-shrink-0 qa-text-warn" }),
|
|
7051
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "qa-text-10 qa-text-warn", children: t("followup_label") }),
|
|
7052
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7053
|
+
"button",
|
|
7054
|
+
{
|
|
7055
|
+
type: "button",
|
|
7056
|
+
onClick: () => {
|
|
7057
|
+
setFollowUpDraft(note.followUp ?? "");
|
|
7058
|
+
setEditingFollowUp(true);
|
|
7059
|
+
},
|
|
7060
|
+
"aria-label": t("edit"),
|
|
7061
|
+
className: "qa-tap-icon qa-ms-auto qa-text-lo",
|
|
7062
|
+
style: { background: "transparent", border: "none", cursor: "pointer" },
|
|
7063
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "Pencil", size: 11 })
|
|
7064
|
+
}
|
|
7065
|
+
)
|
|
7066
|
+
] }),
|
|
7067
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "qa-mt-1 qa-whitespace-pre-wrap qa-break-words qa-text-xs qa-text-hi", children: note.followUp })
|
|
7068
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7069
|
+
"button",
|
|
7070
|
+
{
|
|
7071
|
+
type: "button",
|
|
7072
|
+
onClick: () => {
|
|
7073
|
+
setFollowUpDraft("");
|
|
7074
|
+
setEditingFollowUp(true);
|
|
7075
|
+
},
|
|
7076
|
+
className: "qa-tap qa-inline-flex qa-items-center qa-gap-1 qa-text-11 qa-font-medium qa-text-accent",
|
|
7077
|
+
style: { background: "transparent", border: "none", cursor: "pointer" },
|
|
7078
|
+
children: [
|
|
7079
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "Plus", size: 12 }),
|
|
7080
|
+
t("followup_add")
|
|
7081
|
+
]
|
|
7082
|
+
}
|
|
7083
|
+
)
|
|
7084
|
+
}
|
|
7085
|
+
)
|
|
6900
7086
|
] })
|
|
6901
7087
|
]
|
|
6902
7088
|
}
|
|
@@ -7129,8 +7315,11 @@ function FilterChip({ chip }) {
|
|
|
7129
7315
|
);
|
|
7130
7316
|
}
|
|
7131
7317
|
function NoteFilterBar() {
|
|
7132
|
-
const { filter, setFilter, noteCounts, notes, t } = useQa();
|
|
7318
|
+
const { filter, setFilter, noteCounts, notes, deleteNotes, t } = useQa();
|
|
7319
|
+
const [confirmPrune, setConfirmPrune] = React.useState(false);
|
|
7133
7320
|
if (notes.length < 3) return null;
|
|
7321
|
+
const others = notes.filter((n) => (n.status ?? "open") !== "fixed");
|
|
7322
|
+
const showPrune = filter.status === "fixed" && noteCounts.fixed > 0 && others.length > 0;
|
|
7134
7323
|
const sev = (key) => () => setFilter({ severity: filter.severity === key ? "all" : key });
|
|
7135
7324
|
const stat = (key) => () => setFilter({ status: filter.status === key ? "all" : key });
|
|
7136
7325
|
const chips = [
|
|
@@ -7151,22 +7340,22 @@ function NoteFilterBar() {
|
|
|
7151
7340
|
onClick: sev("bug")
|
|
7152
7341
|
},
|
|
7153
7342
|
{
|
|
7154
|
-
key: "
|
|
7155
|
-
label: t("
|
|
7156
|
-
count: noteCounts.
|
|
7157
|
-
active: filter.severity === "
|
|
7158
|
-
icon: "
|
|
7343
|
+
key: "enhance",
|
|
7344
|
+
label: t("sev_enhance"),
|
|
7345
|
+
count: noteCounts.enhance,
|
|
7346
|
+
active: filter.severity === "enhance",
|
|
7347
|
+
icon: "Plus",
|
|
7159
7348
|
tone: "warn",
|
|
7160
|
-
onClick: sev("
|
|
7349
|
+
onClick: sev("enhance")
|
|
7161
7350
|
},
|
|
7162
7351
|
{
|
|
7163
|
-
key: "
|
|
7164
|
-
label: t("
|
|
7165
|
-
count: noteCounts.
|
|
7166
|
-
active: filter.severity === "
|
|
7352
|
+
key: "design",
|
|
7353
|
+
label: t("sev_design"),
|
|
7354
|
+
count: noteCounts.design,
|
|
7355
|
+
active: filter.severity === "design",
|
|
7167
7356
|
icon: "Pencil",
|
|
7168
7357
|
tone: "accent",
|
|
7169
|
-
onClick: sev("
|
|
7358
|
+
onClick: sev("design")
|
|
7170
7359
|
},
|
|
7171
7360
|
{
|
|
7172
7361
|
key: "open",
|
|
@@ -7235,7 +7424,48 @@ function NoteFilterBar() {
|
|
|
7235
7424
|
]
|
|
7236
7425
|
}
|
|
7237
7426
|
),
|
|
7238
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "qa-flex qa-flex-wrap qa-gap-1", children: chips.map((chip) => /* @__PURE__ */ jsxRuntime.jsx(FilterChip, { chip }, chip.key)) })
|
|
7427
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "qa-flex qa-flex-wrap qa-gap-1", children: chips.map((chip) => /* @__PURE__ */ jsxRuntime.jsx(FilterChip, { chip }, chip.key)) }),
|
|
7428
|
+
showPrune && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "qa-text-11", "data-qa-prune": "true", children: confirmPrune ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "qa-text-mid", children: [
|
|
7429
|
+
t("keep_only_retest_q", { n: others.length }),
|
|
7430
|
+
" ",
|
|
7431
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7432
|
+
"button",
|
|
7433
|
+
{
|
|
7434
|
+
type: "button",
|
|
7435
|
+
onClick: () => {
|
|
7436
|
+
void deleteNotes(others.map((n) => n.id));
|
|
7437
|
+
setConfirmPrune(false);
|
|
7438
|
+
},
|
|
7439
|
+
className: "qa-font-semibold qa-text-danger qa-tap",
|
|
7440
|
+
style: { background: "transparent", border: "none", cursor: "pointer" },
|
|
7441
|
+
children: t("yes")
|
|
7442
|
+
}
|
|
7443
|
+
),
|
|
7444
|
+
" / ",
|
|
7445
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7446
|
+
"button",
|
|
7447
|
+
{
|
|
7448
|
+
type: "button",
|
|
7449
|
+
onClick: () => setConfirmPrune(false),
|
|
7450
|
+
className: "qa-text-accent qa-tap",
|
|
7451
|
+
style: { background: "transparent", border: "none", cursor: "pointer" },
|
|
7452
|
+
children: t("no")
|
|
7453
|
+
}
|
|
7454
|
+
)
|
|
7455
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7456
|
+
"button",
|
|
7457
|
+
{
|
|
7458
|
+
type: "button",
|
|
7459
|
+
onClick: () => setConfirmPrune(true),
|
|
7460
|
+
title: t("keep_only_retest_hint"),
|
|
7461
|
+
className: "qa-tap qa-inline-flex qa-items-center qa-gap-1 qa-text-lo",
|
|
7462
|
+
style: { background: "transparent", border: "none", cursor: "pointer" },
|
|
7463
|
+
children: [
|
|
7464
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "Trash", size: 11 }),
|
|
7465
|
+
t("keep_only_retest", { n: noteCounts.fixed })
|
|
7466
|
+
]
|
|
7467
|
+
}
|
|
7468
|
+
) })
|
|
7239
7469
|
] });
|
|
7240
7470
|
}
|
|
7241
7471
|
function Section({
|
|
@@ -7605,46 +7835,25 @@ function SettingsSheet({ onClose }) {
|
|
|
7605
7835
|
);
|
|
7606
7836
|
}
|
|
7607
7837
|
function WelcomeCard() {
|
|
7608
|
-
const { t,
|
|
7838
|
+
const { t, dismissWelcome } = useQa();
|
|
7609
7839
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7610
7840
|
"div",
|
|
7611
7841
|
{
|
|
7612
|
-
className: "qa-rounded-xl qa-border qa-border-subtle qa-bg-1 qa-
|
|
7842
|
+
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",
|
|
7613
7843
|
role: "note",
|
|
7614
7844
|
children: [
|
|
7615
|
-
/* @__PURE__ */ jsxRuntime.
|
|
7616
|
-
|
|
7617
|
-
"span",
|
|
7618
|
-
{
|
|
7619
|
-
"aria-hidden": "true",
|
|
7620
|
-
className: "qa-shrink-0",
|
|
7621
|
-
style: { width: 6, height: 6, background: "var(--qa-accent)" }
|
|
7622
|
-
}
|
|
7623
|
-
),
|
|
7624
|
-
t("welcome_title", { brand: brand.label })
|
|
7625
|
-
] }),
|
|
7626
|
-
/* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "qa-m-0 qa-space-y-1 qa-text-xs qa-text-mid", children: [
|
|
7627
|
-
/* @__PURE__ */ jsxRuntime.jsxs("li", { className: "qa-flex qa-items-start qa-gap-1.5", children: [
|
|
7628
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "Crosshair", size: 12, className: "qa-shrink-0 qa-mt-1" }),
|
|
7629
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: t("welcome_capture") })
|
|
7630
|
-
] }),
|
|
7631
|
-
/* @__PURE__ */ jsxRuntime.jsxs("li", { className: "qa-flex qa-items-start qa-gap-1.5", children: [
|
|
7632
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "Pencil", size: 12, className: "qa-shrink-0 qa-mt-1" }),
|
|
7633
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: t("welcome_say") })
|
|
7634
|
-
] }),
|
|
7635
|
-
/* @__PURE__ */ jsxRuntime.jsxs("li", { className: "qa-flex qa-items-start qa-gap-1.5", children: [
|
|
7636
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "CheckCircle2", size: 12, className: "qa-shrink-0 qa-mt-1" }),
|
|
7637
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: t("welcome_safe") })
|
|
7638
|
-
] })
|
|
7639
|
-
] }),
|
|
7845
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "Crosshair", size: 13, className: "qa-shrink-0 qa-text-accent" }),
|
|
7846
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "qa-flex-1 qa-text-xs qa-text-mid", children: t("welcome_capture") }),
|
|
7640
7847
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7641
7848
|
"button",
|
|
7642
7849
|
{
|
|
7643
7850
|
type: "button",
|
|
7644
7851
|
onClick: dismissWelcome,
|
|
7645
|
-
|
|
7646
|
-
|
|
7647
|
-
|
|
7852
|
+
"aria-label": t("welcome_got_it"),
|
|
7853
|
+
title: t("welcome_got_it"),
|
|
7854
|
+
className: "qa-tap-icon qa-shrink-0 qa-rounded-full qa-text-mid qa-focus-ring",
|
|
7855
|
+
style: { background: "transparent", border: "none", cursor: "pointer", padding: 2 },
|
|
7856
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "X", size: 13 })
|
|
7648
7857
|
}
|
|
7649
7858
|
)
|
|
7650
7859
|
]
|
|
@@ -7792,249 +8001,9 @@ function CredentialsSection() {
|
|
|
7792
8001
|
})
|
|
7793
8002
|
] });
|
|
7794
8003
|
}
|
|
7795
|
-
|
|
7796
|
-
|
|
7797
|
-
|
|
7798
|
-
group,
|
|
7799
|
-
checked,
|
|
7800
|
-
toggle,
|
|
7801
|
-
pick: pick2,
|
|
7802
|
-
onWalkFrom
|
|
7803
|
-
}) {
|
|
7804
|
-
const { lang, t, guideFailed, evidenceByStep } = useQa();
|
|
7805
|
-
const { id, color = DEFAULT_LANE_COLOR, steps: steps2 } = group;
|
|
7806
|
-
const done = steps2.filter((s) => checked.has(keyOf(id, s.path))).length;
|
|
7807
|
-
const pct = steps2.length > 0 ? Math.round(done / steps2.length * 100) : 0;
|
|
7808
|
-
const uncoveredRedCount = steps2.filter(
|
|
7809
|
-
(s) => s.risk === "red" && !checked.has(keyOf(id, s.path))
|
|
7810
|
-
).length;
|
|
7811
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "qa-rounded-xl qa-border qa-border-subtle qa-bg-2 qa-p-3 qa-elev-1", children: [
|
|
7812
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "qa-mb-2 qa-flex qa-items-center qa-gap-2", children: [
|
|
7813
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7814
|
-
"span",
|
|
7815
|
-
{
|
|
7816
|
-
className: "qa-h-2.5 qa-w-2.5 qa-rounded-full",
|
|
7817
|
-
style: { background: color }
|
|
7818
|
-
}
|
|
7819
|
-
),
|
|
7820
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "qa-text-sm qa-font-bold qa-text-hi", children: pick2(group.role) }),
|
|
7821
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "qa-ms-auto qa-text-11 qa-font-medium qa-text-slate-400", children: [
|
|
7822
|
-
done,
|
|
7823
|
-
"/",
|
|
7824
|
-
steps2.length
|
|
7825
|
-
] }),
|
|
7826
|
-
uncoveredRedCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7827
|
-
"span",
|
|
7828
|
-
{
|
|
7829
|
-
className: "qa-bg-danger-tint qa-text-danger qa-rounded qa-px-1 qa-text-10 qa-font-medium",
|
|
7830
|
-
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)`,
|
|
7831
|
-
children: lang === "ar" ? `\u0623\u062D\u0645\u0631: ${uncoveredRedCount}` : `red: ${uncoveredRedCount}`
|
|
7832
|
-
}
|
|
7833
|
-
)
|
|
7834
|
-
] }),
|
|
7835
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7836
|
-
"div",
|
|
7837
|
-
{
|
|
7838
|
-
className: "qa-mb-3 qa-h-1.5 qa-overflow-hidden qa-rounded-full",
|
|
7839
|
-
style: { background: `${color}22` },
|
|
7840
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
7841
|
-
"div",
|
|
7842
|
-
{
|
|
7843
|
-
className: "qa-h-full qa-rounded-full qa-transition-all",
|
|
7844
|
-
style: { width: `${pct}%`, background: color }
|
|
7845
|
-
}
|
|
7846
|
-
)
|
|
7847
|
-
}
|
|
7848
|
-
),
|
|
7849
|
-
/* @__PURE__ */ jsxRuntime.jsxs("ol", { className: "qa-relative qa-ms-1.5", children: [
|
|
7850
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7851
|
-
"span",
|
|
7852
|
-
{
|
|
7853
|
-
className: "qa-absolute qa-top-1 qa-bottom-0 qa-w-px",
|
|
7854
|
-
style: { insetInlineStart: "7px", background: `${color}40`, bottom: "4px" }
|
|
7855
|
-
}
|
|
7856
|
-
),
|
|
7857
|
-
steps2.map((s, i) => {
|
|
7858
|
-
const k = keyOf(id, s.path);
|
|
7859
|
-
const on = checked.has(k);
|
|
7860
|
-
const failed = guideFailed.has(k);
|
|
7861
|
-
const evidence = evidenceByStep.get(k);
|
|
7862
|
-
const evidenceCount = evidence ? evidence.length : 0;
|
|
7863
|
-
const riskColor = s.risk ? RISK_COLORS[s.risk] : RISK_COLORS.none;
|
|
7864
|
-
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;
|
|
7865
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "qa-relative qa-mb-2 qa-last-mb-0", children: [
|
|
7866
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7867
|
-
"button",
|
|
7868
|
-
{
|
|
7869
|
-
onClick: () => onWalkFrom(k),
|
|
7870
|
-
title: t("walk_take_me", { path: s.path }),
|
|
7871
|
-
"aria-label": t("walk_take_me", { path: s.path }),
|
|
7872
|
-
className: "qa-tap-icon qa-absolute qa-rounded-lg qa-text-mid qa-hover-text-slate-600",
|
|
7873
|
-
style: {
|
|
7874
|
-
top: 0,
|
|
7875
|
-
insetInlineEnd: 0,
|
|
7876
|
-
background: "transparent",
|
|
7877
|
-
border: "none",
|
|
7878
|
-
cursor: "pointer",
|
|
7879
|
-
zIndex: 2
|
|
7880
|
-
},
|
|
7881
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "Play", size: 12 })
|
|
7882
|
-
}
|
|
7883
|
-
),
|
|
7884
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
7885
|
-
"button",
|
|
7886
|
-
{
|
|
7887
|
-
onClick: () => toggle(k),
|
|
7888
|
-
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" : ""}`,
|
|
7889
|
-
style: { background: failed ? void 0 : "transparent", border: "none", cursor: "pointer" },
|
|
7890
|
-
children: [
|
|
7891
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
7892
|
-
"span",
|
|
7893
|
-
{
|
|
7894
|
-
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",
|
|
7895
|
-
style: {
|
|
7896
|
-
borderColor: failed ? "var(--qa-danger)" : color,
|
|
7897
|
-
background: on ? color : failed ? "var(--qa-danger-tint)" : "var(--qa-surface-1)",
|
|
7898
|
-
zIndex: 1
|
|
7899
|
-
},
|
|
7900
|
-
children: [
|
|
7901
|
-
on && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "Check", size: 10, strokeWidth: 3, className: "qa-text-white" }),
|
|
7902
|
-
!on && failed && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "AlertTriangle", size: 9, strokeWidth: 2.5, className: "qa-text-danger" })
|
|
7903
|
-
]
|
|
7904
|
-
}
|
|
7905
|
-
),
|
|
7906
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "qa-min-w-0", children: [
|
|
7907
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "qa-flex qa-items-center qa-gap-1", children: [
|
|
7908
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7909
|
-
"code",
|
|
7910
|
-
{
|
|
7911
|
-
className: "qa-rounded qa-px-1 qa-text-11 qa-font-semibold qa-dir-ltr qa-text-hi",
|
|
7912
|
-
style: {
|
|
7913
|
-
background: failed ? "var(--qa-danger-tint)" : `${color}14`,
|
|
7914
|
-
textDecoration: on ? "line-through" : "none",
|
|
7915
|
-
opacity: on ? 0.55 : 1
|
|
7916
|
-
},
|
|
7917
|
-
children: s.path
|
|
7918
|
-
}
|
|
7919
|
-
),
|
|
7920
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7921
|
-
"span",
|
|
7922
|
-
{
|
|
7923
|
-
className: "qa-inline-block qa-rounded-full qa-shrink-0",
|
|
7924
|
-
style: {
|
|
7925
|
-
width: "6px",
|
|
7926
|
-
height: "6px",
|
|
7927
|
-
background: riskColor,
|
|
7928
|
-
flexShrink: 0
|
|
7929
|
-
},
|
|
7930
|
-
title: dotTitle
|
|
7931
|
-
}
|
|
7932
|
-
)
|
|
7933
|
-
] }),
|
|
7934
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7935
|
-
"span",
|
|
7936
|
-
{
|
|
7937
|
-
className: "qa-mt-0.5 qa-block qa-text-11 qa-leading-relaxed qa-text-slate-500",
|
|
7938
|
-
style: { opacity: on ? 0.5 : 1 },
|
|
7939
|
-
children: pick2(s.what)
|
|
7940
|
-
}
|
|
7941
|
-
),
|
|
7942
|
-
evidenceCount > 0 && /* @__PURE__ */ jsxRuntime.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 }) }),
|
|
7943
|
-
evidenceCount === 0 && on && /* @__PURE__ */ jsxRuntime.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") })
|
|
7944
|
-
] })
|
|
7945
|
-
]
|
|
7946
|
-
}
|
|
7947
|
-
)
|
|
7948
|
-
] }, `${k}-${i}`);
|
|
7949
|
-
})
|
|
7950
|
-
] })
|
|
7951
|
-
] });
|
|
7952
|
-
}
|
|
7953
|
-
function GuideSection() {
|
|
7954
|
-
const {
|
|
7955
|
-
guideChecked,
|
|
7956
|
-
toggleGuide,
|
|
7957
|
-
t,
|
|
7958
|
-
journey,
|
|
7959
|
-
journeyIsGeneric,
|
|
7960
|
-
pick: pick2,
|
|
7961
|
-
lang,
|
|
7962
|
-
startTestAlong,
|
|
7963
|
-
startWalk,
|
|
7964
|
-
testAlongSteps
|
|
7965
|
-
} = useQa();
|
|
7966
|
-
const all = journey.flatMap((g) => g.steps.map((s) => keyOf(g.id, s.path)));
|
|
7967
|
-
const done = all.filter((k) => guideChecked.has(k)).length;
|
|
7968
|
-
const pct = all.length > 0 ? Math.round(done / all.length * 100) : 0;
|
|
7969
|
-
const coverage = computeCoverage(journey, guideChecked);
|
|
7970
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "qa-space-y-3", children: [
|
|
7971
|
-
journeyIsGeneric && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7972
|
-
"p",
|
|
7973
|
-
{
|
|
7974
|
-
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",
|
|
7975
|
-
"data-qa-generic-plan": "true",
|
|
7976
|
-
children: t("journey_generic")
|
|
7977
|
-
}
|
|
7978
|
-
),
|
|
7979
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "qa-rounded-xl qa-border qa-border-accent qa-bg-accent-tint qa-p-3 qa-elev-1", children: [
|
|
7980
|
-
coverage.red.total > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "qa-mb-1 qa-flex qa-items-center qa-gap-1.5 qa-text-11", children: [
|
|
7981
|
-
/* @__PURE__ */ jsxRuntime.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" }),
|
|
7982
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "qa-dir-ltr qa-font-semibold qa-text-hi", children: [
|
|
7983
|
-
coverage.red.covered,
|
|
7984
|
-
"/",
|
|
7985
|
-
coverage.red.total,
|
|
7986
|
-
" ",
|
|
7987
|
-
lang === "ar" ? "\u0645\u063A\u0637\u0649" : "covered"
|
|
7988
|
-
] })
|
|
7989
|
-
] }),
|
|
7990
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "qa-flex qa-items-center qa-justify-between qa-text-sm qa-font-semibold qa-text-hi", children: [
|
|
7991
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: t("journey_title") }),
|
|
7992
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "qa-dir-ltr", children: [
|
|
7993
|
-
done,
|
|
7994
|
-
"/",
|
|
7995
|
-
all.length,
|
|
7996
|
-
" \xB7 ",
|
|
7997
|
-
pct,
|
|
7998
|
-
"%"
|
|
7999
|
-
] })
|
|
8000
|
-
] }),
|
|
8001
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "qa-mt-2 qa-h-2 qa-overflow-hidden qa-rounded-full qa-bg-3", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
8002
|
-
"div",
|
|
8003
|
-
{
|
|
8004
|
-
className: "qa-h-full qa-rounded-full qa-bg-accent qa-transition-all",
|
|
8005
|
-
style: { width: `${pct}%` }
|
|
8006
|
-
}
|
|
8007
|
-
) }),
|
|
8008
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "qa-mt-2 qa-flex qa-justify-end", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8009
|
-
"button",
|
|
8010
|
-
{
|
|
8011
|
-
type: "button",
|
|
8012
|
-
onClick: startTestAlong,
|
|
8013
|
-
disabled: all.length === 0,
|
|
8014
|
-
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",
|
|
8015
|
-
children: [
|
|
8016
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "Play", size: 13 }),
|
|
8017
|
-
t("start_walkthrough")
|
|
8018
|
-
]
|
|
8019
|
-
}
|
|
8020
|
-
) })
|
|
8021
|
-
] }),
|
|
8022
|
-
journey.map((g) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
8023
|
-
Lane,
|
|
8024
|
-
{
|
|
8025
|
-
group: g,
|
|
8026
|
-
checked: guideChecked,
|
|
8027
|
-
toggle: toggleGuide,
|
|
8028
|
-
onWalkFrom: (key) => {
|
|
8029
|
-
const at = testAlongSteps.findIndex((step) => step.key === key);
|
|
8030
|
-
startWalk("plan", at === -1 ? 0 : at);
|
|
8031
|
-
},
|
|
8032
|
-
pick: pick2
|
|
8033
|
-
},
|
|
8034
|
-
g.id
|
|
8035
|
-
))
|
|
8036
|
-
] });
|
|
8037
|
-
}
|
|
8004
|
+
|
|
8005
|
+
// src/lib/features.ts
|
|
8006
|
+
var GUIDE_TAB_ENABLED = false;
|
|
8038
8007
|
function isCoarsePointer() {
|
|
8039
8008
|
if (typeof window === "undefined") return false;
|
|
8040
8009
|
try {
|
|
@@ -8058,11 +8027,15 @@ function useCoarsePointer() {
|
|
|
8058
8027
|
}, []);
|
|
8059
8028
|
return coarse;
|
|
8060
8029
|
}
|
|
8061
|
-
var
|
|
8030
|
+
var ALL_TABS = [
|
|
8062
8031
|
{ key: "notes", labelKey: "tab_notes", icon: "StickyNote" },
|
|
8063
8032
|
{ key: "logins", labelKey: "tab_logins", icon: "KeyRound" },
|
|
8064
8033
|
{ key: "guide", labelKey: "tab_guide", icon: "Map" }
|
|
8065
8034
|
];
|
|
8035
|
+
var TABS = ALL_TABS.filter((tab) => tab.key !== "guide" || GUIDE_TAB_ENABLED);
|
|
8036
|
+
function visibleTab(tab) {
|
|
8037
|
+
return tab === "guide" && !GUIDE_TAB_ENABLED ? "notes" : tab;
|
|
8038
|
+
}
|
|
8066
8039
|
function todayName() {
|
|
8067
8040
|
return `qa-notes-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
|
|
8068
8041
|
}
|
|
@@ -8097,7 +8070,7 @@ var PANEL_TRANSITION_WITH_LIFT = "opacity 200ms cubic-bezier(0.4,0,0.2,1), trans
|
|
|
8097
8070
|
function QaPanel() {
|
|
8098
8071
|
const {
|
|
8099
8072
|
isOpen,
|
|
8100
|
-
activeTab,
|
|
8073
|
+
activeTab: storedTab,
|
|
8101
8074
|
setActiveTab,
|
|
8102
8075
|
notes,
|
|
8103
8076
|
exportZip,
|
|
@@ -8128,6 +8101,7 @@ function QaPanel() {
|
|
|
8128
8101
|
panelCollapsed,
|
|
8129
8102
|
setPanelCollapsed
|
|
8130
8103
|
} = useQa();
|
|
8104
|
+
const activeTab = visibleTab(storedTab);
|
|
8131
8105
|
const [confirmClear, setConfirmClear] = React.useState(false);
|
|
8132
8106
|
const [naming, setNaming] = React.useState(false);
|
|
8133
8107
|
const [filename, setFilename] = React.useState("");
|
|
@@ -8495,7 +8469,7 @@ function QaPanel() {
|
|
|
8495
8469
|
) })
|
|
8496
8470
|
] }),
|
|
8497
8471
|
!simpleMode && activeTab === "logins" && /* @__PURE__ */ jsxRuntime.jsx(CredentialsSection, {}),
|
|
8498
|
-
!simpleMode &&
|
|
8472
|
+
!simpleMode && GUIDE_TAB_ENABLED
|
|
8499
8473
|
] })
|
|
8500
8474
|
] }),
|
|
8501
8475
|
settingsOpen && /* @__PURE__ */ jsxRuntime.jsx(SettingsSheet, { onClose: () => setSettingsOpen(false) }),
|
|
@@ -9111,16 +9085,16 @@ function unlockPageScroll() {
|
|
|
9111
9085
|
var DRAG_THRESHOLD2 = 6;
|
|
9112
9086
|
var TOUCH_DRAG_THRESHOLD = 12;
|
|
9113
9087
|
var MIN_REGION_SIZE = 8;
|
|
9114
|
-
var SEVERITIES2 = ["bug", "
|
|
9088
|
+
var SEVERITIES2 = ["bug", "design", "enhance"];
|
|
9115
9089
|
var SEVERITY_ICON = {
|
|
9116
9090
|
bug: "Bug",
|
|
9117
|
-
|
|
9118
|
-
|
|
9091
|
+
design: "Pencil",
|
|
9092
|
+
enhance: "Plus"
|
|
9119
9093
|
};
|
|
9120
9094
|
var SEVERITY_LABEL_KEY2 = {
|
|
9121
9095
|
bug: "sev_bug",
|
|
9122
|
-
|
|
9123
|
-
|
|
9096
|
+
design: "sev_design",
|
|
9097
|
+
enhance: "sev_enhance"
|
|
9124
9098
|
};
|
|
9125
9099
|
function clampRegionRect(rect) {
|
|
9126
9100
|
const vw = typeof window !== "undefined" ? window.innerWidth : rect.left + rect.width;
|
|
@@ -10074,7 +10048,7 @@ function CaptureMode() {
|
|
|
10074
10048
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "qa-text-11 qa-text-mid qa-me-1", children: t("severity_label") }),
|
|
10075
10049
|
SEVERITIES2.map((sev) => {
|
|
10076
10050
|
const active = severity === sev;
|
|
10077
|
-
const toneClass = sev === "bug" ? "qa-bg-danger-tint qa-text-danger" : sev === "
|
|
10051
|
+
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";
|
|
10078
10052
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10079
10053
|
"button",
|
|
10080
10054
|
{
|
|
@@ -10354,5 +10328,5 @@ function Qapture({ config }) {
|
|
|
10354
10328
|
exports.Qapture = Qapture;
|
|
10355
10329
|
exports.deleteQaDatabase = deleteQaDatabase;
|
|
10356
10330
|
exports.initQaStudio = initQaStudio;
|
|
10357
|
-
//# sourceMappingURL=chunk-
|
|
10358
|
-
//# sourceMappingURL=chunk-
|
|
10331
|
+
//# sourceMappingURL=chunk-4LMLW73U.cjs.map
|
|
10332
|
+
//# sourceMappingURL=chunk-4LMLW73U.cjs.map
|