@m2farhood-2/qapture 0.2.0
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/LICENSE +26 -0
- package/README.md +397 -0
- package/dist/bin/init.cjs +1186 -0
- package/dist/chunk-CJQHPDT7.cjs +3831 -0
- package/dist/chunk-OPZF4IVW.js +3823 -0
- package/dist/index.cjs +20 -0
- package/dist/index.d.cts +179 -0
- package/dist/index.d.ts +179 -0
- package/dist/index.js +3 -0
- package/dist/next.cjs +21 -0
- package/dist/next.d.cts +1 -0
- package/dist/next.d.ts +1 -0
- package/dist/next.js +4 -0
- package/dist/standalone.cjs +39 -0
- package/dist/standalone.d.cts +1 -0
- package/dist/standalone.d.ts +1 -0
- package/dist/standalone.js +32 -0
- package/package.json +116 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 QA Studio contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
Icon path data in src/icons/ is derived from Lucide (https://lucide.dev),
|
|
26
|
+
which is licensed under the ISC License. See src/icons/Icon.tsx for details.
|
package/README.md
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
# Qapture
|
|
2
|
+
|
|
3
|
+
> Drop-in, AI-aware, 100% client-side QA capture widget — ships **zero AI**.
|
|
4
|
+
|
|
5
|
+
A human tester walks your live web app, annotates elements or regions (auto-screenshot + note), follows a **graded testing journey** (red / amber / green risk zones), and exports a ZIP. That ZIP leads with an **induction preamble** your **own** terminal coding agent (Claude Code, Cursor, Windsurf, …) reads so it already knows your project — locating code from each point's CSS selector + screenshot, making the change, verifying it, and grading RED-zone coverage.
|
|
6
|
+
|
|
7
|
+
No model is bundled. No API keys. No network calls. The widget is 100% client-side and keyless; notes live in the tester's browser (IndexedDB) until they export. The CLI scaffolder is deterministic and AI-free. **The AI is yours.**
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install qapture
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Contents
|
|
16
|
+
|
|
17
|
+
- [Quick Start](#quick-start)
|
|
18
|
+
- [Config Reference](#config-reference)
|
|
19
|
+
- [Graded Risk Model](#graded-risk-model)
|
|
20
|
+
- [Export and AI Handoff](#export-and-ai-handoff)
|
|
21
|
+
- [CLI](#cli)
|
|
22
|
+
- [Launcher Gating](#launcher-gating)
|
|
23
|
+
- [Browser and SSR Support](#browser-and-ssr-support)
|
|
24
|
+
- [Isolation and Known Limitations](#isolation-and-known-limitations)
|
|
25
|
+
- [Uninstall](#uninstall)
|
|
26
|
+
- [License](#license)
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
### React (any)
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import { Qapture } from 'qapture';
|
|
36
|
+
import type { QaConfig } from 'qapture';
|
|
37
|
+
|
|
38
|
+
const config: QaConfig = {
|
|
39
|
+
namespace: 'my-app',
|
|
40
|
+
brand: { label: 'My App QA' },
|
|
41
|
+
hotkey: 'shift+alt+q',
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Render once near your app root.
|
|
45
|
+
// Dev-only by default — invisible in production unless alwaysVisible is set.
|
|
46
|
+
function App() {
|
|
47
|
+
return (
|
|
48
|
+
<>
|
|
49
|
+
<RouterAndLayout />
|
|
50
|
+
<Qapture config={config} />
|
|
51
|
+
</>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`<Qapture>` renders `null` on the server and is SSR-safe. On mount it attaches an isolated Shadow DOM host to `document.body`; on unmount it tears it down cleanly. Config is read once at mount time — changes to the prop after mount are ignored.
|
|
57
|
+
|
|
58
|
+
### Next.js App Router
|
|
59
|
+
|
|
60
|
+
`qapture/next` re-exports the same component but ships with a `'use client'` directive prepended to its bundle output — no extra wrapper file needed:
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
// app/layout.tsx
|
|
64
|
+
import { Qapture } from 'qapture/next';
|
|
65
|
+
import config from '../qa.config';
|
|
66
|
+
|
|
67
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
68
|
+
return (
|
|
69
|
+
<html>
|
|
70
|
+
<body>
|
|
71
|
+
{children}
|
|
72
|
+
<Qapture config={config} />
|
|
73
|
+
</body>
|
|
74
|
+
</html>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Standalone (non-React)
|
|
80
|
+
|
|
81
|
+
For apps without React — plain HTML, Vue, Svelte, Astro islands, etc. Use the imperative `initQaStudio()` from `qapture/standalone`:
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
import { initQaStudio } from 'qapture/standalone';
|
|
85
|
+
|
|
86
|
+
const instance = initQaStudio({ namespace: 'my-app', brand: { label: 'My App' } });
|
|
87
|
+
|
|
88
|
+
// Later, to unmount and clean up:
|
|
89
|
+
instance.destroy();
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Or use the registered `<qapture-widget>` custom element — accepts a `config` attribute (JSON string) or a `.config` property:
|
|
93
|
+
|
|
94
|
+
```html
|
|
95
|
+
<script type="module" src="/dist/standalone.js"></script>
|
|
96
|
+
|
|
97
|
+
<!-- attribute-based config -->
|
|
98
|
+
<qapture-widget config='{"namespace":"my-app"}'></qapture-widget>
|
|
99
|
+
|
|
100
|
+
<!-- or property-based config (full object, no JSON serialization needed) -->
|
|
101
|
+
<qapture-widget id="qa"></qapture-widget>
|
|
102
|
+
<script>
|
|
103
|
+
document.getElementById('qa').config = {
|
|
104
|
+
namespace: 'my-app',
|
|
105
|
+
brand: { label: 'My App' },
|
|
106
|
+
};
|
|
107
|
+
</script>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Config Reference
|
|
113
|
+
|
|
114
|
+
All fields are optional. Passing an empty object (or no config at all) produces a valid, usable widget with sensible defaults.
|
|
115
|
+
|
|
116
|
+
### `QaConfig`
|
|
117
|
+
|
|
118
|
+
| Field | Type | Default | Description |
|
|
119
|
+
|---|---|---|---|
|
|
120
|
+
| `namespace` | `string` | `'qapture'` | Prefix for IndexedDB (`${namespace}-db`) and localStorage keys (`${namespace}:*`). Use a unique value per project to avoid storage collisions on the same origin. |
|
|
121
|
+
| `theme` | `Partial<QaTheme>` | See QaTheme defaults | Override any subset of the 9 colour tokens. Unspecified tokens keep their defaults. |
|
|
122
|
+
| `brand` | `{ label?: string }` | `{ label: 'Qapture' }` | Panel heading label. |
|
|
123
|
+
| `loginField` | `{ en: string; ar?: string }` | `{ en: 'Username', ar: 'اسم المستخدم' }` | Display label for the login column in the Credentials tab. |
|
|
124
|
+
| `credentials` | `QaCredential[]` | `[]` | DEV/TEST/SEED login rows shown in the Credentials tab. |
|
|
125
|
+
| `journey` | `QaJourneyLane[]` | `[]` | Role-grouped testing journey shown in the Guide tab. |
|
|
126
|
+
| `preamble` | `QaPreamble` | `null` | AI agent handoff context block embedded in the export. |
|
|
127
|
+
| `rtl` | `boolean` | `false` | When `true`, the UI initialises in Arabic / RTL mode. |
|
|
128
|
+
| `visible` | `boolean \| undefined` | `undefined` | `true` = always show; `false` = always hide; `undefined` = dev-only (hidden in production). |
|
|
129
|
+
| `alwaysVisible` | `boolean` | `false` | When `true`, overrides `visible` and shows the panel even in production. |
|
|
130
|
+
| `hotkey` | `string` | `'shift+alt+q'` | Keyboard shortcut that toggles the panel open/closed. |
|
|
131
|
+
|
|
132
|
+
### `QaTheme`
|
|
133
|
+
|
|
134
|
+
Nine CSS custom-property tokens (`--qa-*`) that control the widget chrome. All values are CSS color strings.
|
|
135
|
+
|
|
136
|
+
| Token | Default | Role |
|
|
137
|
+
|---|---|---|
|
|
138
|
+
| `primary` | `#4f46e5` | Primary buttons, active states |
|
|
139
|
+
| `primaryDark` | `#3730a3` | Hover / pressed states |
|
|
140
|
+
| `accent` | `#7c3aed` | Highlights, badges |
|
|
141
|
+
| `accentDark` | `#6d28d9` | Accent hover |
|
|
142
|
+
| `sage` | `#6b7280` | Muted text, borders |
|
|
143
|
+
| `cream` | `#f8fafc` | Panel background |
|
|
144
|
+
| `mauve` | `#a78bfa` | Soft decorative surfaces |
|
|
145
|
+
| `surface` | `#ffffff` | Cards, inputs |
|
|
146
|
+
| `ink` | `#1f2937` | Body text |
|
|
147
|
+
|
|
148
|
+
### `QaCredential`
|
|
149
|
+
|
|
150
|
+
| Field | Type | Required | Description |
|
|
151
|
+
|---|---|---|---|
|
|
152
|
+
| `role` | `string` | yes | English role label (also used as the stable tracker key). |
|
|
153
|
+
| `roleAr` | `string` | no | Arabic role label. |
|
|
154
|
+
| `login` | `string` | yes | Username / email / phone shown in the table. |
|
|
155
|
+
| `password` | `string` | yes (may be empty) | Password shown in the table. |
|
|
156
|
+
| `seeded` | `boolean` | no | `false` renders the row muted to indicate the credential is not yet seeded. |
|
|
157
|
+
| `hint` | `{ en: string; ar?: string }` | no | Short contextual note shown next to the row. |
|
|
158
|
+
|
|
159
|
+
Credentials are for **DEV / TEST / SEED environments only** — see [Security](#security-model) below.
|
|
160
|
+
|
|
161
|
+
### `QaJourneyLane`
|
|
162
|
+
|
|
163
|
+
| Field | Type | Required | Description |
|
|
164
|
+
|---|---|---|---|
|
|
165
|
+
| `id` | `string` | yes | Stable identifier; combined with `step.path` to form the checked-step key `${lane.id}::${step.path}`. |
|
|
166
|
+
| `role` | `QaBilingual` | yes | Role label — a plain string or `{ en: string; ar?: string }`. |
|
|
167
|
+
| `steps` | `QaJourneyStep[]` | yes | Ordered list of steps for this lane. |
|
|
168
|
+
| `color` | `string` | no | Accent color for the lane header (any CSS color string). |
|
|
169
|
+
|
|
170
|
+
### `QaJourneyStep`
|
|
171
|
+
|
|
172
|
+
| Field | Type | Required | Description |
|
|
173
|
+
|---|---|---|---|
|
|
174
|
+
| `path` | `string` | yes | Route or logical screen identifier (e.g. `/checkout`, `/admin (Users)`). |
|
|
175
|
+
| `what` | `QaBilingual` | yes | Tester instructions — a plain string or `{ en: string; ar?: string }`. |
|
|
176
|
+
| `risk` | `'red' \| 'amber' \| 'green'` | no | Risk classification. Omitted steps count as `'green'` in coverage calculations. |
|
|
177
|
+
| `riskWhy` | `string` | no | One-line explanation of why the step is risky, shown in the Guide tab and embedded in the export Coverage Report. |
|
|
178
|
+
|
|
179
|
+
### `QaBilingual`
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
type QaBilingual = string | { en: string; ar?: string };
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
A plain string is language-neutral and displayed in both languages. An object enables the panel's EN/AR language toggle.
|
|
186
|
+
|
|
187
|
+
### `QaPreamble`
|
|
188
|
+
|
|
189
|
+
Freeform AI handoff context embedded verbatim in the export. All fields are optional; extra keys beyond the listed ones are also allowed.
|
|
190
|
+
|
|
191
|
+
| Field | Type | Description |
|
|
192
|
+
|---|---|---|
|
|
193
|
+
| `projectName` | `string` | Project name shown as the preamble heading. |
|
|
194
|
+
| `oneLiner` | `string` | One-sentence description embedded as a blockquote. |
|
|
195
|
+
| `stack` | `string` | Tech stack description (framework, ORM, database, etc.). |
|
|
196
|
+
| `runCommands` | `string \| string[]` | Commands to start the dev server and seed the database. |
|
|
197
|
+
| `conventions` | `string \| string[]` | Numbered codebase conventions for the agent to follow. |
|
|
198
|
+
| `invariants` | `string \| string[]` | Rules the agent must never violate (e.g. "prices must be >= 0"). |
|
|
199
|
+
| `verifySteps` | `string \| string[]` | Steps to verify a fix in the running app. |
|
|
200
|
+
| `additionalContext` | `string` | Freeform context not covered by the fields above. |
|
|
201
|
+
|
|
202
|
+
Array fields also accept a plain newline-separated string; the export normalises both forms before rendering.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Graded Risk Model
|
|
207
|
+
|
|
208
|
+
Each journey step carries a `risk` value. The Guide tab shows a coloured dot beside every step; the export leads with a coverage report scored on RED steps.
|
|
209
|
+
|
|
210
|
+
| Risk | When to use | Verification rule |
|
|
211
|
+
|---|---|---|
|
|
212
|
+
| `red` | Payment, checkout, authentication, order state mutations, refunds, payouts, user data changes — anything **irreversible or financial** | Must verify; uncovered reds are flagged by the receiving agent before it acts on any points |
|
|
213
|
+
| `amber` | Important flows that are **recoverable** — cart, product CRUD, seller dashboard, messaging, search | Change carefully; verify end-to-end |
|
|
214
|
+
| `green` | Informational / display only — static pages, labels, copy, colour, analytics views, tooltips | Change freely; quick smoke test |
|
|
215
|
+
|
|
216
|
+
Use `riskWhy` to document the specific reason a step is `red`. This text is embedded in the export's Coverage Report so the receiving agent understands the invariants before touching any code.
|
|
217
|
+
|
|
218
|
+
### Coverage tiers
|
|
219
|
+
|
|
220
|
+
Coverage is **scored on RED steps only**. The Guide tab shows `RED N/M covered`; the export includes the tier label.
|
|
221
|
+
|
|
222
|
+
| Tier | Red score |
|
|
223
|
+
|---|---|
|
|
224
|
+
| Minimal | < 50% of red steps covered |
|
|
225
|
+
| Adequate | 50–79% covered |
|
|
226
|
+
| Full | 80–99% covered |
|
|
227
|
+
| Complete | 100% covered |
|
|
228
|
+
|
|
229
|
+
When there are no red steps the score is vacuously Complete. The receiving agent is instructed to flag uncovered RED steps before acting on any annotation in the export.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Export and AI Handoff
|
|
234
|
+
|
|
235
|
+
### The workflow
|
|
236
|
+
|
|
237
|
+
1. **Capture** — click an element or drag a region on the live page. Qapture auto-screenshots the visible page, opens the note editor. Write a description; save.
|
|
238
|
+
2. **Guide** — tick steps in the journey as you walk through them. The Guide tab tracks red-zone coverage and shows the current tier.
|
|
239
|
+
3. **Export** — click Export in the panel. A `qa-notes-<timestamp>.zip` downloads to your machine. Give it a meaningful name.
|
|
240
|
+
4. **Handoff** — drop the ZIP into your terminal coding agent's context. If you use Claude Code, the `.claude/skills/qapture/SKILL.md` the CLI generated (or the `AGENTS.md` snippet) primes the agent automatically when the ZIP is attached.
|
|
241
|
+
5. **Agent acts** — the agent reads `notes.md`, internalises the preamble (project context, theme tokens, dev credentials, red-zone coverage, invariants), flags any uncovered RED steps, then works through each `## Point N` annotation: locates the code via the selector + screenshot, makes the change, verifies it in the running app, and produces a graded summary.
|
|
242
|
+
|
|
243
|
+
### ZIP layout
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
qa-notes-<timestamp>.zip
|
|
247
|
+
├── notes.md
|
|
248
|
+
└── screenshots/
|
|
249
|
+
├── point-1.png
|
|
250
|
+
├── point-2.png
|
|
251
|
+
└── ...
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### `notes.md` structure
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
<!-- Qapture Export Preamble — read before acting on any point. -->
|
|
258
|
+
|
|
259
|
+
# Project — QA Handoff
|
|
260
|
+
> one-liner
|
|
261
|
+
|
|
262
|
+
## Project (name, stack, run commands)
|
|
263
|
+
## Theme Tokens (9-token colour table)
|
|
264
|
+
## Conventions (numbered codebase rules)
|
|
265
|
+
## Login Context (DEV/TEST/SEED credentials table + warning)
|
|
266
|
+
## Coverage Report (red/amber/green totals + uncovered RED list)
|
|
267
|
+
## How to Verify a Fix
|
|
268
|
+
## Invariants (Do Not Break)
|
|
269
|
+
## Additional Context
|
|
270
|
+
|
|
271
|
+
---NOTES---
|
|
272
|
+
|
|
273
|
+
# Brand Testing Notes
|
|
274
|
+
|
|
275
|
+
## Point 1
|
|
276
|
+
- **Page:** /some-route
|
|
277
|
+
- **Selector:** `#element-id`
|
|
278
|
+
- **When:** <timestamp>
|
|
279
|
+
- **Screenshot:** screenshots/point-1.png
|
|
280
|
+
|
|
281
|
+
Tester's note text...
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## Point 2
|
|
286
|
+
...
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
The preamble degrades gracefully — sections with no data are marked `(not provided)` rather than omitted, so the agent always receives the full structure.
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## CLI
|
|
294
|
+
|
|
295
|
+
The CLI scaffolds `qa.config`, the agent skill, and `AGENTS.md` into any repository. It is **deterministic, AI-free, and network-free** — no model call, no network request, no `require()`-ing of target project files.
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
npx qapture init [target-dir] [--force]
|
|
299
|
+
npx qapture version
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
`target-dir` defaults to the current directory. `--force` overwrites existing `qa.config.*` and `qa.preamble.md` (SKILL.md is always refreshed regardless).
|
|
303
|
+
|
|
304
|
+
### What it detects and generates
|
|
305
|
+
|
|
306
|
+
| Step | What happens |
|
|
307
|
+
|---|---|
|
|
308
|
+
| Route detection | Scans `src/`, `app/`, `pages/` for route files; generates journey lanes with placeholder `'green'` steps for you to grade |
|
|
309
|
+
| Theme detection | Reads `tailwind.config.*` and CSS files for colour values |
|
|
310
|
+
| Credential detection | Scans `.env.example` and seeder/seed files for test logins. **Never reads `.env`, `.env.local`, `.env.production`, or any real secrets file** — enforced by a hard blocklist |
|
|
311
|
+
| `qa.config.js` / `.ts` | Generated based on detections; contains TODO comments for manual grading |
|
|
312
|
+
| `qa.preamble.md` | Starter preamble file; fill with project context and paste into `config.preamble` |
|
|
313
|
+
| `.claude/skills/qapture/SKILL.md` | Claude Code agent skill (always refreshed — this is a vendor artifact) |
|
|
314
|
+
| `AGENTS.md` | Idempotent merge with sentinel guards; safe to run repeatedly |
|
|
315
|
+
|
|
316
|
+
All generated files are idempotent — existing `qa.config.*` and `qa.preamble.md` are skipped unless `--force` is passed.
|
|
317
|
+
|
|
318
|
+
### IDE notes
|
|
319
|
+
|
|
320
|
+
After `init`, copy the agent instructions into your IDE's rules directory:
|
|
321
|
+
|
|
322
|
+
- **Cursor** — copy the `qapture` block from `AGENTS.md` into `.cursor/rules/qapture.md`
|
|
323
|
+
- **Windsurf** — append the `qapture` block from `AGENTS.md` to `.windsurf/rules.md`
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Launcher Gating
|
|
328
|
+
|
|
329
|
+
By default the widget is **dev-only** — hidden when `NODE_ENV === 'production'`.
|
|
330
|
+
|
|
331
|
+
| Config | Behaviour |
|
|
332
|
+
|---|---|
|
|
333
|
+
| `visible: undefined` (default) | Dev-only — hidden in production builds |
|
|
334
|
+
| `visible: false` | Always hidden (useful for a temporary disable) |
|
|
335
|
+
| `visible: true` | Always shown |
|
|
336
|
+
| `alwaysVisible: true` | Always shown — overrides `visible` |
|
|
337
|
+
|
|
338
|
+
The **hotkey** (default: `Shift+Alt+Q`) toggles the panel open/closed regardless of `visible`. Change it via `hotkey: 'ctrl+shift+q'` or any `modifier+key` combination recognised by the browser `keydown` event.
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Browser and SSR Support
|
|
343
|
+
|
|
344
|
+
- **Peer dependencies:** React >= 18, ReactDOM >= 18.
|
|
345
|
+
- **SSR-safe:** `Qapture`, `initQaStudio()`, and `<qapture-widget>` all guard `typeof window` and return no-ops on the server. Nothing is rendered server-side.
|
|
346
|
+
- **Next.js App Router:** use `qapture/next` (which has `'use client'` baked into its bundle output) rather than `qapture` directly. This prevents the "attempted to call a Client Component from the Server" error.
|
|
347
|
+
- **Node >= 18** required for the CLI.
|
|
348
|
+
- Heavy dependencies (`jszip`, `html2canvas`) are loaded as **lazy code-split chunks** — they do not affect initial page load and are only fetched when the user triggers a capture or export action.
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## Isolation and Known Limitations
|
|
353
|
+
|
|
354
|
+
### What works everywhere
|
|
355
|
+
|
|
356
|
+
- **Shadow DOM isolation** — the widget chrome (CSS, events) lives inside an open shadow root attached to `<body>`. The host app's CSS frameworks (Tailwind, Bootstrap, etc.) cannot leak into the widget, and the widget's styles cannot leak out. Works with no Tailwind installed in the host.
|
|
357
|
+
- **React peer independence** — the widget's React tree lives inside the shadow root; it does not conflict with the host app's React version or tree.
|
|
358
|
+
- **Storage degradation** — IndexedDB and localStorage both degrade silently to in-memory storage in private browsing mode or SSR environments. Notes will not persist between sessions in private mode, but the current session works normally.
|
|
359
|
+
|
|
360
|
+
### Known limitations
|
|
361
|
+
|
|
362
|
+
- **html2canvas captures the visible light DOM only.** Content inside *other* custom elements that have their own shadow roots (not qapture's own) will not appear in screenshots. This is a limitation of html2canvas, not qapture.
|
|
363
|
+
- **`position: fixed` may shift on transformed ancestors.** If any ancestor of `document.body` has a CSS `transform`, `perspective`, or `will-change` property applied, `position: fixed` elements — including the QA panel — may be offset from their expected position. This is standard CSS containment behaviour.
|
|
364
|
+
- **Next.js App Router requires `qapture/next`.** Importing from `qapture` in a Server Component context will produce a "use client" error. Use the `/next` entry point.
|
|
365
|
+
- **Config changes after mount are ignored.** `<Qapture>` mounts once on first render (`useEffect` with `[]` deps) and ignores subsequent prop changes. To apply a new config, destroy the instance and remount.
|
|
366
|
+
- **One instance per page.** Calling `initQaStudio()` or rendering `<Qapture>` multiple times without calling `destroy()` first will append multiple widget hosts to `<body>`.
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## Security model
|
|
371
|
+
|
|
372
|
+
- **Zero AI, zero network, zero keys.** No model is bundled; no API calls are made; no telemetry is collected.
|
|
373
|
+
- **Data stays in the browser** until the tester explicitly exports a ZIP. Nothing is ever transmitted.
|
|
374
|
+
- **Credentials are DEV/TEST/SEED only.** The `credentials` config field and the Login Context in the export are intended exclusively for non-production environments.
|
|
375
|
+
- **The CLI never reads real secrets.** A hard path blocklist prevents the CLI from reading `.env`, `.env.local`, `.env.production`, certificate files, or any file under `/secrets/`, `/keys/`, `/credentials/`. Only `.env.example` and seeder files are scanned.
|
|
376
|
+
|
|
377
|
+
See [SECURITY.md](./SECURITY.md) for the full security model and vulnerability reporting instructions.
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## Uninstall
|
|
382
|
+
|
|
383
|
+
1. Remove `<Qapture />` (or `initQaStudio()` calls) from your codebase.
|
|
384
|
+
2. Uninstall the package: `npm uninstall qapture`.
|
|
385
|
+
3. Optionally delete the IndexedDB left behind — open the browser console on your app's origin and run:
|
|
386
|
+
|
|
387
|
+
```js
|
|
388
|
+
indexedDB.deleteDatabase('qapture-db'); // replace 'qapture' with your namespace value
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
4. Optionally delete scaffolded files: `qa.config.*`, `qa.preamble.md`, `.claude/skills/qapture/`, and the `qapture` block in `AGENTS.md`.
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
## License
|
|
396
|
+
|
|
397
|
+
MIT. Icon path data derived from [Lucide](https://lucide.dev) (ISC).
|