qapture2 0.7.3 → 0.7.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -351,9 +351,15 @@ Because by default it is. The DOM engine re-draws your page; fonts shift,
351
351
  shadows and gradients flatten, `<canvas>` and video come out blank. From
352
352
  v0.7.3 a redrawn capture says so under the preview and offers a one-tap **Use
353
353
  real screenshots**, which grants tab-share and re-shoots the same selection
354
- with the exact engine below. Chromium desktop only — on Safari and Firefox the
355
- redraw is the only engine there is, so for screenshot-critical QA, test in
356
- Chrome.
354
+ with the exact engine below.
355
+
356
+ That upgrade is Chromium-only, because it depends on `preferCurrentTab`. On
357
+ Safari and Firefox the redraw is the only engine there is — so from v0.7.4 the
358
+ card says so outright and points you at Chrome, rather than leaving you to
359
+ conclude the tool is broken. **If your app draws anything with inline SVG, a
360
+ `<canvas>`, or video, do your screenshot QA in Chrome.** A redraw will render
361
+ an SVG chart as bare outlines and a canvas as a blank box, every time, in any
362
+ tool built this way.
357
363
 
358
364
  ### `exact` — opt-in, pixel-for-pixel
359
365
 
@@ -1784,6 +1784,8 @@ var STR = {
1784
1784
  exact_hint: "Screenshots become a real photo of the tab instead of a redraw. Asks once to share this tab; nothing leaves your device.",
1785
1785
  redraw_notice: "This is a redraw of the page, not a real photo.",
1786
1786
  redraw_action: "Use real screenshots",
1787
+ redraw_no_engine: "This browser cannot photograph a tab, so charts and canvases may come out wrong. Open the app in Chrome, Edge or Brave for true screenshots.",
1788
+ fab_title: "Qapture \u2014 click to open, drag to move it out of the way",
1787
1789
  exact_on: "Pixel-exact screenshots on",
1788
1790
  exact_off: "Back to redrawn screenshots",
1789
1791
  exact_declined: "Staying on redrawn screenshots",
@@ -1980,6 +1982,8 @@ var STR = {
1980
1982
  exact_hint: "\u062A\u0635\u0628\u062D \u0627\u0644\u0644\u0642\u0637\u0629 \u0635\u0648\u0631\u0629 \u062D\u0642\u064A\u0642\u064A\u0629 \u0644\u0644\u062A\u0628\u0648\u064A\u0628 \u0628\u062F\u0644 \u0625\u0639\u0627\u062F\u0629 \u0631\u0633\u0645\u0647. \u064A\u064F\u0637\u0644\u0628 \u0627\u0644\u0625\u0630\u0646 \u0645\u0631\u0629 \u0648\u0627\u062D\u062F\u0629\u060C \u0648\u0644\u0627 \u0634\u064A\u0621 \u064A\u063A\u0627\u062F\u0631 \u062C\u0647\u0627\u0632\u0643.",
1981
1983
  redraw_notice: "\u0647\u0630\u0647 \u0625\u0639\u0627\u062F\u0629 \u0631\u0633\u0645 \u0644\u0644\u0635\u0641\u062D\u0629\u060C \u0648\u0644\u064A\u0633\u062A \u0635\u0648\u0631\u0629 \u062D\u0642\u064A\u0642\u064A\u0629.",
1982
1984
  redraw_action: "\u0627\u0633\u062A\u062E\u062F\u0645 \u0644\u0642\u0637\u0627\u062A \u062D\u0642\u064A\u0642\u064A\u0629",
1985
+ redraw_no_engine: "\u0644\u0627 \u064A\u0633\u062A\u0637\u064A\u0639 \u0647\u0630\u0627 \u0627\u0644\u0645\u062A\u0635\u0641\u062D \u062A\u0635\u0648\u064A\u0631 \u0627\u0644\u062A\u0628\u0648\u064A\u0628\u060C \u0644\u0630\u0627 \u0642\u062F \u062A\u0638\u0647\u0631 \u0627\u0644\u0645\u062E\u0637\u0637\u0627\u062A \u0648\u0627\u0644\u0644\u0648\u062D\u0627\u062A \u0628\u0634\u0643\u0644 \u062E\u0627\u0637\u0626. \u0627\u0641\u062A\u062D \u0627\u0644\u062A\u0637\u0628\u064A\u0642 \u0641\u064A Chrome \u0623\u0648 Edge \u0623\u0648 Brave \u0644\u0644\u062D\u0635\u0648\u0644 \u0639\u0644\u0649 \u0644\u0642\u0637\u0627\u062A \u062D\u0642\u064A\u0642\u064A\u0629.",
1986
+ fab_title: "Qapture \u2014 \u0627\u0636\u063A\u0637 \u0644\u0644\u0641\u062A\u062D\u060C \u0648\u0627\u0633\u062D\u0628 \u0644\u062A\u062D\u0631\u064A\u0643\u0647 \u0645\u0646 \u0645\u0643\u0627\u0646\u0647",
1983
1987
  exact_on: "\u062A\u0645 \u062A\u0641\u0639\u064A\u0644 \u0627\u0644\u0644\u0642\u0637\u0627\u062A \u0627\u0644\u0645\u0637\u0627\u0628\u0642\u0629",
1984
1988
  exact_off: "\u0627\u0644\u0639\u0648\u062F\u0629 \u0625\u0644\u0649 \u0627\u0644\u0644\u0642\u0637\u0627\u062A \u0627\u0644\u0645\u064F\u0639\u0627\u062F \u0631\u0633\u0645\u0647\u0627",
1985
1989
  exact_declined: "\u0633\u0646\u0628\u0642\u0649 \u0639\u0644\u0649 \u0627\u0644\u0644\u0642\u0637\u0627\u062A \u0627\u0644\u0645\u064F\u0639\u0627\u062F \u0631\u0633\u0645\u0647\u0627",
@@ -5304,29 +5308,6 @@ function Icon({
5304
5308
  function renderSvgElement(tag, attrs, key) {
5305
5309
  return React__default.default.createElement(tag, { key, ...attrs });
5306
5310
  }
5307
- function isCoarsePointer() {
5308
- if (typeof window === "undefined") return false;
5309
- try {
5310
- if (window.matchMedia && window.matchMedia("(pointer: coarse)").matches) return true;
5311
- } catch {
5312
- }
5313
- return typeof navigator !== "undefined" && (navigator.maxTouchPoints || 0) > 0;
5314
- }
5315
- function useCoarsePointer() {
5316
- const [coarse, setCoarse] = React.useState(() => isCoarsePointer());
5317
- React.useEffect(() => {
5318
- if (typeof window === "undefined" || !window.matchMedia) return;
5319
- const mq = window.matchMedia("(pointer: coarse)");
5320
- const on = () => setCoarse(isCoarsePointer());
5321
- if (mq.addEventListener) mq.addEventListener("change", on);
5322
- else if (mq.addListener) mq.addListener(on);
5323
- return () => {
5324
- if (mq.removeEventListener) mq.removeEventListener("change", on);
5325
- else if (mq.removeListener) mq.removeListener(on);
5326
- };
5327
- }, []);
5328
- return coarse;
5329
- }
5330
5311
  var DEFAULT_LEFT = "calc(1.25rem + env(safe-area-inset-left))";
5331
5312
  var DEFAULT_BOTTOM = "calc(5rem + env(safe-area-inset-bottom))";
5332
5313
  var FAB_SIZE_PX = 56;
@@ -5383,8 +5364,7 @@ function clampFabPos(p, w = FAB_SIZE_PX, h = FAB_SIZE_PX) {
5383
5364
  };
5384
5365
  }
5385
5366
  function QaFab() {
5386
- const { isOpen, setIsOpen, notes, captureActive, namespace } = useQa();
5387
- const coarse = useCoarsePointer();
5367
+ const { isOpen, setIsOpen, notes, captureActive, namespace, t } = useQa();
5388
5368
  const [pos, setPos] = React.useState(() => loadFabPos(namespace));
5389
5369
  const dragRef = React.useRef(null);
5390
5370
  const didDragRef = React.useRef(false);
@@ -5402,6 +5382,7 @@ function QaFab() {
5402
5382
  if (captureActive) return null;
5403
5383
  const onPointerDown = (e) => {
5404
5384
  if (dragRef.current) return;
5385
+ if (e.pointerType === "mouse" && e.button !== 0) return;
5405
5386
  didDragRef.current = false;
5406
5387
  const target = e.currentTarget;
5407
5388
  const rect = target.getBoundingClientRect();
@@ -5463,7 +5444,7 @@ function QaFab() {
5463
5444
  }
5464
5445
  setIsOpen(!isOpen);
5465
5446
  };
5466
- const applied = coarse && pos ? clampFabPos(pos) : null;
5447
+ const applied = pos ? clampFabPos(pos) : null;
5467
5448
  const fabStyle = {
5468
5449
  left: applied ? `${applied.left}px` : DEFAULT_LEFT,
5469
5450
  bottom: applied ? `${applied.bottom}px` : DEFAULT_BOTTOM,
@@ -5478,13 +5459,13 @@ function QaFab() {
5478
5459
  "data-qa-overlay": "true",
5479
5460
  dir: "ltr",
5480
5461
  onClick: handleClick,
5481
- onPointerDown: coarse ? onPointerDown : void 0,
5482
- onPointerMove: coarse ? onPointerMove : void 0,
5483
- onPointerUp: coarse ? onPointerUp : void 0,
5484
- onPointerCancel: coarse ? onPointerCancel : void 0,
5462
+ onPointerDown,
5463
+ onPointerMove,
5464
+ onPointerUp,
5465
+ onPointerCancel,
5485
5466
  "aria-label": "Qapture \u2014 testing notes",
5486
- title: "Qapture",
5487
- className: `qa-fixed qa-flex qa-items-center qa-justify-center qa-rounded-full qa-bg-accent qa-elev-2 qa-print-hidden qa-fab-btn${coarse ? " qa-touch-none" : ""}`,
5467
+ title: t("fab_title"),
5468
+ className: "qa-fixed qa-flex qa-items-center qa-justify-center qa-rounded-full qa-bg-accent qa-elev-2 qa-print-hidden qa-fab-btn qa-touch-none",
5488
5469
  style: fabStyle,
5489
5470
  children: [
5490
5471
  !isOpen && /* @__PURE__ */ jsxRuntime.jsx(
@@ -7801,6 +7782,29 @@ function GuideSection() {
7801
7782
  ))
7802
7783
  ] });
7803
7784
  }
7785
+ function isCoarsePointer() {
7786
+ if (typeof window === "undefined") return false;
7787
+ try {
7788
+ if (window.matchMedia && window.matchMedia("(pointer: coarse)").matches) return true;
7789
+ } catch {
7790
+ }
7791
+ return typeof navigator !== "undefined" && (navigator.maxTouchPoints || 0) > 0;
7792
+ }
7793
+ function useCoarsePointer() {
7794
+ const [coarse, setCoarse] = React.useState(() => isCoarsePointer());
7795
+ React.useEffect(() => {
7796
+ if (typeof window === "undefined" || !window.matchMedia) return;
7797
+ const mq = window.matchMedia("(pointer: coarse)");
7798
+ const on = () => setCoarse(isCoarsePointer());
7799
+ if (mq.addEventListener) mq.addEventListener("change", on);
7800
+ else if (mq.addListener) mq.addListener(on);
7801
+ return () => {
7802
+ if (mq.removeEventListener) mq.removeEventListener("change", on);
7803
+ else if (mq.removeListener) mq.removeListener(on);
7804
+ };
7805
+ }, []);
7806
+ return coarse;
7807
+ }
7804
7808
  var TABS = [
7805
7809
  { key: "notes", labelKey: "tab_notes", icon: "StickyNote" },
7806
7810
  { key: "logins", labelKey: "tab_logins", icon: "KeyRound" },
@@ -9758,7 +9762,7 @@ function CaptureMode() {
9758
9762
  )
9759
9763
  ] })
9760
9764
  ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "qa-py-4 qa-text-xs qa-text-slate-400", children: t("no_shot") }),
9761
- shotUrl && shotEngine === "dom" && exactShots.supported && exactShots.status !== "live" && /* @__PURE__ */ jsxRuntime.jsxs(
9765
+ shotUrl && shotEngine === "dom" && (exactShots.status !== "live" && exactShots.supported ? /* @__PURE__ */ jsxRuntime.jsxs(
9762
9766
  "span",
9763
9767
  {
9764
9768
  "data-qa-redraw-offer": "true",
@@ -9784,7 +9788,25 @@ function CaptureMode() {
9784
9788
  )
9785
9789
  ]
9786
9790
  }
9787
- )
9791
+ ) : !exactShots.supported ? (
9792
+ // Safari and Firefox have no way to photograph the tab, so
9793
+ // there is nothing to offer — but saying NOTHING was the real
9794
+ // failure. A tester watching an SVG chart come back as bare
9795
+ // outlines concludes the tool is broken and files it as a bug,
9796
+ // over and over. Name the limit and name the way out.
9797
+ /* @__PURE__ */ jsxRuntime.jsxs(
9798
+ "span",
9799
+ {
9800
+ "data-qa-redraw-note": "true",
9801
+ className: "qa-mt-1.5 qa-block qa-text-center qa-text-10 qa-text-mid",
9802
+ children: [
9803
+ t("redraw_notice"),
9804
+ " ",
9805
+ t("redraw_no_engine")
9806
+ ]
9807
+ }
9808
+ )
9809
+ ) : null)
9788
9810
  ]
9789
9811
  }
9790
9812
  ),
@@ -10079,5 +10101,5 @@ function Qapture({ config }) {
10079
10101
  exports.Qapture = Qapture;
10080
10102
  exports.deleteQaDatabase = deleteQaDatabase;
10081
10103
  exports.initQaStudio = initQaStudio;
10082
- //# sourceMappingURL=chunk-C5WJFSMN.cjs.map
10083
- //# sourceMappingURL=chunk-C5WJFSMN.cjs.map
10104
+ //# sourceMappingURL=chunk-COQKFGKZ.cjs.map
10105
+ //# sourceMappingURL=chunk-COQKFGKZ.cjs.map
@@ -1777,6 +1777,8 @@ var STR = {
1777
1777
  exact_hint: "Screenshots become a real photo of the tab instead of a redraw. Asks once to share this tab; nothing leaves your device.",
1778
1778
  redraw_notice: "This is a redraw of the page, not a real photo.",
1779
1779
  redraw_action: "Use real screenshots",
1780
+ redraw_no_engine: "This browser cannot photograph a tab, so charts and canvases may come out wrong. Open the app in Chrome, Edge or Brave for true screenshots.",
1781
+ fab_title: "Qapture \u2014 click to open, drag to move it out of the way",
1780
1782
  exact_on: "Pixel-exact screenshots on",
1781
1783
  exact_off: "Back to redrawn screenshots",
1782
1784
  exact_declined: "Staying on redrawn screenshots",
@@ -1973,6 +1975,8 @@ var STR = {
1973
1975
  exact_hint: "\u062A\u0635\u0628\u062D \u0627\u0644\u0644\u0642\u0637\u0629 \u0635\u0648\u0631\u0629 \u062D\u0642\u064A\u0642\u064A\u0629 \u0644\u0644\u062A\u0628\u0648\u064A\u0628 \u0628\u062F\u0644 \u0625\u0639\u0627\u062F\u0629 \u0631\u0633\u0645\u0647. \u064A\u064F\u0637\u0644\u0628 \u0627\u0644\u0625\u0630\u0646 \u0645\u0631\u0629 \u0648\u0627\u062D\u062F\u0629\u060C \u0648\u0644\u0627 \u0634\u064A\u0621 \u064A\u063A\u0627\u062F\u0631 \u062C\u0647\u0627\u0632\u0643.",
1974
1976
  redraw_notice: "\u0647\u0630\u0647 \u0625\u0639\u0627\u062F\u0629 \u0631\u0633\u0645 \u0644\u0644\u0635\u0641\u062D\u0629\u060C \u0648\u0644\u064A\u0633\u062A \u0635\u0648\u0631\u0629 \u062D\u0642\u064A\u0642\u064A\u0629.",
1975
1977
  redraw_action: "\u0627\u0633\u062A\u062E\u062F\u0645 \u0644\u0642\u0637\u0627\u062A \u062D\u0642\u064A\u0642\u064A\u0629",
1978
+ redraw_no_engine: "\u0644\u0627 \u064A\u0633\u062A\u0637\u064A\u0639 \u0647\u0630\u0627 \u0627\u0644\u0645\u062A\u0635\u0641\u062D \u062A\u0635\u0648\u064A\u0631 \u0627\u0644\u062A\u0628\u0648\u064A\u0628\u060C \u0644\u0630\u0627 \u0642\u062F \u062A\u0638\u0647\u0631 \u0627\u0644\u0645\u062E\u0637\u0637\u0627\u062A \u0648\u0627\u0644\u0644\u0648\u062D\u0627\u062A \u0628\u0634\u0643\u0644 \u062E\u0627\u0637\u0626. \u0627\u0641\u062A\u062D \u0627\u0644\u062A\u0637\u0628\u064A\u0642 \u0641\u064A Chrome \u0623\u0648 Edge \u0623\u0648 Brave \u0644\u0644\u062D\u0635\u0648\u0644 \u0639\u0644\u0649 \u0644\u0642\u0637\u0627\u062A \u062D\u0642\u064A\u0642\u064A\u0629.",
1979
+ fab_title: "Qapture \u2014 \u0627\u0636\u063A\u0637 \u0644\u0644\u0641\u062A\u062D\u060C \u0648\u0627\u0633\u062D\u0628 \u0644\u062A\u062D\u0631\u064A\u0643\u0647 \u0645\u0646 \u0645\u0643\u0627\u0646\u0647",
1976
1980
  exact_on: "\u062A\u0645 \u062A\u0641\u0639\u064A\u0644 \u0627\u0644\u0644\u0642\u0637\u0627\u062A \u0627\u0644\u0645\u0637\u0627\u0628\u0642\u0629",
1977
1981
  exact_off: "\u0627\u0644\u0639\u0648\u062F\u0629 \u0625\u0644\u0649 \u0627\u0644\u0644\u0642\u0637\u0627\u062A \u0627\u0644\u0645\u064F\u0639\u0627\u062F \u0631\u0633\u0645\u0647\u0627",
1978
1982
  exact_declined: "\u0633\u0646\u0628\u0642\u0649 \u0639\u0644\u0649 \u0627\u0644\u0644\u0642\u0637\u0627\u062A \u0627\u0644\u0645\u064F\u0639\u0627\u062F \u0631\u0633\u0645\u0647\u0627",
@@ -5297,29 +5301,6 @@ function Icon({
5297
5301
  function renderSvgElement(tag, attrs, key) {
5298
5302
  return React.createElement(tag, { key, ...attrs });
5299
5303
  }
5300
- function isCoarsePointer() {
5301
- if (typeof window === "undefined") return false;
5302
- try {
5303
- if (window.matchMedia && window.matchMedia("(pointer: coarse)").matches) return true;
5304
- } catch {
5305
- }
5306
- return typeof navigator !== "undefined" && (navigator.maxTouchPoints || 0) > 0;
5307
- }
5308
- function useCoarsePointer() {
5309
- const [coarse, setCoarse] = useState(() => isCoarsePointer());
5310
- useEffect(() => {
5311
- if (typeof window === "undefined" || !window.matchMedia) return;
5312
- const mq = window.matchMedia("(pointer: coarse)");
5313
- const on = () => setCoarse(isCoarsePointer());
5314
- if (mq.addEventListener) mq.addEventListener("change", on);
5315
- else if (mq.addListener) mq.addListener(on);
5316
- return () => {
5317
- if (mq.removeEventListener) mq.removeEventListener("change", on);
5318
- else if (mq.removeListener) mq.removeListener(on);
5319
- };
5320
- }, []);
5321
- return coarse;
5322
- }
5323
5304
  var DEFAULT_LEFT = "calc(1.25rem + env(safe-area-inset-left))";
5324
5305
  var DEFAULT_BOTTOM = "calc(5rem + env(safe-area-inset-bottom))";
5325
5306
  var FAB_SIZE_PX = 56;
@@ -5376,8 +5357,7 @@ function clampFabPos(p, w = FAB_SIZE_PX, h = FAB_SIZE_PX) {
5376
5357
  };
5377
5358
  }
5378
5359
  function QaFab() {
5379
- const { isOpen, setIsOpen, notes, captureActive, namespace } = useQa();
5380
- const coarse = useCoarsePointer();
5360
+ const { isOpen, setIsOpen, notes, captureActive, namespace, t } = useQa();
5381
5361
  const [pos, setPos] = useState(() => loadFabPos(namespace));
5382
5362
  const dragRef = useRef(null);
5383
5363
  const didDragRef = useRef(false);
@@ -5395,6 +5375,7 @@ function QaFab() {
5395
5375
  if (captureActive) return null;
5396
5376
  const onPointerDown = (e) => {
5397
5377
  if (dragRef.current) return;
5378
+ if (e.pointerType === "mouse" && e.button !== 0) return;
5398
5379
  didDragRef.current = false;
5399
5380
  const target = e.currentTarget;
5400
5381
  const rect = target.getBoundingClientRect();
@@ -5456,7 +5437,7 @@ function QaFab() {
5456
5437
  }
5457
5438
  setIsOpen(!isOpen);
5458
5439
  };
5459
- const applied = coarse && pos ? clampFabPos(pos) : null;
5440
+ const applied = pos ? clampFabPos(pos) : null;
5460
5441
  const fabStyle = {
5461
5442
  left: applied ? `${applied.left}px` : DEFAULT_LEFT,
5462
5443
  bottom: applied ? `${applied.bottom}px` : DEFAULT_BOTTOM,
@@ -5471,13 +5452,13 @@ function QaFab() {
5471
5452
  "data-qa-overlay": "true",
5472
5453
  dir: "ltr",
5473
5454
  onClick: handleClick,
5474
- onPointerDown: coarse ? onPointerDown : void 0,
5475
- onPointerMove: coarse ? onPointerMove : void 0,
5476
- onPointerUp: coarse ? onPointerUp : void 0,
5477
- onPointerCancel: coarse ? onPointerCancel : void 0,
5455
+ onPointerDown,
5456
+ onPointerMove,
5457
+ onPointerUp,
5458
+ onPointerCancel,
5478
5459
  "aria-label": "Qapture \u2014 testing notes",
5479
- title: "Qapture",
5480
- className: `qa-fixed qa-flex qa-items-center qa-justify-center qa-rounded-full qa-bg-accent qa-elev-2 qa-print-hidden qa-fab-btn${coarse ? " qa-touch-none" : ""}`,
5460
+ title: t("fab_title"),
5461
+ className: "qa-fixed qa-flex qa-items-center qa-justify-center qa-rounded-full qa-bg-accent qa-elev-2 qa-print-hidden qa-fab-btn qa-touch-none",
5481
5462
  style: fabStyle,
5482
5463
  children: [
5483
5464
  !isOpen && /* @__PURE__ */ jsx(
@@ -7794,6 +7775,29 @@ function GuideSection() {
7794
7775
  ))
7795
7776
  ] });
7796
7777
  }
7778
+ function isCoarsePointer() {
7779
+ if (typeof window === "undefined") return false;
7780
+ try {
7781
+ if (window.matchMedia && window.matchMedia("(pointer: coarse)").matches) return true;
7782
+ } catch {
7783
+ }
7784
+ return typeof navigator !== "undefined" && (navigator.maxTouchPoints || 0) > 0;
7785
+ }
7786
+ function useCoarsePointer() {
7787
+ const [coarse, setCoarse] = useState(() => isCoarsePointer());
7788
+ useEffect(() => {
7789
+ if (typeof window === "undefined" || !window.matchMedia) return;
7790
+ const mq = window.matchMedia("(pointer: coarse)");
7791
+ const on = () => setCoarse(isCoarsePointer());
7792
+ if (mq.addEventListener) mq.addEventListener("change", on);
7793
+ else if (mq.addListener) mq.addListener(on);
7794
+ return () => {
7795
+ if (mq.removeEventListener) mq.removeEventListener("change", on);
7796
+ else if (mq.removeListener) mq.removeListener(on);
7797
+ };
7798
+ }, []);
7799
+ return coarse;
7800
+ }
7797
7801
  var TABS = [
7798
7802
  { key: "notes", labelKey: "tab_notes", icon: "StickyNote" },
7799
7803
  { key: "logins", labelKey: "tab_logins", icon: "KeyRound" },
@@ -9751,7 +9755,7 @@ function CaptureMode() {
9751
9755
  )
9752
9756
  ] })
9753
9757
  ) : /* @__PURE__ */ jsx("span", { className: "qa-py-4 qa-text-xs qa-text-slate-400", children: t("no_shot") }),
9754
- shotUrl && shotEngine === "dom" && exactShots.supported && exactShots.status !== "live" && /* @__PURE__ */ jsxs(
9758
+ shotUrl && shotEngine === "dom" && (exactShots.status !== "live" && exactShots.supported ? /* @__PURE__ */ jsxs(
9755
9759
  "span",
9756
9760
  {
9757
9761
  "data-qa-redraw-offer": "true",
@@ -9777,7 +9781,25 @@ function CaptureMode() {
9777
9781
  )
9778
9782
  ]
9779
9783
  }
9780
- )
9784
+ ) : !exactShots.supported ? (
9785
+ // Safari and Firefox have no way to photograph the tab, so
9786
+ // there is nothing to offer — but saying NOTHING was the real
9787
+ // failure. A tester watching an SVG chart come back as bare
9788
+ // outlines concludes the tool is broken and files it as a bug,
9789
+ // over and over. Name the limit and name the way out.
9790
+ /* @__PURE__ */ jsxs(
9791
+ "span",
9792
+ {
9793
+ "data-qa-redraw-note": "true",
9794
+ className: "qa-mt-1.5 qa-block qa-text-center qa-text-10 qa-text-mid",
9795
+ children: [
9796
+ t("redraw_notice"),
9797
+ " ",
9798
+ t("redraw_no_engine")
9799
+ ]
9800
+ }
9801
+ )
9802
+ ) : null)
9781
9803
  ]
9782
9804
  }
9783
9805
  ),
@@ -10070,5 +10092,5 @@ function Qapture({ config }) {
10070
10092
  }
10071
10093
 
10072
10094
  export { Qapture, deleteQaDatabase, initQaStudio };
10073
- //# sourceMappingURL=chunk-BM3ESR2O.js.map
10074
- //# sourceMappingURL=chunk-BM3ESR2O.js.map
10095
+ //# sourceMappingURL=chunk-FBFESAG6.js.map
10096
+ //# sourceMappingURL=chunk-FBFESAG6.js.map
package/dist/index.cjs CHANGED
@@ -1,24 +1,24 @@
1
1
  'use strict';
2
2
 
3
- var chunkC5WJFSMN_cjs = require('./chunk-C5WJFSMN.cjs');
3
+ var chunkCOQKFGKZ_cjs = require('./chunk-COQKFGKZ.cjs');
4
4
 
5
5
 
6
6
 
7
7
  Object.defineProperty(exports, "QaStudio", {
8
8
  enumerable: true,
9
- get: function () { return chunkC5WJFSMN_cjs.Qapture; }
9
+ get: function () { return chunkCOQKFGKZ_cjs.Qapture; }
10
10
  });
11
11
  Object.defineProperty(exports, "Qapture", {
12
12
  enumerable: true,
13
- get: function () { return chunkC5WJFSMN_cjs.Qapture; }
13
+ get: function () { return chunkCOQKFGKZ_cjs.Qapture; }
14
14
  });
15
15
  Object.defineProperty(exports, "deleteQaDatabase", {
16
16
  enumerable: true,
17
- get: function () { return chunkC5WJFSMN_cjs.deleteQaDatabase; }
17
+ get: function () { return chunkCOQKFGKZ_cjs.deleteQaDatabase; }
18
18
  });
19
19
  Object.defineProperty(exports, "initQaStudio", {
20
20
  enumerable: true,
21
- get: function () { return chunkC5WJFSMN_cjs.initQaStudio; }
21
+ get: function () { return chunkCOQKFGKZ_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-BM3ESR2O.js';
1
+ export { Qapture as QaStudio, Qapture, deleteQaDatabase, initQaStudio } from './chunk-FBFESAG6.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 chunkC5WJFSMN_cjs = require('./chunk-C5WJFSMN.cjs');
4
+ var chunkCOQKFGKZ_cjs = require('./chunk-COQKFGKZ.cjs');
5
5
 
6
6
 
7
7
 
8
8
  Object.defineProperty(exports, "QaStudio", {
9
9
  enumerable: true,
10
- get: function () { return chunkC5WJFSMN_cjs.Qapture; }
10
+ get: function () { return chunkCOQKFGKZ_cjs.Qapture; }
11
11
  });
12
12
  Object.defineProperty(exports, "Qapture", {
13
13
  enumerable: true,
14
- get: function () { return chunkC5WJFSMN_cjs.Qapture; }
14
+ get: function () { return chunkCOQKFGKZ_cjs.Qapture; }
15
15
  });
16
16
  Object.defineProperty(exports, "initQaStudio", {
17
17
  enumerable: true,
18
- get: function () { return chunkC5WJFSMN_cjs.initQaStudio; }
18
+ get: function () { return chunkCOQKFGKZ_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-BM3ESR2O.js';
2
+ export { Qapture as QaStudio, Qapture, initQaStudio } from './chunk-FBFESAG6.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 chunkC5WJFSMN_cjs = require('./chunk-C5WJFSMN.cjs');
3
+ var chunkCOQKFGKZ_cjs = require('./chunk-COQKFGKZ.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 chunkC5WJFSMN_cjs.initQaStudio; }
41
+ get: function () { return chunkCOQKFGKZ_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-BM3ESR2O.js';
1
+ export { initQaStudio } from './chunk-FBFESAG6.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.3",
3
+ "version": "0.7.5",
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",
@@ -90,6 +90,7 @@
90
90
  "browser-test": "node scripts/browser-test.mjs",
91
91
  "capture-accuracy-test": "node scripts/capture-accuracy-test.mjs",
92
92
  "element-capture-test": "node scripts/element-capture-test.mjs",
93
+ "svg-vars-test": "node scripts/svg-vars-test.mjs",
93
94
  "loop-features-test": "node scripts/loop-features-test.mjs",
94
95
  "walk-test": "node scripts/walk-test.mjs",
95
96
  "modern-css-test": "node scripts/modern-css-test.mjs",