peachsim 0.1.668 → 0.1.670
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/cli/commands/control.ts +8 -5
- package/cli/commands/inspect/core.ts +7 -5
- package/cli/commands/inspect/wait-selector.ts +2 -11
- package/cli/commands/inspect.ts +6 -59
- package/cli/commands/kotlin.ts +6 -4
- package/cli/commands/skills.ts +28 -7
- package/cli/commands/upgrade.ts +18 -3
- package/cli/main.ts +21 -0
- package/cli/output-redaction.ts +182 -0
- package/cli/parse-args.ts +1 -0
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +1 -1
- package/dist-lib/agent-sessions.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/bridge-contract-input.cjs +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +1 -1
- package/dist-lib/cloud.mjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +1 -1
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +29 -19
- package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/replacement-module-handler.cjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +260 -17
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/kotlin-host.cjs +2 -1
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +259 -16
- package/dist-lib/native-app-identity.cjs +1 -1
- package/dist-lib/native-app-identity.mjs +1 -1
- package/dist-lib/native-firebase-metadata.cjs +115 -0
- package/dist-lib/native-firebase-metadata.mjs +86 -0
- package/dist-lib/plist.cjs +1 -1
- package/dist-lib/plist.mjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- package/dist-lib/react-native-host-modules.cjs +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
- package/dist-lib/sdk.cjs +2 -2
- package/dist-lib/sdk.mjs +2 -2
- package/dist-lib/skills.cjs +479 -204
- package/dist-lib/swift-compile.cjs +1 -1
- package/dist-lib/swift-compile.mjs +1 -1
- package/dist-lib/swift-package.cjs +1 -1
- package/dist-lib/swift-package.mjs +1 -1
- package/dist-lib/swift-submit.cjs +1 -1
- package/dist-lib/swift-submit.mjs +1 -1
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +21 -13
- package/dist-lib/vite.cjs +1 -1
- package/kotlin/AppSources/androidx/activity/ActivityFlags.kt +9 -0
- package/kotlin/AppSources/androidx/activity/BackEventCompat.kt +25 -4
- package/kotlin/AppSources/androidx/activity/ComponentActivity.kt +15 -4
- package/kotlin/AppSources/androidx/activity/EdgeToEdge.kt +12 -8
- package/kotlin/AppSources/androidx/activity/ExperimentalActivityApi.kt +6 -0
- package/kotlin/AppSources/androidx/activity/OnBackPressedCallback.kt +50 -9
- package/kotlin/AppSources/androidx/activity/OnBackPressedDispatcher.kt +67 -89
- package/kotlin/AppSources/androidx/activity/compose/BackHandler.kt +55 -24
- package/kotlin/AppSources/androidx/activity/compose/internal/BackHandlerCompat.kt +38 -0
- package/kotlin/AppSources/androidx/activity/compose/internal/BackHandlerDispatcherCompat.kt +27 -0
- package/kotlin/AppSources/dev/peach/app/PeachApp.kt +5 -5
- package/kotlin/ComposeSources/PeachComposeHost.kt +31 -7
- package/kotlin/fixtures/kotlin-app/app/build.gradle.kts +1 -1
- package/kotlin/fixtures/kotlin-app-variants/Browse.kt +73 -0
- package/kotlin/fixtures/kotlin-app-variants/Focus.kt +62 -0
- package/kotlin/fixtures/kotlin-app-variants/Lists.kt +57 -0
- package/kotlin/fixtures/kotlin-app-variants/Nav.kt +47 -0
- package/kotlin/fixtures/kotlin-app-variants/Nested.kt +64 -0
- package/kotlin/fixtures/kotlin-app-variants/Pan.kt +38 -0
- package/kotlin/fixtures/kotlin-app-variants/Pickers.kt +44 -0
- package/kotlin/fixtures/kotlin-app-variants/Press.kt +43 -0
- package/kotlin/fixtures/kotlin-app-variants/TextWidth.kt +45 -0
- package/kotlin/fixtures/kotlin-app-variants/Transition.kt +69 -0
- package/kotlin/fixtures/kotlin-app-variants/ViewConfig.kt +22 -0
- package/kotlin/toolchain.json +30 -2
- package/package.json +8 -1
- package/skills/contrast/SKILL.md +2 -1
- package/skills/peach-debug/SKILL.md +2 -2
- package/skills/peach-test/SKILL.md +3 -3
- package/src/expo-swift-host.ts +13 -4
- package/src/kotlin-app.ts +35 -16
- package/src/kotlin-host.ts +1 -0
- package/src/metro-plugin.ts +24 -0
- package/src/native-firebase-config.ts +202 -21
- package/src/native-firebase-metadata.ts +114 -0
- package/src/swift-host.ts +30 -15
- package/src/vite-plugin.ts +46 -2
- package/kotlin/AppSources/androidx/activity/Cancellable.kt +0 -5
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
package dev.contrast.kotlinapp
|
|
2
|
+
|
|
3
|
+
import androidx.compose.foundation.background
|
|
4
|
+
import androidx.compose.foundation.layout.Arrangement
|
|
5
|
+
import androidx.compose.foundation.layout.Column
|
|
6
|
+
import androidx.compose.foundation.layout.fillMaxSize
|
|
7
|
+
import androidx.compose.foundation.layout.padding
|
|
8
|
+
import androidx.compose.foundation.layout.safeDrawingPadding
|
|
9
|
+
import androidx.compose.material3.MaterialTheme
|
|
10
|
+
import androidx.compose.material3.Text
|
|
11
|
+
import androidx.compose.runtime.Composable
|
|
12
|
+
import androidx.compose.runtime.mutableStateMapOf
|
|
13
|
+
import androidx.compose.runtime.remember
|
|
14
|
+
import androidx.compose.ui.Modifier
|
|
15
|
+
import androidx.compose.ui.graphics.Color
|
|
16
|
+
import androidx.compose.ui.text.TextStyle
|
|
17
|
+
import androidx.compose.ui.text.font.FontWeight
|
|
18
|
+
import androidx.compose.ui.unit.dp
|
|
19
|
+
import androidx.compose.ui.unit.sp
|
|
20
|
+
import kotlin.math.roundToInt
|
|
21
|
+
|
|
22
|
+
// laid out widths in px of the same words at the label and body styles
|
|
23
|
+
@Composable
|
|
24
|
+
fun TextWidth() {
|
|
25
|
+
val widths = remember { mutableStateMapOf<String, Float>() }
|
|
26
|
+
val samples = listOf(
|
|
27
|
+
"label Month" to MaterialTheme.typography.labelLarge,
|
|
28
|
+
"label Compose" to MaterialTheme.typography.labelLarge,
|
|
29
|
+
"body Range: Day" to MaterialTheme.typography.bodyLarge,
|
|
30
|
+
"body Suggest" to MaterialTheme.typography.bodyLarge,
|
|
31
|
+
"plain500 Month" to TextStyle(fontSize = 14.sp, fontWeight = FontWeight.Medium),
|
|
32
|
+
"plain400 Month" to TextStyle(fontSize = 14.sp),
|
|
33
|
+
)
|
|
34
|
+
val label = MaterialTheme.typography.labelLarge
|
|
35
|
+
Column(
|
|
36
|
+
Modifier.fillMaxSize().background(Color(0xFFE8EEF8)).safeDrawingPadding().padding(16.dp),
|
|
37
|
+
verticalArrangement = Arrangement.spacedBy(12.dp),
|
|
38
|
+
) {
|
|
39
|
+
Text("Label: ${label.fontSize} ${label.letterSpacing} ${label.fontWeight?.weight} ${label.fontFamily}")
|
|
40
|
+
Text("Widths: " + samples.joinToString(" ") { (key) -> "${((widths[key] ?: 0f) * 100).roundToInt() / 100.0}" })
|
|
41
|
+
samples.forEach { (key, style: TextStyle) ->
|
|
42
|
+
Text(key.substringAfter(' '), style = style, onTextLayout = { widths[key] = it.getLineRight(0) })
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
package dev.contrast.kotlinapp
|
|
2
|
+
|
|
3
|
+
import androidx.compose.animation.AnimatedContent
|
|
4
|
+
import androidx.compose.animation.Crossfade
|
|
5
|
+
import androidx.compose.animation.animateColor
|
|
6
|
+
import androidx.compose.animation.core.RepeatMode
|
|
7
|
+
import androidx.compose.animation.core.animateDp
|
|
8
|
+
import androidx.compose.animation.core.animateFloat
|
|
9
|
+
import androidx.compose.animation.core.infiniteRepeatable
|
|
10
|
+
import androidx.compose.animation.core.rememberInfiniteTransition
|
|
11
|
+
import androidx.compose.animation.core.tween
|
|
12
|
+
import androidx.compose.animation.core.updateTransition
|
|
13
|
+
import androidx.compose.foundation.background
|
|
14
|
+
import androidx.compose.foundation.layout.Arrangement
|
|
15
|
+
import androidx.compose.foundation.layout.Box
|
|
16
|
+
import androidx.compose.foundation.layout.Column
|
|
17
|
+
import androidx.compose.foundation.layout.fillMaxSize
|
|
18
|
+
import androidx.compose.foundation.layout.height
|
|
19
|
+
import androidx.compose.foundation.layout.padding
|
|
20
|
+
import androidx.compose.foundation.layout.safeDrawingPadding
|
|
21
|
+
import androidx.compose.foundation.layout.width
|
|
22
|
+
import androidx.compose.material3.Button
|
|
23
|
+
import androidx.compose.material3.Text
|
|
24
|
+
import androidx.compose.runtime.Composable
|
|
25
|
+
import androidx.compose.runtime.LaunchedEffect
|
|
26
|
+
import androidx.compose.runtime.getValue
|
|
27
|
+
import androidx.compose.runtime.mutableIntStateOf
|
|
28
|
+
import androidx.compose.runtime.mutableStateOf
|
|
29
|
+
import androidx.compose.runtime.remember
|
|
30
|
+
import androidx.compose.runtime.setValue
|
|
31
|
+
import androidx.compose.runtime.snapshotFlow
|
|
32
|
+
import androidx.compose.ui.Modifier
|
|
33
|
+
import androidx.compose.ui.graphics.Color
|
|
34
|
+
import androidx.compose.ui.unit.dp
|
|
35
|
+
|
|
36
|
+
// one state drives an updateTransition's size and color, a Crossfade and,
|
|
37
|
+
// with a counter, an AnimatedContent. an infinite transition runs beside
|
|
38
|
+
// them; the status reports once its value has reached both ends, and never
|
|
39
|
+
// draws it, so a capture is the same at any moment
|
|
40
|
+
@Composable
|
|
41
|
+
fun Transition() {
|
|
42
|
+
var large by remember { mutableStateOf(false) }
|
|
43
|
+
var count by remember { mutableIntStateOf(0) }
|
|
44
|
+
val transition = updateTransition(large, label = "box")
|
|
45
|
+
val size by transition.animateDp(label = "size") { if (it) 160.dp else 80.dp }
|
|
46
|
+
val color by transition.animateColor(label = "color") { if (it) Color(0xFF6750A4) else Color(0xFFE91E63) }
|
|
47
|
+
val pulse = rememberInfiniteTransition(label = "pulse")
|
|
48
|
+
val level = pulse.animateFloat(0f, 1f, infiniteRepeatable(tween(300), RepeatMode.Reverse), label = "level")
|
|
49
|
+
var low by remember { mutableStateOf(false) }
|
|
50
|
+
var high by remember { mutableStateOf(false) }
|
|
51
|
+
LaunchedEffect(Unit) {
|
|
52
|
+
snapshotFlow { level.value }.collect {
|
|
53
|
+
if (it <= 0.01f) low = true
|
|
54
|
+
if (it >= 0.99f) high = true
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
val settled = !transition.isRunning && transition.currentState == transition.targetState
|
|
58
|
+
Column(
|
|
59
|
+
Modifier.fillMaxSize().background(Color(0xFFE8EEF8)).safeDrawingPadding().padding(16.dp),
|
|
60
|
+
verticalArrangement = Arrangement.spacedBy(12.dp),
|
|
61
|
+
) {
|
|
62
|
+
Text("Transition: $large settled $settled cycled ${low && high}")
|
|
63
|
+
Button(onClick = { large = !large }) { Text("Switch") }
|
|
64
|
+
Button(onClick = { count += 1 }) { Text("Add") }
|
|
65
|
+
AnimatedContent(count, label = "count") { Text("Count $it") }
|
|
66
|
+
Crossfade(large, label = "fade") { Text(if (it) "Large" else "Small") }
|
|
67
|
+
Box(Modifier.width(size).height(size).background(color))
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
package dev.contrast.kotlinapp
|
|
2
|
+
|
|
3
|
+
import androidx.compose.foundation.background
|
|
4
|
+
import androidx.compose.foundation.layout.fillMaxSize
|
|
5
|
+
import androidx.compose.foundation.layout.padding
|
|
6
|
+
import androidx.compose.foundation.layout.safeDrawingPadding
|
|
7
|
+
import androidx.compose.material3.Text
|
|
8
|
+
import androidx.compose.runtime.Composable
|
|
9
|
+
import androidx.compose.ui.Modifier
|
|
10
|
+
import androidx.compose.ui.graphics.Color
|
|
11
|
+
import androidx.compose.ui.platform.LocalViewConfiguration
|
|
12
|
+
import androidx.compose.ui.unit.dp
|
|
13
|
+
|
|
14
|
+
// every value of the window's view configuration, as gestures read them
|
|
15
|
+
@Composable
|
|
16
|
+
fun ViewConfig() {
|
|
17
|
+
val c = LocalViewConfiguration.current
|
|
18
|
+
Text(
|
|
19
|
+
"Config: ${c.longPressTimeoutMillis} ${c.doubleTapTimeoutMillis} ${c.doubleTapMinTimeMillis} ${c.touchSlop} ${c.handwritingSlop} ${c.minimumTouchTargetSize} ${c.maximumFlingVelocity} ${c.minimumFlingVelocity} ${c.handwritingGestureLineMargin}",
|
|
20
|
+
Modifier.fillMaxSize().background(Color(0xFFE8EEF8)).safeDrawingPadding().padding(16.dp),
|
|
21
|
+
)
|
|
22
|
+
}
|
package/kotlin/toolchain.json
CHANGED
|
@@ -256,6 +256,31 @@
|
|
|
256
256
|
"name": "savedstate-wasm-js-1.5.0.klib",
|
|
257
257
|
"url": "https://dl.google.com/dl/android/maven2/androidx/savedstate/savedstate-wasm-js/1.5.0/savedstate-wasm-js-1.5.0.klib",
|
|
258
258
|
"sha256": "02bb90d1173b771a9eb4ff9d2440bbe50202f0d9b0578abb2f5b47b1c27285ef"
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
"name": "navigation-common-wasm-js-2.10.0.klib",
|
|
262
|
+
"url": "https://dl.google.com/dl/android/maven2/androidx/navigation/navigation-common-wasm-js/2.10.0/navigation-common-wasm-js-2.10.0.klib",
|
|
263
|
+
"sha256": "ecd83faf3e808a1b90aae9b73b2c23c793b0cb8a42116cd4c0c206c4e3f3768c"
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
"name": "navigation-runtime-wasm-js-2.10.0.klib",
|
|
267
|
+
"url": "https://dl.google.com/dl/android/maven2/androidx/navigation/navigation-runtime-wasm-js/2.10.0/navigation-runtime-wasm-js-2.10.0.klib",
|
|
268
|
+
"sha256": "a378f0c8ea8997cdac63d4845f692abb18c8d4c3aed7acbfae32c1a10275374a"
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
"name": "navigation-common-wasm-js-2.10.0-beta01.klib",
|
|
272
|
+
"url": "https://repo1.maven.org/maven2/org/jetbrains/androidx/navigation/navigation-common-wasm-js/2.10.0-beta01/navigation-common-wasm-js-2.10.0-beta01.klib",
|
|
273
|
+
"sha256": "1409cfbaa2a1985886a524ff36cc73ee3ebd37ed08371194bb2fa73019d447fa"
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
"name": "navigation-runtime-wasm-js-2.10.0-beta01.klib",
|
|
277
|
+
"url": "https://repo1.maven.org/maven2/org/jetbrains/androidx/navigation/navigation-runtime-wasm-js/2.10.0-beta01/navigation-runtime-wasm-js-2.10.0-beta01.klib",
|
|
278
|
+
"sha256": "3682e0f481303d71f71e9713b1b1c160839567b4bea1644cc73635c141206be2"
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
"name": "navigation-compose-wasm-js-2.10.0-beta01.klib",
|
|
282
|
+
"url": "https://repo1.maven.org/maven2/org/jetbrains/androidx/navigation/navigation-compose-wasm-js/2.10.0-beta01/navigation-compose-wasm-js-2.10.0-beta01.klib",
|
|
283
|
+
"sha256": "35e147105bfc48ef083c56b5108436234f8d8ab4747bce855e48a30243b15cd6"
|
|
259
284
|
}
|
|
260
285
|
],
|
|
261
286
|
"compose": {
|
|
@@ -266,7 +291,9 @@
|
|
|
266
291
|
"runtime": "1.13.0-alpha02",
|
|
267
292
|
"material3": "1.5.0-alpha27",
|
|
268
293
|
"materialRipple": "1.13.0-alpha02",
|
|
269
|
-
"material3Ripple": "1.5.0-alpha27"
|
|
294
|
+
"material3Ripple": "1.5.0-alpha27",
|
|
295
|
+
"navigation": "2.10.0",
|
|
296
|
+
"activity": "1.13.0"
|
|
270
297
|
},
|
|
271
298
|
"mappingSources": [
|
|
272
299
|
"https://repo1.maven.org/maven2/org/jetbrains/compose/ui/ui-android/1.13.0-alpha01/ui-android-1.13.0-alpha01.module",
|
|
@@ -285,7 +312,8 @@
|
|
|
285
312
|
"https://repo1.maven.org/maven2/org/jetbrains/compose/foundation/foundation-layout-android/1.13.0-alpha01/foundation-layout-android-1.13.0-alpha01.module",
|
|
286
313
|
"https://repo1.maven.org/maven2/org/jetbrains/compose/material/material-ripple-android/1.13.0-alpha01/material-ripple-android-1.13.0-alpha01.module",
|
|
287
314
|
"https://repo1.maven.org/maven2/org/jetbrains/compose/animation/animation-core-android/1.13.0-alpha01/animation-core-android-1.13.0-alpha01.module",
|
|
288
|
-
"https://repo1.maven.org/maven2/org/jetbrains/compose/material3/material3-ripple-android/1.13.0-alpha01/material3-ripple-android-1.13.0-alpha01.module"
|
|
315
|
+
"https://repo1.maven.org/maven2/org/jetbrains/compose/material3/material3-ripple-android/1.13.0-alpha01/material3-ripple-android-1.13.0-alpha01.module",
|
|
316
|
+
"https://repo1.maven.org/maven2/org/jetbrains/androidx/navigation/navigation-compose-android/2.10.0-beta01/navigation-compose-android-2.10.0-beta01.module"
|
|
289
317
|
],
|
|
290
318
|
"skiko": "0.152.0-alpha02"
|
|
291
319
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "peachsim",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.670",
|
|
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",
|
|
@@ -91,6 +91,13 @@
|
|
|
91
91
|
"import": "./dist-lib/plist.mjs",
|
|
92
92
|
"default": "./dist-lib/plist.cjs"
|
|
93
93
|
},
|
|
94
|
+
"./native-firebase-metadata": {
|
|
95
|
+
"types": "./src/native-firebase-metadata.ts",
|
|
96
|
+
"source": "./src/native-firebase-metadata.ts",
|
|
97
|
+
"bun": "./src/native-firebase-metadata.ts",
|
|
98
|
+
"import": "./dist-lib/native-firebase-metadata.mjs",
|
|
99
|
+
"default": "./dist-lib/native-firebase-metadata.cjs"
|
|
100
|
+
},
|
|
94
101
|
"./native-app-identity": {
|
|
95
102
|
"types": "./src/native-app-identity.ts",
|
|
96
103
|
"source": "./src/native-app-identity.ts",
|
package/skills/contrast/SKILL.md
CHANGED
|
@@ -89,6 +89,7 @@ run `peach --help` to see the full list under each group.
|
|
|
89
89
|
- `-d, --device <model>` — override the simulated device model
|
|
90
90
|
- `-t, --theme <scheme>` — override light, dark, or auto color scheme
|
|
91
91
|
- `--headless` — run browser automation without a visible window where supported
|
|
92
|
+
- `--redact` — mask keys, tokens, account ids and emails in the output of logs, network, what-happened and get, so it can be shared
|
|
92
93
|
|
|
93
94
|
## bundled Peach agent skills
|
|
94
95
|
|
|
@@ -123,7 +124,7 @@ commands in your own `sleep` or chain `do settle` "just to be safe"**.
|
|
|
123
124
|
|
|
124
125
|
what this means:
|
|
125
126
|
|
|
126
|
-
- `peach do tap
|
|
127
|
+
- `peach do tap login` then `peach find "Welcome"` Just Works™.
|
|
127
128
|
- you only need `peach do settle <seconds>` or `peach wait idle
|
|
128
129
|
--max-ms <N>` when the CLI explicitly prints
|
|
129
130
|
`⚠ auto-wait timed out after Nms — next command may see mid-animation
|
|
@@ -138,7 +138,7 @@ need a manual wait when:
|
|
|
138
138
|
peach do settle 5 # wait up to 5s for layout stability
|
|
139
139
|
peach wait idle --max-ms 5000 # same thing under the `wait` verb
|
|
140
140
|
peach wait idle --strict # also require animation flags clear
|
|
141
|
-
peach do tap
|
|
141
|
+
peach do tap play --no-wait # skip auto-settle on one command
|
|
142
142
|
PEACH_NO_AUTO_WAIT=1 peach ... # disable auto-settle for a lane
|
|
143
143
|
```
|
|
144
144
|
|
|
@@ -401,7 +401,7 @@ peach find --role button
|
|
|
401
401
|
peach find --pressable
|
|
402
402
|
peach find --visible # only on-screen nodes
|
|
403
403
|
|
|
404
|
-
peach do tap
|
|
404
|
+
peach do tap loginButton # tap by testID (preferred)
|
|
405
405
|
peach do tap-text "Submit" # tap by text
|
|
406
406
|
peach do tap 196 400 # tap at coordinates
|
|
407
407
|
peach do type "hello world" # type into focused input
|
|
@@ -67,7 +67,7 @@ peach wait selector dashboard
|
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
the batch stops on the first failure. add `--json` for per-action results.
|
|
70
|
-
`tap <id>` and `tap-id <id>` are
|
|
70
|
+
`tap <id>` is the default way to tap by testID (`--testid <id>` and `tap-id <id>` are aliases); use `tap-text <text>` for visible text.
|
|
71
71
|
punctuation in action data remains data. the exact `--then` token is reserved
|
|
72
72
|
as the action boundary. use Maestro when the sequence should be reviewed,
|
|
73
73
|
reused, or run in CI; an inline chain is not a third test runner and does not
|
|
@@ -89,11 +89,11 @@ Author from a live interaction:
|
|
|
89
89
|
|
|
90
90
|
```sh
|
|
91
91
|
peach maestro start # begin a draft
|
|
92
|
-
peach do tap
|
|
92
|
+
peach do tap loginButton # drive the app via the CLI
|
|
93
93
|
peach maestro keep # save the step
|
|
94
94
|
peach do type "user@example.com"
|
|
95
95
|
peach maestro keep
|
|
96
|
-
peach do tap
|
|
96
|
+
peach do tap submit
|
|
97
97
|
peach maestro keep
|
|
98
98
|
peach maestro end --output .maestro/login.yaml --validate
|
|
99
99
|
```
|
package/src/expo-swift-host.ts
CHANGED
|
@@ -9,7 +9,13 @@ import path from 'node:path'
|
|
|
9
9
|
import { promisify } from 'node:util'
|
|
10
10
|
import { RNX_FORKED_SCRIPT_ENV } from '../cli/internal-child'
|
|
11
11
|
import { isLoopbackHost } from './backend-origin'
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
artifactFor,
|
|
14
|
+
icuUrl,
|
|
15
|
+
localSwiftToolchain,
|
|
16
|
+
projectFor,
|
|
17
|
+
serveSwiftPackageAsset,
|
|
18
|
+
} from './swift-host'
|
|
13
19
|
import { MAX_SWIFT_COMPILE_FILES, MAX_SWIFT_COMPILE_SOURCE_BYTES } from './swift-submit'
|
|
14
20
|
import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
15
21
|
|
|
@@ -206,6 +212,11 @@ export async function handleExpoSwiftRequest(
|
|
|
206
212
|
pending = (async () => {
|
|
207
213
|
const files = await collectExpoSwiftSources(root)
|
|
208
214
|
if (!files) return null
|
|
215
|
+
// app Swift compiles only on this machine, never on a remote service;
|
|
216
|
+
// an install without the SwiftUI module and toolchain leaves the
|
|
217
|
+
// app's own native modules unregistered, as an unlinked module is.
|
|
218
|
+
const moduleRoot = path.resolve(import.meta.dirname, '../../sootsim-swift')
|
|
219
|
+
if (!localSwiftToolchain(moduleRoot)) return null
|
|
209
220
|
const key = createHash('sha256').update(fs.realpathSync(root)).digest('hex')
|
|
210
221
|
const packagePath = path.join(os.tmpdir(), 'peach-expo-swift', key)
|
|
211
222
|
const sources = path.join(packagePath, 'Sources')
|
|
@@ -217,12 +228,10 @@ export async function handleExpoSwiftRequest(
|
|
|
217
228
|
fs.mkdirSync(path.dirname(target), { recursive: true })
|
|
218
229
|
fs.writeFileSync(target, content)
|
|
219
230
|
}
|
|
220
|
-
const moduleRoot = path.resolve(import.meta.dirname, '../../sootsim-swift')
|
|
221
|
-
const local = fs.existsSync(path.join(moduleRoot, 'toolchain.json'))
|
|
222
231
|
const artifact = await artifactFor(
|
|
223
232
|
projectFor(packagePath),
|
|
224
233
|
EXPO_SWIFT_ENTRY,
|
|
225
|
-
|
|
234
|
+
moduleRoot,
|
|
226
235
|
)
|
|
227
236
|
const host = req.headers.host
|
|
228
237
|
if (!host) throw new Error('Expo Swift request has no host')
|
package/src/kotlin-app.ts
CHANGED
|
@@ -44,13 +44,18 @@ export interface KotlinAppProject {
|
|
|
44
44
|
// the activity's configChanges includes uiMode: a night mode change reaches
|
|
45
45
|
// onConfigurationChanged instead of relaunching it
|
|
46
46
|
handlesUiMode: boolean
|
|
47
|
+
// how the activity's window meets the keyboard: pan over a covered focused
|
|
48
|
+
// view, resize above the keyboard, or nothing
|
|
49
|
+
softInputAdjust: 'pan' | 'resize' | 'nothing'
|
|
47
50
|
}
|
|
48
51
|
|
|
49
|
-
export interface
|
|
52
|
+
export interface AndroidPins {
|
|
50
53
|
ui: string
|
|
51
54
|
foundation: string
|
|
52
55
|
runtime: string
|
|
53
56
|
material3: string
|
|
57
|
+
navigation: string
|
|
58
|
+
activity: string
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
const ANDROID_NS = 'android:'
|
|
@@ -61,18 +66,17 @@ const PLUGINS = new Set([
|
|
|
61
66
|
'org.jetbrains.kotlin.android',
|
|
62
67
|
'org.jetbrains.kotlin.plugin.compose',
|
|
63
68
|
])
|
|
64
|
-
// the closure Peach links. compose
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
'androidx.savedstate',
|
|
70
|
-
])
|
|
71
|
-
const COMPOSE_PIN: Record<string, keyof ComposeAndroidPins> = {
|
|
69
|
+
// the closure Peach links. compose, navigation and activity artifacts must
|
|
70
|
+
// match their Android pins (Peach's activity actuals are that version's); the
|
|
71
|
+
// lifecycle and savedstate artifacts are Peach's platform actuals.
|
|
72
|
+
const PLATFORM_GROUPS = new Set(['androidx.lifecycle', 'androidx.savedstate'])
|
|
73
|
+
const ANDROID_PIN: Record<string, keyof AndroidPins> = {
|
|
72
74
|
'androidx.compose.ui': 'ui',
|
|
73
75
|
'androidx.compose.foundation': 'foundation',
|
|
74
76
|
'androidx.compose.runtime': 'runtime',
|
|
75
77
|
'androidx.compose.material3': 'material3',
|
|
78
|
+
'androidx.navigation': 'navigation',
|
|
79
|
+
'androidx.activity': 'activity',
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
function readGradle(dir: string): { file: string; text: string } | null {
|
|
@@ -128,10 +132,7 @@ function kotlinPluginVersion(
|
|
|
128
132
|
)
|
|
129
133
|
}
|
|
130
134
|
|
|
131
|
-
function checkModuleGradle(
|
|
132
|
-
gradle: { file: string; text: string },
|
|
133
|
-
pins: ComposeAndroidPins,
|
|
134
|
-
) {
|
|
135
|
+
function checkModuleGradle(gradle: { file: string; text: string }, pins: AndroidPins) {
|
|
135
136
|
const at = (index: number) => `${gradle.file}:${lineOf(gradle.text, index)}`
|
|
136
137
|
for (const match of gradle.text.matchAll(
|
|
137
138
|
/\bid\(\s*"([^"]+)"\s*\)|\bkotlin\(\s*"([^"]+)"\s*\)/g,
|
|
@@ -166,11 +167,11 @@ function checkModuleGradle(
|
|
|
166
167
|
if (!coordinate)
|
|
167
168
|
throw new Error(`${where}: ${value} is not a literal group:artifact:version`)
|
|
168
169
|
const [, group = '', artifact, version] = coordinate
|
|
169
|
-
const pin =
|
|
170
|
+
const pin = ANDROID_PIN[group]
|
|
170
171
|
if (pin) {
|
|
171
172
|
if (version !== pins[pin])
|
|
172
173
|
throw new Error(
|
|
173
|
-
`${where}: ${group}:${artifact}:${version} differs from Peach's
|
|
174
|
+
`${where}: ${group}:${artifact}:${version} differs from Peach's ${pin} pin ${pins[pin]}`,
|
|
174
175
|
)
|
|
175
176
|
} else if (!PLATFORM_GROUPS.has(group))
|
|
176
177
|
throw new Error(`${where}: ${group}:${artifact} is not in Peach's Kotlin closure`)
|
|
@@ -305,7 +306,7 @@ function isLauncher(element: XmlElement): boolean {
|
|
|
305
306
|
|
|
306
307
|
export function readKotlinApp(
|
|
307
308
|
moduleArg: string,
|
|
308
|
-
options: { activity?: string; pins:
|
|
309
|
+
options: { activity?: string; pins: AndroidPins },
|
|
309
310
|
): KotlinAppProject {
|
|
310
311
|
const moduleDir = fs.realpathSync(path.resolve(moduleArg))
|
|
311
312
|
const manifestFile = path.join(moduleDir, 'src/main/AndroidManifest.xml')
|
|
@@ -444,6 +445,23 @@ export function readKotlinApp(
|
|
|
444
445
|
const handlesUiMode = (themeOwner?.attributes[`${ANDROID_NS}configChanges`] ?? '')
|
|
445
446
|
.split('|')
|
|
446
447
|
.includes('uiMode')
|
|
448
|
+
// the adjust part of the activity's windowSoftInputMode. ViewRootImpl
|
|
449
|
+
// resolves unspecified to resize only for a window holding a scroll
|
|
450
|
+
// container view, which a ComposeView is not, so it pans
|
|
451
|
+
const adjust =
|
|
452
|
+
(themeOwner?.attributes[`${ANDROID_NS}windowSoftInputMode`] ?? '')
|
|
453
|
+
.split('|')
|
|
454
|
+
.find((flag) => flag.startsWith('adjust')) ?? 'adjustUnspecified'
|
|
455
|
+
const softInputAdjust =
|
|
456
|
+
adjust === 'adjustUnspecified' || adjust === 'adjustPan'
|
|
457
|
+
? 'pan'
|
|
458
|
+
: adjust === 'adjustResize'
|
|
459
|
+
? 'resize'
|
|
460
|
+
: adjust === 'adjustNothing'
|
|
461
|
+
? 'nothing'
|
|
462
|
+
: null
|
|
463
|
+
if (!softInputAdjust)
|
|
464
|
+
throw new Error(`${manifestFile}: unknown windowSoftInputMode ${adjust}`)
|
|
447
465
|
|
|
448
466
|
const sources: string[] = []
|
|
449
467
|
for (const set of ['java', 'kotlin']) {
|
|
@@ -478,5 +496,6 @@ export function readKotlinApp(
|
|
|
478
496
|
launcher: { name: launcherName, targetClass },
|
|
479
497
|
predictiveBack,
|
|
480
498
|
handlesUiMode,
|
|
499
|
+
softInputAdjust,
|
|
481
500
|
}
|
|
482
501
|
}
|
package/src/kotlin-host.ts
CHANGED
|
@@ -385,6 +385,7 @@ export async function kotlinAppArtifact(
|
|
|
385
385
|
` { ${app.applicationClass ?? 'android.app.Application'}() },`,
|
|
386
386
|
` { ${app.launcher.targetClass}() },`,
|
|
387
387
|
` ${app.handlesUiMode},`,
|
|
388
|
+
` ${kotlinString(app.softInputAdjust)},`,
|
|
388
389
|
' androidx.lifecycle.viewmodel.viewModelFactory {',
|
|
389
390
|
...viewModels.map(viewModelInitializer),
|
|
390
391
|
' },',
|
package/src/metro-plugin.ts
CHANGED
|
@@ -30,6 +30,10 @@ import {
|
|
|
30
30
|
insertNativeAppIdentityFooter,
|
|
31
31
|
} from './native-app-identity'
|
|
32
32
|
import { readNativeFirebaseConfig } from './native-firebase-config'
|
|
33
|
+
import {
|
|
34
|
+
insertNativeFirebaseFooter,
|
|
35
|
+
readNativeFirebaseMetadata,
|
|
36
|
+
} from './native-firebase-metadata'
|
|
33
37
|
import { parsePlistSource } from './plist'
|
|
34
38
|
import {
|
|
35
39
|
isRootRuntimeAssetPath,
|
|
@@ -258,6 +262,26 @@ export function withRNX<T extends Record<string, unknown>>(config: T): T {
|
|
|
258
262
|
}
|
|
259
263
|
}
|
|
260
264
|
}
|
|
265
|
+
if (
|
|
266
|
+
[...(graph.dependencies?.values() ?? [])].some((module) =>
|
|
267
|
+
module.path.replace(/\\/g, '/').includes('/@react-native-firebase/app/'),
|
|
268
|
+
)
|
|
269
|
+
) {
|
|
270
|
+
const firebase = readNativeFirebaseMetadata(
|
|
271
|
+
readNativeFirebaseConfig(
|
|
272
|
+
cfgProjectRoot,
|
|
273
|
+
typeof bundlePlatform === 'string' ? [bundlePlatform] : undefined,
|
|
274
|
+
),
|
|
275
|
+
)
|
|
276
|
+
if (firebase)
|
|
277
|
+
annotated =
|
|
278
|
+
typeof annotated === 'string'
|
|
279
|
+
? insertNativeFirebaseFooter(annotated, firebase)
|
|
280
|
+
: {
|
|
281
|
+
...annotated,
|
|
282
|
+
code: insertNativeFirebaseFooter(annotated.code, firebase),
|
|
283
|
+
}
|
|
284
|
+
}
|
|
261
285
|
return annotated
|
|
262
286
|
},
|
|
263
287
|
},
|