driftx 0.1.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/dist/bin.js +2983 -0
- package/dist/bin.js.map +1 -0
- package/driftx-plugin/.claude-plugin/plugin.json +10 -0
- package/driftx-plugin/skills/driftx.md +299 -0
- package/package.json +56 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: driftx
|
|
3
|
+
description: Visual comparison, accessibility audit, and layout regression for React Native and Android apps. Use when the user asks to compare their app against a design, check accessibility, detect visual regressions, inspect the component tree, or capture screenshots from a device/simulator.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# driftx — Visual Analysis for React Native & Android
|
|
7
|
+
|
|
8
|
+
Use driftx when the user wants to:
|
|
9
|
+
- Compare their running app against a design image (Figma export, mockup, etc.)
|
|
10
|
+
- Run an accessibility audit on the component tree
|
|
11
|
+
- Detect layout regressions between builds
|
|
12
|
+
- Inspect the React Native component tree on a device
|
|
13
|
+
- Capture a screenshot from a running simulator/emulator
|
|
14
|
+
|
|
15
|
+
## Prerequisites
|
|
16
|
+
|
|
17
|
+
driftx must be installed in the project: `npm install driftx` or available globally.
|
|
18
|
+
Run `npx driftx doctor` to verify the environment is set up correctly.
|
|
19
|
+
|
|
20
|
+
## Commands Reference
|
|
21
|
+
|
|
22
|
+
### `driftx compare` — Run visual analysis
|
|
23
|
+
|
|
24
|
+
The primary command. Compares a screenshot from a running device against a design image or baseline.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Compare against a design image
|
|
28
|
+
npx driftx compare --design path/to/design.png --format json
|
|
29
|
+
|
|
30
|
+
# Compare with specific device
|
|
31
|
+
npx driftx compare --design path/to/design.png --device "iPhone 16 Pro" --format json
|
|
32
|
+
|
|
33
|
+
# Use an existing screenshot instead of capturing
|
|
34
|
+
npx driftx compare --design path/to/design.png --screenshot path/to/screenshot.png --format json
|
|
35
|
+
|
|
36
|
+
# Run only specific analyses
|
|
37
|
+
npx driftx compare --design path/to/design.png --with pixel,a11y --format json
|
|
38
|
+
|
|
39
|
+
# Exclude specific analyses
|
|
40
|
+
npx driftx compare --design path/to/design.png --without pixel --format json
|
|
41
|
+
|
|
42
|
+
# Regression mode — compare against previous run (no design needed)
|
|
43
|
+
npx driftx compare --baseline --format json
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Flags:**
|
|
47
|
+
| Flag | Description |
|
|
48
|
+
|------|-------------|
|
|
49
|
+
| `--design <path>` | Path to design image (PNG). Required unless `--baseline`. |
|
|
50
|
+
| `--screenshot <path>` | Use existing screenshot instead of capturing from device. |
|
|
51
|
+
| `-d, --device <id>` | Device ID or name to capture from. |
|
|
52
|
+
| `--threshold <n>` | Pixel sensitivity threshold (0-1, default 0.1). |
|
|
53
|
+
| `--with <analyses>` | Comma-separated analyses to run (e.g., `pixel,a11y,regression`). |
|
|
54
|
+
| `--without <analyses>` | Analyses to exclude. |
|
|
55
|
+
| `--baseline` | Compare against previous run's screenshot. |
|
|
56
|
+
| `--format json` | **Always use this** for structured output. |
|
|
57
|
+
|
|
58
|
+
**Available analyses:**
|
|
59
|
+
| Analysis | What it does | Auto-enabled when |
|
|
60
|
+
|----------|-------------|-------------------|
|
|
61
|
+
| `pixel` | Pixel-level diff between screenshot and design | Design image provided |
|
|
62
|
+
| `a11y` | Accessibility audit — missing labels, small tap targets, images without alt text | Component tree available (device connected) |
|
|
63
|
+
| `regression` | Layout regression — diff against previous run | `--baseline` flag used |
|
|
64
|
+
|
|
65
|
+
**Smart defaults:** When no `--with`/`--without` is specified, analyses auto-enable based on available inputs. You don't need to specify `--with` unless you want to override.
|
|
66
|
+
|
|
67
|
+
### `driftx inspect` — Inspect component tree
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npx driftx inspect --format json
|
|
71
|
+
npx driftx inspect --device "Pixel 8" --format json
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Returns the React Native component tree with component names, testIDs, text content, and bounds.
|
|
75
|
+
|
|
76
|
+
### `driftx devices` — List connected devices
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npx driftx devices --format json
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Returns available simulators/emulators and their state (booted/offline).
|
|
83
|
+
|
|
84
|
+
### `driftx capture` — Capture screenshot
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npx driftx capture --output screenshot.png
|
|
88
|
+
npx driftx capture --device "iPhone 16 Pro" --output screenshot.png
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Captures a screenshot from the device and saves it to the specified path.
|
|
92
|
+
|
|
93
|
+
### `driftx doctor` — Check prerequisites
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
npx driftx doctor --format json
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Checks that adb, xcrun, Metro, etc. are available.
|
|
100
|
+
|
|
101
|
+
## Output Interpretation
|
|
102
|
+
|
|
103
|
+
### JSON format (CompareReport)
|
|
104
|
+
|
|
105
|
+
When using `--format json`, `driftx compare` outputs a `CompareReport`:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"report": {
|
|
110
|
+
"runId": "abc123",
|
|
111
|
+
"analyses": [
|
|
112
|
+
{
|
|
113
|
+
"analysisName": "pixel",
|
|
114
|
+
"findings": [...],
|
|
115
|
+
"summary": "Pixel diff: 2.100% — 3 regions (fail)",
|
|
116
|
+
"metadata": {
|
|
117
|
+
"diffPercentage": 2.1,
|
|
118
|
+
"diffPixels": 5000,
|
|
119
|
+
"totalPixels": 238000,
|
|
120
|
+
"regions": [...],
|
|
121
|
+
"passed": false
|
|
122
|
+
},
|
|
123
|
+
"durationMs": 450
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"analysisName": "a11y",
|
|
127
|
+
"findings": [...],
|
|
128
|
+
"summary": "2 accessibility issues found",
|
|
129
|
+
"metadata": {
|
|
130
|
+
"totalChecked": 15,
|
|
131
|
+
"issuesByType": { "label": 1, "tapTarget": 1, "image": 0, "emptyText": 0 }
|
|
132
|
+
},
|
|
133
|
+
"durationMs": 5
|
|
134
|
+
}
|
|
135
|
+
],
|
|
136
|
+
"findings": [...],
|
|
137
|
+
"summary": "...",
|
|
138
|
+
"durationMs": 460
|
|
139
|
+
},
|
|
140
|
+
"artifactDir": ".driftx/runs/abc123"
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Key fields to check:
|
|
145
|
+
- `report.analyses[].metadata.passed` — `false` means the analysis failed its threshold
|
|
146
|
+
- `report.analyses[].findings` — individual issues found
|
|
147
|
+
- `report.findings` — all findings merged across analyses
|
|
148
|
+
- `artifactDir` — where screenshots, diff masks, and region crops are saved
|
|
149
|
+
|
|
150
|
+
### Artifacts
|
|
151
|
+
|
|
152
|
+
After a compare run, artifacts are saved to `.driftx/runs/<runId>/`:
|
|
153
|
+
|
|
154
|
+
| File | Description |
|
|
155
|
+
|------|-------------|
|
|
156
|
+
| `screenshot.png` | Captured screenshot |
|
|
157
|
+
| `design.png` | Copy of the design image |
|
|
158
|
+
| `diff-mask.png` | Visual overlay showing pixel differences |
|
|
159
|
+
| `regions/<id>.png` | Cropped images of each diff region |
|
|
160
|
+
| `regression-diff-mask.png` | Diff mask for regression analysis |
|
|
161
|
+
| `result.json` | Full CompareReport as JSON |
|
|
162
|
+
| `report.md` | Human-readable markdown report |
|
|
163
|
+
|
|
164
|
+
Read `report.md` for a formatted summary. Read `result.json` for structured data. View diff mask and region crops to understand what changed visually.
|
|
165
|
+
|
|
166
|
+
### Finding categories
|
|
167
|
+
|
|
168
|
+
| Category | Source | Meaning |
|
|
169
|
+
|----------|--------|---------|
|
|
170
|
+
| `spacing`, `color`, `font`, `alignment`, `size`, `content`, `missing`, `extra` | pixel analysis | Visual differences between design and screenshot |
|
|
171
|
+
| `accessibility` | a11y analysis | Missing labels, small tap targets, images without alt text |
|
|
172
|
+
| `regression` | regression analysis | Layout changed from previous baseline |
|
|
173
|
+
|
|
174
|
+
### Finding severity
|
|
175
|
+
|
|
176
|
+
- `critical` — major visual breakage
|
|
177
|
+
- `major` — noticeable difference (missing labels, significant pixel diff)
|
|
178
|
+
- `minor` — small issues (slightly small tap targets)
|
|
179
|
+
- `info` — informational (empty text nodes)
|
|
180
|
+
|
|
181
|
+
## Workflow Patterns
|
|
182
|
+
|
|
183
|
+
### Compare against a Figma design
|
|
184
|
+
|
|
185
|
+
If the user has a Figma MCP server connected, use it to export the frame as an image first, then run driftx:
|
|
186
|
+
|
|
187
|
+
1. Use Figma MCP to get the design frame → save as PNG
|
|
188
|
+
2. Run `npx driftx compare --design <saved-image-path> --format json`
|
|
189
|
+
3. Read the JSON output to understand differences
|
|
190
|
+
4. Read artifact files (diff mask, report.md) for visual context
|
|
191
|
+
5. Suggest code fixes based on the findings
|
|
192
|
+
|
|
193
|
+
If no Figma MCP, ask the user to provide the design image path.
|
|
194
|
+
|
|
195
|
+
### Accessibility audit
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
npx driftx compare --design <path> --with a11y --format json
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Or without a design image (a11y only needs the component tree):
|
|
202
|
+
```bash
|
|
203
|
+
npx driftx compare --baseline --with a11y --format json
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Review findings with `category: 'accessibility'` and suggest fixes (add accessibilityLabel, increase tap target size, etc.).
|
|
207
|
+
|
|
208
|
+
### Regression detection
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
npx driftx compare --baseline --format json
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Compares current app state against the most recent previous run. No design file needed. Useful after code changes to verify nothing broke visually.
|
|
215
|
+
|
|
216
|
+
### Full audit (all analyses)
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
npx driftx compare --design <path> --with pixel,a11y --format json
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Runs pixel diff and accessibility audit together.
|
|
223
|
+
|
|
224
|
+
## Device Interaction
|
|
225
|
+
|
|
226
|
+
driftx can interact with the running app — tap buttons, type text, swipe, navigate.
|
|
227
|
+
|
|
228
|
+
### `driftx tap <target>` — Tap a component
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
npx driftx tap login-btn
|
|
232
|
+
npx driftx tap LoginButton
|
|
233
|
+
npx driftx tap "Submit"
|
|
234
|
+
npx driftx tap 150,300 --xy
|
|
235
|
+
npx driftx tap login-btn --device "iPhone 16 Pro"
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Returns JSON with interaction result.
|
|
239
|
+
|
|
240
|
+
### `driftx type <target> <text>` — Type text into a field
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
npx driftx type email-input "user@example.com"
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Taps the target first to focus it, then types.
|
|
247
|
+
|
|
248
|
+
### `driftx swipe <direction>` — Swipe gesture
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
npx driftx swipe up
|
|
252
|
+
npx driftx swipe down
|
|
253
|
+
npx driftx swipe left
|
|
254
|
+
npx driftx swipe right
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### `driftx go-back` — Press back button
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
npx driftx go-back
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### `driftx open-url <url>` — Open a deep link
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
npx driftx open-url "myapp://profile/123"
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Interaction Workflow
|
|
270
|
+
|
|
271
|
+
1. Write/modify code
|
|
272
|
+
2. Hot reload
|
|
273
|
+
3. `npx driftx tap "Login"` — navigate
|
|
274
|
+
4. `npx driftx compare --design mockup.png --format json` — verify
|
|
275
|
+
5. Fix issues and repeat
|
|
276
|
+
|
|
277
|
+
For form testing:
|
|
278
|
+
1. `npx driftx type email-input "test@example.com"`
|
|
279
|
+
2. `npx driftx type password-input "password123"`
|
|
280
|
+
3. `npx driftx tap "Submit"`
|
|
281
|
+
4. `npx driftx compare --baseline --format json`
|
|
282
|
+
|
|
283
|
+
## Configuration
|
|
284
|
+
|
|
285
|
+
driftx uses `.driftxrc.json` in the project root. Run `npx driftx init` to create one. Key settings:
|
|
286
|
+
|
|
287
|
+
- `platform`: `"android"` or `"ios"`
|
|
288
|
+
- `metroPort`: Metro bundler port (default 8081)
|
|
289
|
+
- `threshold`: Pixel sensitivity (default 0.1)
|
|
290
|
+
- `diffThreshold`: Pass/fail percentage threshold (default 0.01)
|
|
291
|
+
- `analyses.default`: Default analyses to run
|
|
292
|
+
- `analyses.disabled`: Analyses to never run
|
|
293
|
+
|
|
294
|
+
## Error Handling
|
|
295
|
+
|
|
296
|
+
- If no booted device is found, suggest the user start a simulator/emulator
|
|
297
|
+
- If Metro is not running, suggest `npx react-native start`
|
|
298
|
+
- If `driftx doctor` shows missing prerequisites, show the user which tools to install
|
|
299
|
+
- If compare fails with "Either --design or --baseline must be provided", one of those flags is required
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "driftx",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Visual diff tool for React Native and Android development",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/bin.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"driftx": "./dist/bin.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"driftx-plugin"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsup",
|
|
16
|
+
"dev": "tsup --watch",
|
|
17
|
+
"test": "vitest run",
|
|
18
|
+
"test:watch": "vitest",
|
|
19
|
+
"test:coverage": "vitest run --coverage",
|
|
20
|
+
"test:device": "DRIFTX_DEVICE_TESTS=1 vitest run test/device/",
|
|
21
|
+
"lint": "tsc --noEmit"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"react-native",
|
|
25
|
+
"visual-diff",
|
|
26
|
+
"screenshot",
|
|
27
|
+
"testing"
|
|
28
|
+
],
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@inquirer/prompts": "^8.3.0",
|
|
35
|
+
"commander": "^14.0.3",
|
|
36
|
+
"cosmiconfig": "^9.0.1",
|
|
37
|
+
"nanoid": "^5.1.6",
|
|
38
|
+
"picocolors": "^1.1.1",
|
|
39
|
+
"pino": "^10.3.1",
|
|
40
|
+
"pino-pretty": "^13.1.3",
|
|
41
|
+
"pixelmatch": "^7.1.0",
|
|
42
|
+
"pngjs": "^7.0.0",
|
|
43
|
+
"sharp": "^0.34.5",
|
|
44
|
+
"ws": "^8.19.0",
|
|
45
|
+
"zod": "^4.3.6"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^25.4.0",
|
|
49
|
+
"@types/pngjs": "^6.0.5",
|
|
50
|
+
"@types/ws": "^8.18.1",
|
|
51
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
52
|
+
"tsup": "^8.5.1",
|
|
53
|
+
"typescript": "^5.9.3",
|
|
54
|
+
"vitest": "^4.0.18"
|
|
55
|
+
}
|
|
56
|
+
}
|