qapture2 0.7.6 → 0.7.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: "Severity",
1758
+ severity_label: "What is this?",
1759
1759
  sev_bug: "Bug",
1760
- sev_question: "Question",
1761
- sev_polish: "Polish",
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",
@@ -1953,10 +1953,10 @@ var STR = {
1953
1953
  copied: "\u062A\u0645 \u0627\u0644\u0646\u0633\u062E",
1954
1954
  copy_failed: "\u0641\u0634\u0644 \u0627\u0644\u0646\u0633\u062E",
1955
1955
  copy_prompt: "\u0646\u0633\u062E \u0643\u0645\u0648\u062C\u0651\u0647 \u0644\u0644\u0648\u0643\u064A\u0644",
1956
- severity_label: "\u0627\u0644\u0623\u0647\u0645\u064A\u0629",
1957
- sev_bug: "\u062E\u0644\u0644",
1958
- sev_question: "\u0633\u0624\u0627\u0644",
1959
- sev_polish: "\u062A\u062D\u0633\u064A\u0646",
1956
+ severity_label: "\u0645\u0627 \u0647\u0630\u0627\u061F",
1957
+ sev_bug: "Bug",
1958
+ sev_design: "Design",
1959
+ sev_enhance: "Enhance",
1960
1960
  status_open: "\u0645\u0641\u062A\u0648\u062D",
1961
1961
  status_verified: "\u062A\u0645 \u0627\u0644\u062A\u062D\u0642\u0642",
1962
1962
  context_attached: "{n} \u0645\u0646 \u0623\u062D\u062F\u0627\u062B \u0627\u0644\u062A\u0634\u063A\u064A\u0644 \u0645\u0631\u0641\u0642\u0629",
@@ -3277,15 +3277,15 @@ function fmtPct(n, d) {
3277
3277
  }
3278
3278
  function summariseSession(notes, stamp) {
3279
3279
  if (!notes.length) return "No points captured.";
3280
- let bugs = 0, questions = 0, polish = 0, verified = 0, awaiting = 0;
3280
+ let bugs = 0, enhancements = 0, designs = 0, verified = 0, awaiting = 0;
3281
3281
  const pages = /* @__PURE__ */ new Set();
3282
3282
  let earliest = Number.POSITIVE_INFINITY;
3283
3283
  let latest = 0;
3284
3284
  for (const n of notes) {
3285
3285
  const sev = n.severity ?? "bug";
3286
3286
  if (sev === "bug") bugs++;
3287
- else if (sev === "question") questions++;
3288
- else polish++;
3287
+ else if (sev === "enhance") enhancements++;
3288
+ else designs++;
3289
3289
  const status = n.status ?? "open";
3290
3290
  if (status === "verified") verified++;
3291
3291
  else if (status === "fixed") awaiting++;
@@ -3300,8 +3300,8 @@ function summariseSession(notes, stamp) {
3300
3300
  `${notes.length} point${notes.length === 1 ? "" : "s"}`,
3301
3301
  `${bugs} bug${bugs === 1 ? "" : "s"}`
3302
3302
  ];
3303
- if (questions) parts.push(`${questions} question${questions === 1 ? "" : "s"}`);
3304
- if (polish) parts.push(`${polish} polish`);
3303
+ if (enhancements) parts.push(`${enhancements} enhancement${enhancements === 1 ? "" : "s"}`);
3304
+ if (designs) parts.push(`${designs} design`);
3305
3305
  if (verified) parts.push(`${verified} verified`);
3306
3306
  if (awaiting) parts.push(`${awaiting} awaiting re-test`);
3307
3307
  parts.push(`${pages.size} page${pages.size === 1 ? "" : "s"}`);
@@ -3332,6 +3332,37 @@ Exported: ${stamp}
3332
3332
  Points: ${noteCount}
3333
3333
 
3334
3334
  **Session:** ${summariseSession(notes, stamp)}`
3335
+ );
3336
+ sections.push(
3337
+ `## What the tags mean
3338
+
3339
+ 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.
3340
+
3341
+ ### Bug \u2014 something is broken
3342
+
3343
+ Find the **root cause**, not the symptom. Do not patch over it.
3344
+
3345
+ 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.
3346
+
3347
+ **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.
3348
+
3349
+ ### Design \u2014 it works, but it looks or feels wrong
3350
+
3351
+ 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.
3352
+
3353
+ ### Enhance \u2014 a new idea, thought of while using the app
3354
+
3355
+ The tester wants something added or changed: a new section, a new feature, a different way of doing it.
3356
+
3357
+ **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.
3358
+
3359
+ 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.
3360
+
3361
+ ### When the tag and the words disagree
3362
+
3363
+ 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.
3364
+
3365
+ And if a point is phrased as a question rather than a report, **answer it \u2014 do not change code on a maybe.**`
3335
3366
  );
3336
3367
  const stack = typeof p.stack === "string" && p.stack.trim() ? p.stack.trim() : "(not provided)";
3337
3368
  const runArr = toStrings(p.runCommands);
@@ -3927,12 +3958,12 @@ function campaignJsonText(allNotes) {
3927
3958
  function reportMarkdownText(allNotes) {
3928
3959
  if (!campaign) return "";
3929
3960
  const ordered = orderedKnownNotes(allNotes);
3930
- const counts = { bug: 0, question: 0, polish: 0, verified: 0 };
3961
+ const counts = { bug: 0, design: 0, enhance: 0, verified: 0 };
3931
3962
  for (const n of ordered) {
3932
3963
  const sev = n.severity ?? "bug";
3933
3964
  if (sev === "bug") counts.bug++;
3934
- else if (sev === "question") counts.question++;
3935
- else counts.polish++;
3965
+ else if (sev === "enhance") counts.enhance++;
3966
+ else counts.design++;
3936
3967
  if (n.status === "verified") counts.verified++;
3937
3968
  }
3938
3969
  const header = [
@@ -3941,7 +3972,7 @@ function reportMarkdownText(allNotes) {
3941
3972
  campaign.tester ? `Tester: ${campaign.tester} ` : "",
3942
3973
  `Started: ${campaign.startedAt} `,
3943
3974
  `Updated: ${(/* @__PURE__ */ new Date()).toISOString()} `,
3944
- `Points: ${ordered.length} (${counts.bug} bug, ${counts.question} question, ${counts.polish} polish \xB7 ${counts.verified} verified)`,
3975
+ `Points: ${ordered.length} (${counts.bug} bug, ${counts.design} design, ${counts.enhance} enhance \xB7 ${counts.verified} verified)`,
3945
3976
  "",
3946
3977
  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
3978
  "`screenshots/`, one Markdown file per point in `notes/`.",
@@ -4293,6 +4324,12 @@ function QaProvider({
4293
4324
  for (const id of pendingIds) void idb.delete(id);
4294
4325
  storage.setJSON(PENDING_DELETE_KEY, []);
4295
4326
  }
4327
+ live = live.map((n) => {
4328
+ const legacy = n.severity;
4329
+ if (legacy === "question") return { ...n, severity: "enhance" };
4330
+ if (legacy === "polish") return { ...n, severity: "design" };
4331
+ return n;
4332
+ });
4296
4333
  const sorted = live.slice().sort(
4297
4334
  (a, b) => a.timestamp < b.timestamp ? 1 : -1
4298
4335
  );
@@ -4891,8 +4928,8 @@ function QaProvider({
4891
4928
  const counts = {
4892
4929
  all: notes.length,
4893
4930
  bug: 0,
4894
- question: 0,
4895
- polish: 0,
4931
+ design: 0,
4932
+ enhance: 0,
4896
4933
  open: 0,
4897
4934
  fixed: 0,
4898
4935
  verified: 0,
@@ -4901,8 +4938,8 @@ function QaProvider({
4901
4938
  for (const n of notes) {
4902
4939
  const sev = n.severity ?? "bug";
4903
4940
  if (sev === "bug") counts.bug++;
4904
- else if (sev === "question") counts.question++;
4905
- else counts.polish++;
4941
+ else if (sev === "enhance") counts.enhance++;
4942
+ else counts.design++;
4906
4943
  const status = n.status ?? "open";
4907
4944
  if (status === "verified") counts.verified++;
4908
4945
  else if (status === "fixed") counts.fixed++;
@@ -5291,7 +5328,11 @@ function QaProvider({
5291
5328
  pendingShare,
5292
5329
  sharePending,
5293
5330
  retestNote,
5294
- showWelcome,
5331
+ // The greeting explains how to capture. Once a note exists, the tester has
5332
+ // either done it or found their own way, so the line has nothing left to
5333
+ // say — take it away rather than making them dismiss it. (The stored flag
5334
+ // still governs a first visit; this only hides it early.)
5335
+ showWelcome: showWelcome && notes.length === 0,
5295
5336
  dismissWelcome,
5296
5337
  // v0.6
5297
5338
  panelSide,
@@ -5744,8 +5785,8 @@ function QaFab() {
5744
5785
  }
5745
5786
  var SEVERITIES = [
5746
5787
  { value: "bug", labelKey: "sev_bug", icon: "Bug" },
5747
- { value: "question", labelKey: "sev_question" },
5748
- { value: "polish", labelKey: "sev_polish" }
5788
+ { value: "enhance", labelKey: "sev_enhance" },
5789
+ { value: "design", labelKey: "sev_design" }
5749
5790
  ];
5750
5791
  function SeverityChipRow({
5751
5792
  value,
@@ -6490,13 +6531,13 @@ function KindBadge({
6490
6531
  }
6491
6532
  var SEVERITY_CLASS = {
6492
6533
  bug: "qa-bg-danger-tint qa-text-danger",
6493
- question: "qa-bg-warn-tint qa-text-warn",
6494
- polish: "qa-bg-accent-tint qa-text-accent"
6534
+ enhance: "qa-bg-warn-tint qa-text-warn",
6535
+ design: "qa-bg-accent-tint qa-text-accent"
6495
6536
  };
6496
6537
  var SEVERITY_LABEL_KEY = {
6497
6538
  bug: "sev_bug",
6498
- question: "sev_question",
6499
- polish: "sev_polish"
6539
+ enhance: "sev_enhance",
6540
+ design: "sev_design"
6500
6541
  };
6501
6542
  function SeverityChip({ severity, t }) {
6502
6543
  return /* @__PURE__ */ jsxs(
@@ -6601,7 +6642,7 @@ function NoteItem({
6601
6642
  notify(t("copy_failed"), { tone: "error" });
6602
6643
  }
6603
6644
  };
6604
- const severityDot = severity === "bug" ? "var(--qa-danger)" : severity === "question" ? "var(--qa-warn)" : "var(--qa-accent)";
6645
+ const severityDot = severity === "bug" ? "var(--qa-danger)" : severity === "enhance" ? "var(--qa-warn)" : "var(--qa-accent)";
6605
6646
  if (dense && !open && !editing) {
6606
6647
  return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
6607
6648
  "button",
@@ -7144,22 +7185,22 @@ function NoteFilterBar() {
7144
7185
  onClick: sev("bug")
7145
7186
  },
7146
7187
  {
7147
- key: "question",
7148
- label: t("sev_question"),
7149
- count: noteCounts.question,
7150
- active: filter.severity === "question",
7151
- icon: "AlertTriangle",
7188
+ key: "enhance",
7189
+ label: t("sev_enhance"),
7190
+ count: noteCounts.enhance,
7191
+ active: filter.severity === "enhance",
7192
+ icon: "Plus",
7152
7193
  tone: "warn",
7153
- onClick: sev("question")
7194
+ onClick: sev("enhance")
7154
7195
  },
7155
7196
  {
7156
- key: "polish",
7157
- label: t("sev_polish"),
7158
- count: noteCounts.polish,
7159
- active: filter.severity === "polish",
7197
+ key: "design",
7198
+ label: t("sev_design"),
7199
+ count: noteCounts.design,
7200
+ active: filter.severity === "design",
7160
7201
  icon: "Pencil",
7161
7202
  tone: "accent",
7162
- onClick: sev("polish")
7203
+ onClick: sev("design")
7163
7204
  },
7164
7205
  {
7165
7206
  key: "open",
@@ -7598,46 +7639,25 @@ function SettingsSheet({ onClose }) {
7598
7639
  );
7599
7640
  }
7600
7641
  function WelcomeCard() {
7601
- const { t, brand, dismissWelcome } = useQa();
7642
+ const { t, dismissWelcome } = useQa();
7602
7643
  return /* @__PURE__ */ jsxs(
7603
7644
  "div",
7604
7645
  {
7605
- className: "qa-rounded-xl qa-border qa-border-subtle qa-bg-1 qa-p-3 qa-space-y-2",
7646
+ 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
7647
  role: "note",
7607
7648
  children: [
7608
- /* @__PURE__ */ jsxs("p", { className: "qa-m-0 qa-flex qa-items-center qa-gap-1.5 qa-text-sm qa-font-semibold qa-text-hi", children: [
7609
- /* @__PURE__ */ jsx(
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
- ] }),
7649
+ /* @__PURE__ */ jsx(Icon, { name: "Crosshair", size: 13, className: "qa-shrink-0 qa-text-accent" }),
7650
+ /* @__PURE__ */ jsx("span", { className: "qa-flex-1 qa-text-xs qa-text-mid", children: t("welcome_capture") }),
7633
7651
  /* @__PURE__ */ jsx(
7634
7652
  "button",
7635
7653
  {
7636
7654
  type: "button",
7637
7655
  onClick: dismissWelcome,
7638
- className: "qa-tap qa-w-full qa-rounded-lg qa-bg-accent qa-px-3 qa-py-1.5 qa-text-xs qa-font-semibold",
7639
- style: { border: "none", cursor: "pointer" },
7640
- children: t("welcome_got_it")
7656
+ "aria-label": t("welcome_got_it"),
7657
+ title: t("welcome_got_it"),
7658
+ className: "qa-tap-icon qa-shrink-0 qa-rounded-full qa-text-mid qa-focus-ring",
7659
+ style: { background: "transparent", border: "none", cursor: "pointer", padding: 2 },
7660
+ children: /* @__PURE__ */ jsx(Icon, { name: "X", size: 13 })
7641
7661
  }
7642
7662
  )
7643
7663
  ]
@@ -9104,16 +9124,16 @@ function unlockPageScroll() {
9104
9124
  var DRAG_THRESHOLD2 = 6;
9105
9125
  var TOUCH_DRAG_THRESHOLD = 12;
9106
9126
  var MIN_REGION_SIZE = 8;
9107
- var SEVERITIES2 = ["bug", "question", "polish"];
9127
+ var SEVERITIES2 = ["bug", "design", "enhance"];
9108
9128
  var SEVERITY_ICON = {
9109
9129
  bug: "Bug",
9110
- question: "AlertTriangle",
9111
- polish: "Pencil"
9130
+ design: "Pencil",
9131
+ enhance: "Plus"
9112
9132
  };
9113
9133
  var SEVERITY_LABEL_KEY2 = {
9114
9134
  bug: "sev_bug",
9115
- question: "sev_question",
9116
- polish: "sev_polish"
9135
+ design: "sev_design",
9136
+ enhance: "sev_enhance"
9117
9137
  };
9118
9138
  function clampRegionRect(rect) {
9119
9139
  const vw = typeof window !== "undefined" ? window.innerWidth : rect.left + rect.width;
@@ -10067,7 +10087,7 @@ function CaptureMode() {
10067
10087
  /* @__PURE__ */ jsx("span", { className: "qa-text-11 qa-text-mid qa-me-1", children: t("severity_label") }),
10068
10088
  SEVERITIES2.map((sev) => {
10069
10089
  const active = severity === sev;
10070
- const toneClass = sev === "bug" ? "qa-bg-danger-tint qa-text-danger" : sev === "question" ? "qa-bg-warn-tint qa-text-warn" : "qa-bg-accent-tint qa-text-accent";
10090
+ 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
10091
  return /* @__PURE__ */ jsxs(
10072
10092
  "button",
10073
10093
  {
@@ -10345,5 +10365,5 @@ function Qapture({ config }) {
10345
10365
  }
10346
10366
 
10347
10367
  export { Qapture, deleteQaDatabase, initQaStudio };
10348
- //# sourceMappingURL=chunk-NM3IDVEL.js.map
10349
- //# sourceMappingURL=chunk-NM3IDVEL.js.map
10368
+ //# sourceMappingURL=chunk-FVQKCQC3.js.map
10369
+ //# sourceMappingURL=chunk-FVQKCQC3.js.map
@@ -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: "Severity",
1765
+ severity_label: "What is this?",
1766
1766
  sev_bug: "Bug",
1767
- sev_question: "Question",
1768
- sev_polish: "Polish",
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",
@@ -1960,10 +1960,10 @@ var STR = {
1960
1960
  copied: "\u062A\u0645 \u0627\u0644\u0646\u0633\u062E",
1961
1961
  copy_failed: "\u0641\u0634\u0644 \u0627\u0644\u0646\u0633\u062E",
1962
1962
  copy_prompt: "\u0646\u0633\u062E \u0643\u0645\u0648\u062C\u0651\u0647 \u0644\u0644\u0648\u0643\u064A\u0644",
1963
- severity_label: "\u0627\u0644\u0623\u0647\u0645\u064A\u0629",
1964
- sev_bug: "\u062E\u0644\u0644",
1965
- sev_question: "\u0633\u0624\u0627\u0644",
1966
- sev_polish: "\u062A\u062D\u0633\u064A\u0646",
1963
+ severity_label: "\u0645\u0627 \u0647\u0630\u0627\u061F",
1964
+ sev_bug: "Bug",
1965
+ sev_design: "Design",
1966
+ sev_enhance: "Enhance",
1967
1967
  status_open: "\u0645\u0641\u062A\u0648\u062D",
1968
1968
  status_verified: "\u062A\u0645 \u0627\u0644\u062A\u062D\u0642\u0642",
1969
1969
  context_attached: "{n} \u0645\u0646 \u0623\u062D\u062F\u0627\u062B \u0627\u0644\u062A\u0634\u063A\u064A\u0644 \u0645\u0631\u0641\u0642\u0629",
@@ -3284,15 +3284,15 @@ function fmtPct(n, d) {
3284
3284
  }
3285
3285
  function summariseSession(notes, stamp) {
3286
3286
  if (!notes.length) return "No points captured.";
3287
- let bugs = 0, questions = 0, polish = 0, verified = 0, awaiting = 0;
3287
+ let bugs = 0, enhancements = 0, designs = 0, verified = 0, awaiting = 0;
3288
3288
  const pages = /* @__PURE__ */ new Set();
3289
3289
  let earliest = Number.POSITIVE_INFINITY;
3290
3290
  let latest = 0;
3291
3291
  for (const n of notes) {
3292
3292
  const sev = n.severity ?? "bug";
3293
3293
  if (sev === "bug") bugs++;
3294
- else if (sev === "question") questions++;
3295
- else polish++;
3294
+ else if (sev === "enhance") enhancements++;
3295
+ else designs++;
3296
3296
  const status = n.status ?? "open";
3297
3297
  if (status === "verified") verified++;
3298
3298
  else if (status === "fixed") awaiting++;
@@ -3307,8 +3307,8 @@ function summariseSession(notes, stamp) {
3307
3307
  `${notes.length} point${notes.length === 1 ? "" : "s"}`,
3308
3308
  `${bugs} bug${bugs === 1 ? "" : "s"}`
3309
3309
  ];
3310
- if (questions) parts.push(`${questions} question${questions === 1 ? "" : "s"}`);
3311
- if (polish) parts.push(`${polish} polish`);
3310
+ if (enhancements) parts.push(`${enhancements} enhancement${enhancements === 1 ? "" : "s"}`);
3311
+ if (designs) parts.push(`${designs} design`);
3312
3312
  if (verified) parts.push(`${verified} verified`);
3313
3313
  if (awaiting) parts.push(`${awaiting} awaiting re-test`);
3314
3314
  parts.push(`${pages.size} page${pages.size === 1 ? "" : "s"}`);
@@ -3339,6 +3339,37 @@ Exported: ${stamp}
3339
3339
  Points: ${noteCount}
3340
3340
 
3341
3341
  **Session:** ${summariseSession(notes, stamp)}`
3342
+ );
3343
+ sections.push(
3344
+ `## What the tags mean
3345
+
3346
+ Every point below is tagged **Bug**, **Design** or **Enhance**. These are not priorities \u2014 they are three different kinds of work. Read the tag before you decide what to do.
3347
+
3348
+ ### Bug \u2014 something is broken
3349
+
3350
+ Find the **root cause**, not the symptom. Do not patch over it.
3351
+
3352
+ State the cause in one plain sentence before you change anything \u2014 e.g. "the date was wrong because the server sends UTC and the list formats in local time". **If you cannot write that sentence, you have not found the cause yet.** Keep looking. The real cause is often something small and silly; that is normal, and it is still the cause.
3353
+
3354
+ **The cause is not always in the code.** If the code does exactly what it was asked to do and the result is still wrong, then nothing is broken \u2014 what was *asked for* and what was *meant* did not match. That mismatch is the bug. Realign to what the owner meant and fix it there. If the note already says what they meant ("this isn't what I wanted, I wanted X"), they have told you \u2014 just do X. Do not stop to ask a question they have already answered.
3355
+
3356
+ ### Design \u2014 it works, but it looks or feels wrong
3357
+
3358
+ The functionality is fine and is not in question. Do not go hunting for a fault; there isn't one. Think properly about the interface and the experience \u2014 layout, spacing, hierarchy, wording, states, what the person is actually trying to do \u2014 and change how it looks and feels.
3359
+
3360
+ ### Enhance \u2014 a new idea, thought of while using the app
3361
+
3362
+ The tester wants something added or changed: a new section, a new feature, a different way of doing it.
3363
+
3364
+ **Plan it before you build it.** Read the surrounding code, work out what it touches and what it might break, and choose an approach that fits how the app is already built. Then build it. You do not need to stop and ask for approval \u2014 plan well, then go.
3365
+
3366
+ Push back only when you have a real reason: it would break something, or it conflicts with an invariant below. Then say so plainly and propose the alternative you would build instead.
3367
+
3368
+ ### When the tag and the words disagree
3369
+
3370
+ The tag sets your starting posture; **the words win.** A point tagged Design that says "this crashes" is a bug \u2014 treat it as one, and mention that you changed lane. People pick the wrong chip all the time; that is not a reason to do the wrong work.
3371
+
3372
+ And if a point is phrased as a question rather than a report, **answer it \u2014 do not change code on a maybe.**`
3342
3373
  );
3343
3374
  const stack = typeof p.stack === "string" && p.stack.trim() ? p.stack.trim() : "(not provided)";
3344
3375
  const runArr = toStrings(p.runCommands);
@@ -3934,12 +3965,12 @@ function campaignJsonText(allNotes) {
3934
3965
  function reportMarkdownText(allNotes) {
3935
3966
  if (!campaign) return "";
3936
3967
  const ordered = orderedKnownNotes(allNotes);
3937
- const counts = { bug: 0, question: 0, polish: 0, verified: 0 };
3968
+ const counts = { bug: 0, design: 0, enhance: 0, verified: 0 };
3938
3969
  for (const n of ordered) {
3939
3970
  const sev = n.severity ?? "bug";
3940
3971
  if (sev === "bug") counts.bug++;
3941
- else if (sev === "question") counts.question++;
3942
- else counts.polish++;
3972
+ else if (sev === "enhance") counts.enhance++;
3973
+ else counts.design++;
3943
3974
  if (n.status === "verified") counts.verified++;
3944
3975
  }
3945
3976
  const header = [
@@ -3948,7 +3979,7 @@ function reportMarkdownText(allNotes) {
3948
3979
  campaign.tester ? `Tester: ${campaign.tester} ` : "",
3949
3980
  `Started: ${campaign.startedAt} `,
3950
3981
  `Updated: ${(/* @__PURE__ */ new Date()).toISOString()} `,
3951
- `Points: ${ordered.length} (${counts.bug} bug, ${counts.question} question, ${counts.polish} polish \xB7 ${counts.verified} verified)`,
3982
+ `Points: ${ordered.length} (${counts.bug} bug, ${counts.design} design, ${counts.enhance} enhance \xB7 ${counts.verified} verified)`,
3952
3983
  "",
3953
3984
  getFsSyncEngine() === "download" ? "Saved by Qapture as the campaign was tested. Screenshots are in" : "Written live by Qapture as each point was saved. Screenshots are in",
3954
3985
  "`screenshots/`, one Markdown file per point in `notes/`.",
@@ -4300,6 +4331,12 @@ function QaProvider({
4300
4331
  for (const id of pendingIds) void idb.delete(id);
4301
4332
  storage.setJSON(PENDING_DELETE_KEY, []);
4302
4333
  }
4334
+ live = live.map((n) => {
4335
+ const legacy = n.severity;
4336
+ if (legacy === "question") return { ...n, severity: "enhance" };
4337
+ if (legacy === "polish") return { ...n, severity: "design" };
4338
+ return n;
4339
+ });
4303
4340
  const sorted = live.slice().sort(
4304
4341
  (a, b) => a.timestamp < b.timestamp ? 1 : -1
4305
4342
  );
@@ -4898,8 +4935,8 @@ function QaProvider({
4898
4935
  const counts = {
4899
4936
  all: notes.length,
4900
4937
  bug: 0,
4901
- question: 0,
4902
- polish: 0,
4938
+ design: 0,
4939
+ enhance: 0,
4903
4940
  open: 0,
4904
4941
  fixed: 0,
4905
4942
  verified: 0,
@@ -4908,8 +4945,8 @@ function QaProvider({
4908
4945
  for (const n of notes) {
4909
4946
  const sev = n.severity ?? "bug";
4910
4947
  if (sev === "bug") counts.bug++;
4911
- else if (sev === "question") counts.question++;
4912
- else counts.polish++;
4948
+ else if (sev === "enhance") counts.enhance++;
4949
+ else counts.design++;
4913
4950
  const status = n.status ?? "open";
4914
4951
  if (status === "verified") counts.verified++;
4915
4952
  else if (status === "fixed") counts.fixed++;
@@ -5298,7 +5335,11 @@ function QaProvider({
5298
5335
  pendingShare,
5299
5336
  sharePending,
5300
5337
  retestNote,
5301
- showWelcome,
5338
+ // The greeting explains how to capture. Once a note exists, the tester has
5339
+ // either done it or found their own way, so the line has nothing left to
5340
+ // say — take it away rather than making them dismiss it. (The stored flag
5341
+ // still governs a first visit; this only hides it early.)
5342
+ showWelcome: showWelcome && notes.length === 0,
5302
5343
  dismissWelcome,
5303
5344
  // v0.6
5304
5345
  panelSide,
@@ -5751,8 +5792,8 @@ function QaFab() {
5751
5792
  }
5752
5793
  var SEVERITIES = [
5753
5794
  { value: "bug", labelKey: "sev_bug", icon: "Bug" },
5754
- { value: "question", labelKey: "sev_question" },
5755
- { value: "polish", labelKey: "sev_polish" }
5795
+ { value: "enhance", labelKey: "sev_enhance" },
5796
+ { value: "design", labelKey: "sev_design" }
5756
5797
  ];
5757
5798
  function SeverityChipRow({
5758
5799
  value,
@@ -6497,13 +6538,13 @@ function KindBadge({
6497
6538
  }
6498
6539
  var SEVERITY_CLASS = {
6499
6540
  bug: "qa-bg-danger-tint qa-text-danger",
6500
- question: "qa-bg-warn-tint qa-text-warn",
6501
- polish: "qa-bg-accent-tint qa-text-accent"
6541
+ enhance: "qa-bg-warn-tint qa-text-warn",
6542
+ design: "qa-bg-accent-tint qa-text-accent"
6502
6543
  };
6503
6544
  var SEVERITY_LABEL_KEY = {
6504
6545
  bug: "sev_bug",
6505
- question: "sev_question",
6506
- polish: "sev_polish"
6546
+ enhance: "sev_enhance",
6547
+ design: "sev_design"
6507
6548
  };
6508
6549
  function SeverityChip({ severity, t }) {
6509
6550
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -6608,7 +6649,7 @@ function NoteItem({
6608
6649
  notify(t("copy_failed"), { tone: "error" });
6609
6650
  }
6610
6651
  };
6611
- const severityDot = severity === "bug" ? "var(--qa-danger)" : severity === "question" ? "var(--qa-warn)" : "var(--qa-accent)";
6652
+ const severityDot = severity === "bug" ? "var(--qa-danger)" : severity === "enhance" ? "var(--qa-warn)" : "var(--qa-accent)";
6612
6653
  if (dense && !open && !editing) {
6613
6654
  return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(
6614
6655
  "button",
@@ -7151,22 +7192,22 @@ function NoteFilterBar() {
7151
7192
  onClick: sev("bug")
7152
7193
  },
7153
7194
  {
7154
- key: "question",
7155
- label: t("sev_question"),
7156
- count: noteCounts.question,
7157
- active: filter.severity === "question",
7158
- icon: "AlertTriangle",
7195
+ key: "enhance",
7196
+ label: t("sev_enhance"),
7197
+ count: noteCounts.enhance,
7198
+ active: filter.severity === "enhance",
7199
+ icon: "Plus",
7159
7200
  tone: "warn",
7160
- onClick: sev("question")
7201
+ onClick: sev("enhance")
7161
7202
  },
7162
7203
  {
7163
- key: "polish",
7164
- label: t("sev_polish"),
7165
- count: noteCounts.polish,
7166
- active: filter.severity === "polish",
7204
+ key: "design",
7205
+ label: t("sev_design"),
7206
+ count: noteCounts.design,
7207
+ active: filter.severity === "design",
7167
7208
  icon: "Pencil",
7168
7209
  tone: "accent",
7169
- onClick: sev("polish")
7210
+ onClick: sev("design")
7170
7211
  },
7171
7212
  {
7172
7213
  key: "open",
@@ -7605,46 +7646,25 @@ function SettingsSheet({ onClose }) {
7605
7646
  );
7606
7647
  }
7607
7648
  function WelcomeCard() {
7608
- const { t, brand, dismissWelcome } = useQa();
7649
+ const { t, dismissWelcome } = useQa();
7609
7650
  return /* @__PURE__ */ jsxRuntime.jsxs(
7610
7651
  "div",
7611
7652
  {
7612
- className: "qa-rounded-xl qa-border qa-border-subtle qa-bg-1 qa-p-3 qa-space-y-2",
7653
+ className: "qa-flex qa-items-center qa-gap-2 qa-rounded-xl qa-border qa-border-subtle qa-bg-1 qa-px-3 qa-py-2",
7613
7654
  role: "note",
7614
7655
  children: [
7615
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "qa-m-0 qa-flex qa-items-center qa-gap-1.5 qa-text-sm qa-font-semibold qa-text-hi", children: [
7616
- /* @__PURE__ */ jsxRuntime.jsx(
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
- ] }),
7656
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "Crosshair", size: 13, className: "qa-shrink-0 qa-text-accent" }),
7657
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "qa-flex-1 qa-text-xs qa-text-mid", children: t("welcome_capture") }),
7640
7658
  /* @__PURE__ */ jsxRuntime.jsx(
7641
7659
  "button",
7642
7660
  {
7643
7661
  type: "button",
7644
7662
  onClick: dismissWelcome,
7645
- className: "qa-tap qa-w-full qa-rounded-lg qa-bg-accent qa-px-3 qa-py-1.5 qa-text-xs qa-font-semibold",
7646
- style: { border: "none", cursor: "pointer" },
7647
- children: t("welcome_got_it")
7663
+ "aria-label": t("welcome_got_it"),
7664
+ title: t("welcome_got_it"),
7665
+ className: "qa-tap-icon qa-shrink-0 qa-rounded-full qa-text-mid qa-focus-ring",
7666
+ style: { background: "transparent", border: "none", cursor: "pointer", padding: 2 },
7667
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "X", size: 13 })
7648
7668
  }
7649
7669
  )
7650
7670
  ]
@@ -9111,16 +9131,16 @@ function unlockPageScroll() {
9111
9131
  var DRAG_THRESHOLD2 = 6;
9112
9132
  var TOUCH_DRAG_THRESHOLD = 12;
9113
9133
  var MIN_REGION_SIZE = 8;
9114
- var SEVERITIES2 = ["bug", "question", "polish"];
9134
+ var SEVERITIES2 = ["bug", "design", "enhance"];
9115
9135
  var SEVERITY_ICON = {
9116
9136
  bug: "Bug",
9117
- question: "AlertTriangle",
9118
- polish: "Pencil"
9137
+ design: "Pencil",
9138
+ enhance: "Plus"
9119
9139
  };
9120
9140
  var SEVERITY_LABEL_KEY2 = {
9121
9141
  bug: "sev_bug",
9122
- question: "sev_question",
9123
- polish: "sev_polish"
9142
+ design: "sev_design",
9143
+ enhance: "sev_enhance"
9124
9144
  };
9125
9145
  function clampRegionRect(rect) {
9126
9146
  const vw = typeof window !== "undefined" ? window.innerWidth : rect.left + rect.width;
@@ -10074,7 +10094,7 @@ function CaptureMode() {
10074
10094
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "qa-text-11 qa-text-mid qa-me-1", children: t("severity_label") }),
10075
10095
  SEVERITIES2.map((sev) => {
10076
10096
  const active = severity === sev;
10077
- const toneClass = sev === "bug" ? "qa-bg-danger-tint qa-text-danger" : sev === "question" ? "qa-bg-warn-tint qa-text-warn" : "qa-bg-accent-tint qa-text-accent";
10097
+ const toneClass = sev === "bug" ? "qa-bg-danger-tint qa-text-danger" : sev === "enhance" ? "qa-bg-warn-tint qa-text-warn" : "qa-bg-accent-tint qa-text-accent";
10078
10098
  return /* @__PURE__ */ jsxRuntime.jsxs(
10079
10099
  "button",
10080
10100
  {
@@ -10354,5 +10374,5 @@ function Qapture({ config }) {
10354
10374
  exports.Qapture = Qapture;
10355
10375
  exports.deleteQaDatabase = deleteQaDatabase;
10356
10376
  exports.initQaStudio = initQaStudio;
10357
- //# sourceMappingURL=chunk-CNNNWLTX.cjs.map
10358
- //# sourceMappingURL=chunk-CNNNWLTX.cjs.map
10377
+ //# sourceMappingURL=chunk-WU7QMHPK.cjs.map
10378
+ //# sourceMappingURL=chunk-WU7QMHPK.cjs.map
package/dist/index.cjs CHANGED
@@ -1,24 +1,24 @@
1
1
  'use strict';
2
2
 
3
- var chunkCNNNWLTX_cjs = require('./chunk-CNNNWLTX.cjs');
3
+ var chunkWU7QMHPK_cjs = require('./chunk-WU7QMHPK.cjs');
4
4
 
5
5
 
6
6
 
7
7
  Object.defineProperty(exports, "QaStudio", {
8
8
  enumerable: true,
9
- get: function () { return chunkCNNNWLTX_cjs.Qapture; }
9
+ get: function () { return chunkWU7QMHPK_cjs.Qapture; }
10
10
  });
11
11
  Object.defineProperty(exports, "Qapture", {
12
12
  enumerable: true,
13
- get: function () { return chunkCNNNWLTX_cjs.Qapture; }
13
+ get: function () { return chunkWU7QMHPK_cjs.Qapture; }
14
14
  });
15
15
  Object.defineProperty(exports, "deleteQaDatabase", {
16
16
  enumerable: true,
17
- get: function () { return chunkCNNNWLTX_cjs.deleteQaDatabase; }
17
+ get: function () { return chunkWU7QMHPK_cjs.deleteQaDatabase; }
18
18
  });
19
19
  Object.defineProperty(exports, "initQaStudio", {
20
20
  enumerable: true,
21
- get: function () { return chunkCNNNWLTX_cjs.initQaStudio; }
21
+ get: function () { return chunkWU7QMHPK_cjs.initQaStudio; }
22
22
  });
23
23
  //# sourceMappingURL=index.cjs.map
24
24
  //# sourceMappingURL=index.cjs.map
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- export { Qapture as QaStudio, Qapture, deleteQaDatabase, initQaStudio } from './chunk-NM3IDVEL.js';
1
+ export { Qapture as QaStudio, Qapture, deleteQaDatabase, initQaStudio } from './chunk-FVQKCQC3.js';
2
2
  //# sourceMappingURL=index.js.map
3
3
  //# sourceMappingURL=index.js.map
package/dist/next.cjs CHANGED
@@ -1,21 +1,21 @@
1
1
  "use client";
2
2
  'use strict';
3
3
 
4
- var chunkCNNNWLTX_cjs = require('./chunk-CNNNWLTX.cjs');
4
+ var chunkWU7QMHPK_cjs = require('./chunk-WU7QMHPK.cjs');
5
5
 
6
6
 
7
7
 
8
8
  Object.defineProperty(exports, "QaStudio", {
9
9
  enumerable: true,
10
- get: function () { return chunkCNNNWLTX_cjs.Qapture; }
10
+ get: function () { return chunkWU7QMHPK_cjs.Qapture; }
11
11
  });
12
12
  Object.defineProperty(exports, "Qapture", {
13
13
  enumerable: true,
14
- get: function () { return chunkCNNNWLTX_cjs.Qapture; }
14
+ get: function () { return chunkWU7QMHPK_cjs.Qapture; }
15
15
  });
16
16
  Object.defineProperty(exports, "initQaStudio", {
17
17
  enumerable: true,
18
- get: function () { return chunkCNNNWLTX_cjs.initQaStudio; }
18
+ get: function () { return chunkWU7QMHPK_cjs.initQaStudio; }
19
19
  });
20
20
  //# sourceMappingURL=next.cjs.map
21
21
  //# sourceMappingURL=next.cjs.map
package/dist/next.js CHANGED
@@ -1,4 +1,4 @@
1
1
  "use client";
2
- export { Qapture as QaStudio, Qapture, initQaStudio } from './chunk-NM3IDVEL.js';
2
+ export { Qapture as QaStudio, Qapture, initQaStudio } from './chunk-FVQKCQC3.js';
3
3
  //# sourceMappingURL=next.js.map
4
4
  //# sourceMappingURL=next.js.map
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkCNNNWLTX_cjs = require('./chunk-CNNNWLTX.cjs');
3
+ var chunkWU7QMHPK_cjs = require('./chunk-WU7QMHPK.cjs');
4
4
 
5
5
  // src/standalone.ts
6
6
  if (typeof window !== "undefined" && typeof customElements !== "undefined" && !customElements.get("qapture-widget")) {
@@ -38,7 +38,7 @@ if (typeof window !== "undefined" && typeof customElements !== "undefined" && !c
38
38
 
39
39
  Object.defineProperty(exports, "initQaStudio", {
40
40
  enumerable: true,
41
- get: function () { return chunkCNNNWLTX_cjs.initQaStudio; }
41
+ get: function () { return chunkWU7QMHPK_cjs.initQaStudio; }
42
42
  });
43
43
  //# sourceMappingURL=standalone.cjs.map
44
44
  //# sourceMappingURL=standalone.cjs.map
@@ -1,4 +1,4 @@
1
- export { initQaStudio } from './chunk-NM3IDVEL.js';
1
+ export { initQaStudio } from './chunk-FVQKCQC3.js';
2
2
 
3
3
  // src/standalone.ts
4
4
  if (typeof window !== "undefined" && typeof customElements !== "undefined" && !customElements.get("qapture-widget")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qapture2",
3
- "version": "0.7.6",
3
+ "version": "0.7.7",
4
4
  "description": "Drop-in, AI-aware in-browser QA capture widget. A tester annotates the live app (element/region + auto-screenshot + note), tracks a graded testing journey, and exports a ZIP your own coding agent reads. Ships zero AI.",
5
5
  "type": "module",
6
6
  "license": "MIT",