peachsim 0.1.899 → 0.1.908

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.
Files changed (103) hide show
  1. package/README.md +8 -0
  2. package/cli/app-fonts.ts +27 -2
  3. package/cli/commands/box/bundle-plane.ts +13 -4
  4. package/cli/commands/box.ts +22 -1
  5. package/cli/commands/control.ts +29 -16
  6. package/cli/commands/flow.ts +8 -1
  7. package/cli/commands/platform.ts +15 -2
  8. package/cli/commands/skills.ts +125 -10
  9. package/cli/commands/upload.ts +53 -39
  10. package/cli/flow-live-status.ts +11 -6
  11. package/detox/expectations.ts +22 -15
  12. package/detox/index.ts +4 -19
  13. package/dist-lib/agent-daemon-client.cjs +1 -1
  14. package/dist-lib/agent-events.cjs +1 -1
  15. package/dist-lib/agent-identity.cjs +1 -1
  16. package/dist-lib/agent-sessions.cjs +1 -1
  17. package/dist-lib/attached-projects.cjs +1 -1
  18. package/dist-lib/auth/shared-session.cjs +1 -1
  19. package/dist-lib/backend-origin.cjs +1 -1
  20. package/dist-lib/beta.cjs +1 -1
  21. package/dist-lib/beta.mjs +1 -1
  22. package/dist-lib/bridge-constants.cjs +1 -1
  23. package/dist-lib/bridge-contract-input.cjs +1 -1
  24. package/dist-lib/bridge-contract-input.mjs +1 -1
  25. package/dist-lib/bridge-contract.cjs +1 -1
  26. package/dist-lib/bridge-contract.mjs +1 -1
  27. package/dist-lib/bundle-channel.cjs +8 -5
  28. package/dist-lib/capture-contract.cjs +1 -1
  29. package/dist-lib/capture-contract.mjs +1 -1
  30. package/dist-lib/cli-constants.cjs +1 -1
  31. package/dist-lib/cloud-contract.cjs +1 -1
  32. package/dist-lib/cloud-contract.mjs +1 -1
  33. package/dist-lib/cloud-test-args.cjs +1 -1
  34. package/dist-lib/cloud-test-args.mjs +1 -1
  35. package/dist-lib/cloud.cjs +1 -1
  36. package/dist-lib/cloud.mjs +1 -1
  37. package/dist-lib/config.cjs +1 -1
  38. package/dist-lib/detox/index.cjs +15 -18
  39. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  40. package/dist-lib/expo-swift-bundle.cjs +8 -5
  41. package/dist-lib/expo-swift-metadata.cjs +1 -1
  42. package/dist-lib/expo-swift-metadata.mjs +1 -1
  43. package/dist-lib/home-paths.cjs +1 -1
  44. package/dist-lib/host/bridge-host.cjs +8 -5
  45. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  46. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  47. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  48. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  49. package/dist-lib/host/websocket-proxy.cjs +1 -1
  50. package/dist-lib/index.cjs +1 -1
  51. package/dist-lib/jump-to-source-babel.cjs +1 -1
  52. package/dist-lib/jump-to-source-native.cjs +1 -1
  53. package/dist-lib/kotlin-host.cjs +1 -1
  54. package/dist-lib/menu.cjs +1 -1
  55. package/dist-lib/menu.mjs +1 -1
  56. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  57. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  58. package/dist-lib/metro-production-bundle.cjs +1 -1
  59. package/dist-lib/metro-production-bundle.mjs +1 -1
  60. package/dist-lib/metro.cjs +1 -1
  61. package/dist-lib/native-app-identity.cjs +1 -1
  62. package/dist-lib/native-app-identity.mjs +1 -1
  63. package/dist-lib/native-firebase-metadata.cjs +1 -1
  64. package/dist-lib/native-firebase-metadata.mjs +1 -1
  65. package/dist-lib/plist.cjs +1 -1
  66. package/dist-lib/plist.mjs +1 -1
  67. package/dist-lib/profiles.cjs +1 -1
  68. package/dist-lib/public-brand.cjs +1 -1
  69. package/dist-lib/react-native-host-modules.cjs +1 -1
  70. package/dist-lib/react-native-host-modules.mjs +1 -1
  71. package/dist-lib/render-mode.cjs +1 -1
  72. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  73. package/dist-lib/sdk.cjs +1 -1
  74. package/dist-lib/sdk.mjs +1 -1
  75. package/dist-lib/skills.cjs +136 -25
  76. package/dist-lib/swift-compile.cjs +1 -1
  77. package/dist-lib/swift-compile.mjs +1 -1
  78. package/dist-lib/swift-package.cjs +1 -1
  79. package/dist-lib/swift-package.mjs +1 -1
  80. package/dist-lib/swift-submit.cjs +1 -1
  81. package/dist-lib/swift-submit.mjs +1 -1
  82. package/dist-lib/swift-toolchain.cjs +1 -1
  83. package/dist-lib/swift.cjs +1 -1
  84. package/dist-lib/vite-swift.cjs +1 -1
  85. package/dist-lib/vite.cjs +1 -1
  86. package/dist-lib/worklet-executable.cjs +1 -1
  87. package/dist-lib/worklet-executable.mjs +1 -1
  88. package/kotlin/fork/NOTICE +3 -0
  89. package/kotlin/fork/README.md +10 -2
  90. package/kotlin/fork/build.sh +14 -14
  91. package/kotlin/fork/compose-worker-globals.patch +105 -0
  92. package/kotlin/fork/fork.json +3 -3
  93. package/kotlin/fork/publish.ts +8 -1
  94. package/kotlin/fork/skia-android-shaping.patch +30 -0
  95. package/kotlin/fork/skiko-local.init.gradle +10 -0
  96. package/kotlin/fork/skiko-worker-clock.patch +17 -0
  97. package/kotlin/toolchain.json +9 -9
  98. package/package.json +1 -1
  99. package/skills/peach-debug/SKILL.md +15 -153
  100. package/skills/peach-debug/references/accessibility.md +102 -0
  101. package/skills/peach-debug/references/commands.md +56 -0
  102. package/src/expo-swift-host.ts +15 -4
  103. package/src/vite-plugin.ts +3 -3
@@ -22,7 +22,7 @@
22
22
  "tag": "v1.13.0-alpha01",
23
23
  "commit": "a9e47e8aa04c95cfe572ab4297c770f3cbfb234e",
24
24
  "patch": "compose-worker-globals.patch",
25
- "patchSha256": "7a4002f6ea07fc78b23e8d16851b586521b221ba3e7a9690420b3c1d6be69cd9"
25
+ "patchSha256": "71beda710e878c2d59d6481bcc4b8a1f256730b3f3b3cad4228c9b2442b5244a"
26
26
  },
27
27
  {
28
28
  "name": "skiko",
@@ -31,7 +31,7 @@
31
31
  "tag": "v0.152.0-alpha02",
32
32
  "commit": "96378296bb23c23c5f30cd2b0d42d6f8fdeff730",
33
33
  "patch": "skiko-worker-clock.patch",
34
- "patchSha256": "44b38c49b9f8d860573ec43d54d7ce74c0ad36da59100ec482b46953d9d6ef81"
34
+ "patchSha256": "37bbfbb362a8759c6648969b20b32c5b6aa2ff140fd6dbdbbdb910bbb74783a8"
35
35
  },
36
36
  {
37
37
  "name": "skia",
@@ -40,7 +40,7 @@
40
40
  "tag": "m152-2ca5fe6a81",
41
41
  "commit": "2ca5fe6a8170e2f55f519d9de083be4dccc06e70",
42
42
  "patch": "skia-android-shaping.patch",
43
- "patchSha256": "b6293707ff832c77fa6016d7d1e469b7e3d24e31b7af7d70cdfb5ee1bc68ba7f"
43
+ "patchSha256": "04fae34c4e29dd92ce4b94dc57ace28848a46fff8e745c820877a7e03bc00867"
44
44
  }
45
45
  ]
46
46
  }
@@ -22,7 +22,14 @@ const files = readdirSync(delivered)
22
22
  const record = files.filter(
23
23
  (name) =>
24
24
  name.endsWith('.patch') ||
25
- ['fork.json', 'LICENSE', 'SKIA_LICENSE', 'NOTICE', 'artifacts.sha256'].includes(name),
25
+ [
26
+ 'fork.json',
27
+ 'LICENSE',
28
+ 'SKIA_LICENSE',
29
+ 'NOTICE',
30
+ 'artifacts.sha256',
31
+ 'skiko-local.init.gradle',
32
+ ].includes(name),
26
33
  )
27
34
  if (
28
35
  record.length !==
@@ -157,3 +157,33 @@ diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.c
157
157
  }
158
158
 
159
159
  if ((fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag) == 0) {
160
+ diff --git a/modules/skparagraph/include/DartTypes.h b/modules/skparagraph/include/DartTypes.h
161
+ --- a/modules/skparagraph/include/DartTypes.h
162
+ +++ b/modules/skparagraph/include/DartTypes.h
163
+ @@ -1,3 +1,4 @@
164
+ +// modified for peach: Android center alignment retains its integer origin.
165
+ // Copyright 2019 Google LLC
166
+
167
+ #ifndef DartTypes_DEFINED
168
+ @@ -56,6 +57,7 @@
169
+ kJustify,
170
+ kStart,
171
+ kEnd,
172
+ + kCenterAndroid,
173
+ };
174
+
175
+ enum class TextDirection {
176
+ diff --git a/modules/skparagraph/src/TextLine.cpp b/modules/skparagraph/src/TextLine.cpp
177
+ --- a/modules/skparagraph/src/TextLine.cpp
178
+ +++ b/modules/skparagraph/src/TextLine.cpp
179
+ @@ -318,6 +318,10 @@
180
+ }
181
+ } else if (align == TextAlign::kRight) {
182
+ fShift = delta;
183
+ + } else if (align == TextAlign::kCenterAndroid) {
184
+ + const int extent = static_cast<int>(this->width()) & ~1;
185
+ + fShift = ((static_cast<int>(maxWidth - fIndent) - extent) >> 1) +
186
+ + (isRtl ? 0 : fIndent);
187
+ } else if (align == TextAlign::kCenter) {
188
+ fShift = delta / 2 + (isRtl ? 0 : fIndent);
189
+ } else if (align == TextAlign::kLeft) {
@@ -0,0 +1,10 @@
1
+ // consumes the fork built earlier in this invocation.
2
+ allprojects {
3
+ def forkRepository = repositories.maven {
4
+ name = "peachSkikoFork"
5
+ url = uri(System.getProperty("maven.repo.local"))
6
+ content { includeModule("org.jetbrains.skiko", "skiko-wasm-js") }
7
+ }
8
+ repositories.remove(forkRepository)
9
+ repositories.addFirst(forkRepository)
10
+ }
@@ -91,3 +91,20 @@ diff --git a/skiko/buildSrc/src/main/kotlin/tasks/configuration/WasmTasksConfigu
91
91
  addAll(buildType.clangFlags)
92
92
  add("-fno-rtti")
93
93
  add("-fno-exceptions")
94
+ diff --git a/skiko/src/commonMain/kotlin/org/jetbrains/skia/paragraph/Alignment.kt b/skiko/src/commonMain/kotlin/org/jetbrains/skia/paragraph/Alignment.kt
95
+ --- a/skiko/src/commonMain/kotlin/org/jetbrains/skia/paragraph/Alignment.kt
96
+ +++ b/skiko/src/commonMain/kotlin/org/jetbrains/skia/paragraph/Alignment.kt
97
+ @@ -1,3 +1,4 @@
98
+ +// modified for peach: carries Android center alignment to the native paragraph.
99
+ package org.jetbrains.skia.paragraph
100
+
101
+ enum class Alignment {
102
+ @@ -6,5 +7,6 @@
103
+ CENTER,
104
+ JUSTIFY,
105
+ START,
106
+ - END;
107
+ + END,
108
+ + CENTER_ANDROID;
109
+ }
110
+
@@ -33,9 +33,9 @@
33
33
  "sha256": "fc48b8999ea9812e86d9922d9b8ef1b022e9fb32ee07dcf929bcd80bac441d01"
34
34
  },
35
35
  {
36
- "name": "skiko-wasm-js-0.152.0-alpha02-peach-3834433e1790.klib",
37
- "url": "https://r2.rnxsim.com/peach-kotlin-fork/3834433e17902a3d86dc973abb245b7af5aae0012ca29e1ac9668d74335f3115/skiko-wasm-js-0.152.0-alpha02.klib",
38
- "sha256": "3834433e17902a3d86dc973abb245b7af5aae0012ca29e1ac9668d74335f3115"
36
+ "name": "skiko-wasm-js-0.152.0-alpha02-peach-6442c264f2e2.klib",
37
+ "url": "https://r2.rnxsim.com/peach-kotlin-fork/6442c264f2e2b839fa4b1a3ac35714c643b24d1b22d544ec434986031dffba1b/skiko-wasm-js-0.152.0-alpha02.klib",
38
+ "sha256": "6442c264f2e2b839fa4b1a3ac35714c643b24d1b22d544ec434986031dffba1b"
39
39
  },
40
40
  {
41
41
  "name": "annotation-wasm-js-1.9.1.klib",
@@ -68,9 +68,9 @@
68
68
  "sha256": "63f16892ab6435778c60e217a0959516f4fcd46cad8862d6831f055ac30f09d8"
69
69
  },
70
70
  {
71
- "name": "ui-skiko-wasm-js-1.13.0-alpha01-peach-1aa9f1b32539.klib",
72
- "url": "https://r2.rnxsim.com/peach-kotlin-fork/1aa9f1b3253964ff20f60d6b057aa0ddaed0343e93d7b4defc613b3ed5b65c3d/ui-skiko-wasm-js-1.13.0-alpha01.klib",
73
- "sha256": "1aa9f1b3253964ff20f60d6b057aa0ddaed0343e93d7b4defc613b3ed5b65c3d"
71
+ "name": "ui-skiko-wasm-js-1.13.0-alpha01-peach-c1c532120db6.klib",
72
+ "url": "https://r2.rnxsim.com/peach-kotlin-fork/c1c532120db605507e75a47ea72c66de314861322a0fd42fbee7fd4eb1d43294/ui-skiko-wasm-js-1.13.0-alpha01.klib",
73
+ "sha256": "c1c532120db605507e75a47ea72c66de314861322a0fd42fbee7fd4eb1d43294"
74
74
  },
75
75
  {
76
76
  "name": "ui-text-wasm-js-1.13.0-alpha01-peach-10c54ff0fa5f.klib",
@@ -325,9 +325,9 @@
325
325
  "skiko": "0.152.0-alpha02"
326
326
  },
327
327
  "composeRuntime": {
328
- "name": "skiko-wasm-0.152.0-alpha02-peach-bf4a72712ff0.jar",
329
- "url": "https://r2.rnxsim.com/peach-kotlin-fork/bf4a72712ff0ab67c5a5d28e058f6743497545392b593111a212e95b7435a339/skiko-wasm-0.152.0-alpha02.jar",
330
- "sha256": "bf4a72712ff0ab67c5a5d28e058f6743497545392b593111a212e95b7435a339"
328
+ "name": "skiko-wasm-0.152.0-alpha02-peach-3d68b5a68311.jar",
329
+ "url": "https://r2.rnxsim.com/peach-kotlin-fork/3d68b5a68311fedbd5f08ddd7bbd6fc8c8c19cb1c17c4394959b2507b125c323/skiko-wasm-0.152.0-alpha02.jar",
330
+ "sha256": "3d68b5a68311fedbd5f08ddd7bbd6fc8c8c19cb1c17c4394959b2507b125c323"
331
331
  },
332
332
  "composeJsLibraries": [
333
333
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "peachsim",
3
- "version": "0.1.899",
3
+ "version": "0.1.908",
4
4
  "description": "Vite and Metro plugins, testing drivers, and SDK exports for peach.",
5
5
  "author": "Tamagui LLC",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -174,6 +174,17 @@ peach debug state scroll-input
174
174
  peach debug state scroll-mirror
175
175
  ```
176
176
 
177
+ for responder negotiation, record the `touch` debug channel:
178
+
179
+ ```sh
180
+ peach debug enable touch
181
+ # reproduce the drag
182
+ peach debug recent touch 40
183
+ peach debug disable touch
184
+ ```
185
+
186
+ `gesture` is a timeline kind; responder debug events use `touch`.
187
+
177
188
  reading `describe` output:
178
189
 
179
190
  ```
@@ -269,105 +280,8 @@ watch out for:
269
280
 
270
281
  ## branch — accessibility
271
282
 
272
- ```sh
273
- peach get a11y # roles, labels, hints, and target geometry
274
- peach get layout --styling # boxes, styles, and native pixel contrast
275
- peach describe --a11y # inline a11y info on every node
276
- ```
277
-
278
- native styled layout reads rendered pixels rather than inferring contrast from
279
- tokens. it compares the normal frame with one CanvasKit capture where text is
280
- suppressed and adds each text score to the existing layout row and issue list.
281
-
282
- contrast summary format:
283
-
284
- ```
285
- #loginButton @(24,347) 120x24 font:16 color:#777 contrast:1.4/4.5 FAIL on:#888888 "Sign in"
286
- ```
287
-
288
- finding offenders fast:
289
-
290
- ```sh
291
- peach find --no-label # interactive nodes missing label
292
- peach find --no-role # interactive nodes missing role
293
- peach find --pressable --max-dim 44 # undersized tap targets
294
- ```
295
-
296
- what gets checked:
297
-
298
- - **missing labels** — interactive nodes without `accessibilityLabel`
299
- - **missing roles** — `Pressable`/`Touchable` without `accessibilityRole`
300
- - **missing hints** — complex interactions without `accessibilityHint`
301
- - **touch target size** — tappable nodes with hitSlop-expanded rect <44×44
302
- - **contrast** — run `peach get layout --styling`; it samples final text and
303
- text-suppressed background pixels, including gradients and composited opacity
304
- - **label / visible-text mismatch** — label that reads differently from
305
- the text VoiceOver would fall back to (often a translation bug)
306
- - **duplicate labels** — two reachable nodes with identical labels on
307
- one screen ("Open" + "Open" leaves no way to disambiguate)
308
-
309
- triage order:
310
-
311
- - **P0** — interactive node with no label or no role (blocks screen
312
- reader entirely)
313
- - **P1** — tap target <44×44, duplicate labels
314
- - **P2** — contrast, label/visible-text mismatch
315
-
316
- fix → verify loop (the same shape as debugging):
317
-
318
- ```sh
319
- peach get a11y > before-a11y.txt
320
- peach get layout --styling --json > before-layout.json
321
- # apply the fix
322
- peach get a11y > after-a11y.txt
323
- peach get layout --styling --json > after-layout.json
324
- diff before-a11y.txt after-a11y.txt
325
- diff before-layout.json after-layout.json
326
- ```
327
-
328
- fixing common cases:
329
-
330
- ```tsx
331
- // bad: icon button with no accessible name
332
- <Pressable onPress={onBack}>
333
- <ChevronLeftIcon />
334
- </Pressable>
335
-
336
- // good: labeled + roled + hinted + hitSlop'd
337
- <Pressable
338
- onPress={onBack}
339
- accessibilityRole="button"
340
- accessibilityLabel="Back"
341
- accessibilityHint="Returns to the previous screen"
342
- hitSlop={12} // bumps tap target to 44×44 minimum
343
- >
344
- <ChevronLeftIcon />
345
- </Pressable>
346
-
347
- // bad: text input with no programmatic label
348
- <TextInput placeholder="Email" onChangeText={setEmail} />
349
-
350
- // good: label survives even when placeholder is hidden
351
- <TextInput
352
- placeholder="Email"
353
- accessibilityLabel="Email address"
354
- onChangeText={setEmail}
355
- />
356
- ```
357
-
358
- watch out for:
359
-
360
- - **placeholders are not labels.** they disappear after the user types.
361
- - **hitSlop expands the a11y rect too.** rarely need to make the
362
- rendered button bigger — `hitSlop={12}` is usually enough.
363
- - **`accessibilityRole="image"` is not an `alt` tag.** pair it with
364
- `accessibilityLabel` or VoiceOver reads "image" and nothing else.
365
- - **`importantForAccessibility="no-hide-descendants"` hides an entire
366
- subtree.** useful for decorative chrome, dangerous if you forget it.
367
-
368
- run the screen reader on a real device at least once before shipping —
369
- labels that read fine on paper often sound robotic ("delete_message_btn_v2"
370
- type names sneak in).
283
+ Read [accessibility](references/accessibility.md) for contrast capture, labels,
284
+ roles, touch targets and the verification commands.
371
285
 
372
286
  ## recovery — common failure modes
373
287
 
@@ -388,57 +302,5 @@ type names sneak in).
388
302
 
389
303
  ## reference
390
304
 
391
- ### finding and interacting
392
-
393
- ```sh
394
- peach describe # all visible elements with styles
395
- peach describe button # filter by text/role/label/testID
396
- peach describe --testid foo # only the subtree under a testID
397
- peach describe --verbose # include every style property
398
-
399
- peach find "Sign in" # by text content
400
- peach find --testid loginButton
401
- peach find --role button
402
- peach find --pressable
403
- peach find --visible # only on-screen nodes
404
-
405
- peach do tap loginButton # tap by testID (preferred)
406
- peach do tap-text "Submit" # tap by text
407
- peach do tap 196 400 # tap at coordinates
408
- peach do type "hello world" # type into focused input
409
- peach do type-into emailInput "a@b" # focus then type
410
- peach do key return # special key (return, backspace, …)
411
- peach do scroll feed-list 0 500 # scroll a testID to absolute x/y offsets
412
- peach do swipe feed-list left # directional swipe
413
- peach do long-press messageRow 800 # ms hold
414
- ```
415
-
416
- ### errors and requests
417
-
418
- ```sh
419
- peach get errors 5 # last 5 console errors
420
- peach get warnings 5
421
- peach get requests 5 # recent failed requests
422
- peach logs --since 30s # recent console output
423
- peach network --since 30s # recent network traffic
424
- ```
425
-
426
- ### snapshots and diff
427
-
428
- ```sh
429
- peach debug snapshot before
430
- # interact
431
- peach debug snapshot after
432
- peach debug diff before after # added / removed / changed nodes
433
- ```
434
-
435
- ### debug channels
436
-
437
- useful: `animated`, `layout`, `onlayout`, `render`, `sheets`, `portals`,
438
- `gesture`, `scroll`, `text`, `image`.
439
-
440
- ```sh
441
- peach debug enable animated,layout,sheets,portals
442
- peach debug recent layout 20 # last 20 events on the channel
443
- peach debug disable animated,layout,sheets,portals
444
- ```
305
+ Read [commands](references/commands.md) for finding, interaction, error,
306
+ snapshot and debug-channel commands.
@@ -0,0 +1,102 @@
1
+ # accessibility
2
+
3
+ ```sh
4
+ peach get a11y # roles, labels, hints, and target geometry
5
+ peach get layout --styling # boxes, styles, and native pixel contrast
6
+ peach describe --a11y # inline a11y info on every node
7
+ ```
8
+
9
+ native styled layout reads rendered pixels rather than inferring contrast from
10
+ tokens. it compares the normal frame with one CanvasKit capture where text is
11
+ suppressed and adds each text score to the existing layout row and issue list.
12
+
13
+ contrast summary format:
14
+
15
+ ```
16
+ #loginButton @(24,347) 120x24 font:16 color:#777 contrast:1.4/4.5 FAIL on:#888888 "Sign in"
17
+ ```
18
+
19
+ finding offenders fast:
20
+
21
+ ```sh
22
+ peach find --no-label # interactive nodes missing label
23
+ peach find --no-role # interactive nodes missing role
24
+ peach find --pressable --max-dim 44 # undersized tap targets
25
+ ```
26
+
27
+ what gets checked:
28
+
29
+ - **missing labels** — interactive nodes without `accessibilityLabel`
30
+ - **missing roles** — `Pressable`/`Touchable` without `accessibilityRole`
31
+ - **missing hints** — complex interactions without `accessibilityHint`
32
+ - **touch target size** — tappable nodes with hitSlop-expanded rect <44×44
33
+ - **contrast** — run `peach get layout --styling`; it samples final text and
34
+ text-suppressed background pixels, including gradients and composited opacity
35
+ - **label / visible-text mismatch** — label that reads differently from
36
+ the text VoiceOver would fall back to (often a translation bug)
37
+ - **duplicate labels** — two reachable nodes with identical labels on
38
+ one screen ("Open" + "Open" leaves no way to disambiguate)
39
+
40
+ triage order:
41
+
42
+ - **P0** — interactive node with no label or no role (blocks screen
43
+ reader entirely)
44
+ - **P1** — tap target <44×44, duplicate labels
45
+ - **P2** — contrast, label/visible-text mismatch
46
+
47
+ fix → verify loop (the same shape as debugging):
48
+
49
+ ```sh
50
+ peach get a11y > before-a11y.txt
51
+ peach get layout --styling --json > before-layout.json
52
+ # apply the fix
53
+ peach get a11y > after-a11y.txt
54
+ peach get layout --styling --json > after-layout.json
55
+ diff before-a11y.txt after-a11y.txt
56
+ diff before-layout.json after-layout.json
57
+ ```
58
+
59
+ fixing common cases:
60
+
61
+ ```tsx
62
+ // bad: icon button with no accessible name
63
+ <Pressable onPress={onBack}>
64
+ <ChevronLeftIcon />
65
+ </Pressable>
66
+
67
+ // good: labeled + roled + hinted + hitSlop'd
68
+ <Pressable
69
+ onPress={onBack}
70
+ accessibilityRole="button"
71
+ accessibilityLabel="Back"
72
+ accessibilityHint="Returns to the previous screen"
73
+ hitSlop={12} // bumps tap target to 44×44 minimum
74
+ >
75
+ <ChevronLeftIcon />
76
+ </Pressable>
77
+
78
+ // bad: text input with no programmatic label
79
+ <TextInput placeholder="Email" onChangeText={setEmail} />
80
+
81
+ // good: label survives even when placeholder is hidden
82
+ <TextInput
83
+ placeholder="Email"
84
+ accessibilityLabel="Email address"
85
+ onChangeText={setEmail}
86
+ />
87
+ ```
88
+
89
+ watch out for:
90
+
91
+ - **placeholders are not labels.** they disappear after the user types.
92
+ - **hitSlop expands the a11y rect too.** rarely need to make the
93
+ rendered button bigger — `hitSlop={12}` is usually enough.
94
+ - **`accessibilityRole="image"` is not an `alt` tag.** pair it with
95
+ `accessibilityLabel` or VoiceOver reads "image" and nothing else.
96
+ - **`importantForAccessibility="no-hide-descendants"` hides an entire
97
+ subtree.** useful for decorative chrome, dangerous if you forget it.
98
+
99
+ run the screen reader on a real device at least once before shipping —
100
+ labels that read fine on paper often sound robotic ("delete_message_btn_v2"
101
+ type names sneak in).
102
+
@@ -0,0 +1,56 @@
1
+ # commands
2
+
3
+ ### finding and interacting
4
+
5
+ ```sh
6
+ peach describe # all visible elements with styles
7
+ peach describe button # filter by text/role/label/testID
8
+ peach describe --testid foo # only the subtree under a testID
9
+ peach describe --verbose # include every style property
10
+
11
+ peach find "Sign in" # by text content
12
+ peach find --testid loginButton
13
+ peach find --role button
14
+ peach find --pressable
15
+ peach find --visible # only on-screen nodes
16
+
17
+ peach do tap loginButton # tap by testID (preferred)
18
+ peach do tap-text "Submit" # tap by text
19
+ peach do tap 196 400 # tap at coordinates
20
+ peach do type "hello world" # type into focused input
21
+ peach do type-into emailInput "a@b" # focus then type
22
+ peach do key return # special key (return, backspace, …)
23
+ peach do scroll feed-list 0 500 # scroll a testID to absolute x/y offsets
24
+ peach do swipe feed-list left # directional swipe
25
+ peach do long-press messageRow 800 # ms hold
26
+ ```
27
+
28
+ ### errors and requests
29
+
30
+ ```sh
31
+ peach get errors 5 # last 5 console errors
32
+ peach get warnings 5
33
+ peach get requests 5 # recent failed requests
34
+ peach logs --since 30s # recent console output
35
+ peach network --since 30s # recent network traffic
36
+ ```
37
+
38
+ ### snapshots and diff
39
+
40
+ ```sh
41
+ peach debug snapshot before
42
+ # interact
43
+ peach debug snapshot after
44
+ peach debug diff before after # added / removed / changed nodes
45
+ ```
46
+
47
+ ### debug channels
48
+
49
+ useful: `animated`, `layout`, `onlayout`, `render`, `sheets`, `portals`,
50
+ `touch`, `scroll`, `text`, `image`.
51
+
52
+ ```sh
53
+ peach debug enable animated,layout,sheets,portals
54
+ peach debug recent layout 20 # last 20 events on the channel
55
+ peach debug disable animated,layout,sheets,portals
56
+ ```
@@ -84,7 +84,8 @@ export async function collectExpoSwiftSources(
84
84
  const namespacesSeen = new Set<string>()
85
85
  let bytes = 0
86
86
  let fileCount = 0
87
- for (const entry of Object.values(search)) {
87
+ // native Expo resolves packages by name before generating its Apple provider.
88
+ for (const [, entry] of Object.entries(search).sort(([a], [b]) => a.localeCompare(b))) {
88
89
  if (!entry || typeof entry !== 'object')
89
90
  throw new Error('invalid Expo autolinking entry')
90
91
  const sourceRoot = Reflect.get(entry, 'path')
@@ -125,9 +126,17 @@ export async function collectExpoSwiftSources(
125
126
  const classes: { name: string; registration: string }[] = []
126
127
  for (const module of modules) {
127
128
  const className =
128
- module && typeof module === 'object' ? Reflect.get(module, 'class') : null
129
+ typeof module === 'string'
130
+ ? module
131
+ : module && typeof module === 'object'
132
+ ? Reflect.get(module, 'class')
133
+ : null
129
134
  const name =
130
- module && typeof module === 'object' ? Reflect.get(module, 'name') : null
135
+ typeof module === 'string'
136
+ ? null
137
+ : module && typeof module === 'object'
138
+ ? Reflect.get(module, 'name')
139
+ : null
131
140
  if (
132
141
  typeof className !== 'string' ||
133
142
  !/^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(className) ||
@@ -165,7 +174,9 @@ export async function collectExpoSwiftSources(
165
174
  files[
166
175
  `Sources/${namespace}/${path.relative(ios, file).split(path.sep).join('/')}`
167
176
  ] =
168
- `#sourceLocation(file: ${JSON.stringify(file)}, line: 1)\n${fs.readFileSync(file, 'utf8')}`
177
+ // CocoaPods' iOS umbrella header imports UIKit into every Swift file
178
+ // through the underlying module; these source units have no clang pod.
179
+ `import UIKit\n#sourceLocation(file: ${JSON.stringify(file)}, line: 1)\n${fs.readFileSync(file, 'utf8')}`
169
180
  }
170
181
  }
171
182
  walk(ios)
@@ -636,10 +636,10 @@ const ENGINE_WORKER_BOOT_CLOSURES: WorkerBootClosure[] = [
636
636
  // although the shell renders only the plain sheet: 67kB of the boot file.
637
637
  const WORKER_SIDE_EFFECT_FREE_PACKAGES = [
638
638
  { test: /\/node_modules\/@gorhom\/bottom-sheet\//, sideEffects: false },
639
- // legacy wrapper construction only creates exported components. keep rngh's
640
- // index initialization and createHandler's event registrations side-effectful.
639
+ // button, touchable, and legacy/v3 wrapper modules only construct components.
640
+ // keep rngh's index initialization and createHandler's registrations side-effectful.
641
641
  {
642
- test: /\/node_modules\/react-native-gesture-handler\/(?:src|lib\/module)\/components\/GestureComponents\.[jt]sx?$/,
642
+ test: /\/node_modules\/react-native-gesture-handler\/(?:src|lib\/module)\/(?:components\/(?:GestureComponents|GestureButtons)\.[jt]sx?|components\/touchables\/[^/]+\.[jt]sx?|v3\/components\/(?:GestureComponents|GestureButtons)\.[jt]sx?)$/,
643
643
  sideEffects: false,
644
644
  },
645
645
  ]