peachsim 0.1.578 → 0.1.579
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 +1 -1
- package/README.md +11 -9
- package/cli/app-catalog.ts +77 -0
- package/cli/app-fonts.ts +4 -20
- package/cli/app-splash.ts +2 -18
- package/cli/bridge-flow-runner.ts +505 -139
- package/cli/cloud-client.ts +1 -4
- package/cli/cloud-session.ts +1 -4
- package/cli/cloud-test-result-files.ts +3 -0
- package/cli/cloud-test-tunnel.ts +2 -5
- package/cli/command-registry.ts +2 -0
- package/cli/commands/agent.ts +149 -23
- package/cli/commands/assert.ts +1 -4
- package/cli/commands/box/peach-command.ts +62 -0
- package/cli/commands/box/plane-share.ts +0 -70
- package/cli/commands/box/rnx-command.ts +1 -60
- package/cli/commands/box/search-command.ts +14 -0
- package/cli/commands/box.ts +2 -2
- package/cli/commands/control.ts +121 -27
- package/cli/commands/cpu-profile.ts +23 -10
- package/cli/commands/daemon.ts +1 -9
- package/cli/commands/detox.ts +26 -18
- package/cli/commands/device.ts +129 -1
- package/cli/commands/film.ts +25 -16
- package/cli/commands/flow.ts +9 -2
- package/cli/commands/inspect/actions.ts +75 -1
- package/cli/commands/inspect/core.ts +81 -2
- package/cli/commands/inspect/describe.ts +2 -6
- package/cli/commands/inspect/env.ts +1 -1
- package/cli/commands/inspect/settling.ts +20 -3
- package/cli/commands/inspect/shared.ts +1 -4
- package/cli/commands/inspect/sleep.ts +1 -1
- package/cli/commands/inspect.ts +16 -49
- package/cli/commands/install-desktop.ts +8 -6
- package/cli/commands/login.ts +3 -5
- package/cli/commands/maestro-generate.ts +1 -4
- package/cli/commands/maestro.ts +38 -21
- package/cli/commands/no-bridge-hint.ts +0 -14
- package/cli/commands/permissions.ts +2 -2
- package/cli/commands/platform.ts +13 -6
- package/cli/commands/screenshot.ts +6 -1
- package/cli/commands/skills.ts +1 -1
- package/cli/commands/test.ts +6 -2
- package/cli/commands/three-mode.ts +162 -19
- package/cli/commands/upload.ts +3 -18
- package/cli/current-sim.ts +0 -1
- package/cli/desktop-companion.ts +40 -8
- package/cli/drivers/playwright-provisioning.ts +1 -4
- package/cli/flow-export.ts +1 -8
- package/cli/flow-session.ts +0 -4
- package/cli/hints.ts +1 -16
- package/cli/inspect-notice-state.ts +0 -4
- package/cli/main.ts +2 -4
- package/cli/outbound-endpoints.ts +1 -24
- package/cli/parse-args.ts +1 -2
- package/cli/rnx-in-process.ts +3 -1
- package/cli/run-rnx-core.ts +4 -2
- package/cli/run-rnx.ts +18 -10
- package/cli/send-to-box.ts +1 -4
- package/cli/telemetry.ts +1 -1
- package/cli/ws-bridge.ts +1 -5
- package/detox/gestures.ts +20 -6
- package/detox/index.ts +379 -123
- package/detox/navigation-lifecycle.ts +22 -13
- package/dist-lib/agent-daemon-client.cjs +21 -15
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +3 -17
- package/dist-lib/agent-sessions.cjs +5 -4
- package/dist-lib/attached-projects.cjs +1 -77
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +11 -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 +7 -5
- package/dist-lib/bridge-contract-input.mjs +7 -5
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/capture-contract.cjs +30 -7
- package/dist-lib/capture-contract.mjs +30 -7
- 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 +17 -11
- package/dist-lib/cloud.mjs +17 -11
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +321 -122
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +427 -212
- package/dist-lib/host/fetch-proxy-handler.cjs +5 -28
- 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 +315 -58
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/menu.cjs +67 -31
- package/dist-lib/menu.mjs +64 -28
- package/dist-lib/metro-fingerprint-registry.cjs +8 -4
- package/dist-lib/metro-fingerprint-registry.mjs +6 -4
- package/dist-lib/metro-production-bundle.cjs +2 -2
- package/dist-lib/metro-production-bundle.mjs +2 -2
- package/dist-lib/metro.cjs +8 -4
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +7 -4
- 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 +3 -13
- package/dist-lib/scripts/dev-server-scanner.cjs +5 -17
- package/dist-lib/sdk.cjs +129 -10
- package/dist-lib/sdk.mjs +127 -10
- package/dist-lib/skills.cjs +1635 -1121
- package/dist-lib/swift-compile.cjs +277 -70
- package/dist-lib/swift-compile.mjs +275 -70
- package/dist-lib/swift-package.cjs +17 -5
- package/dist-lib/swift-package.mjs +15 -4
- package/dist-lib/swift-submit.cjs +32 -8
- package/dist-lib/swift-submit.mjs +29 -7
- package/dist-lib/swift.cjs +30912 -577
- package/dist-lib/vite-kotlin.cjs +356 -0
- package/dist-lib/vite-swift.cjs +1743 -0
- package/dist-lib/vite.cjs +208 -1039
- package/kotlin/README.md +18 -0
- package/kotlin/Sources/androidx/annotation/Keep.kt +4 -0
- package/kotlin/Sources/dev/onejs/one/OneNativeSourceDispatch.kt +7 -0
- package/kotlin/Sources/dev/onejs/one/source/OneModule.kt +4 -0
- package/kotlin/Sources/org/json/JSON.kt +244 -0
- package/kotlin/toolchain.json +19 -0
- package/package.json +14 -2
- package/scripts/dev-server-scanner.ts +0 -18
- package/scripts/optional-demo-registry.ts +0 -17
- package/skills/contrast/SKILL.md +2 -2
- package/skills/peach-setup/SKILL.md +16 -0
- package/src/agent-daemon-client.ts +36 -22
- package/src/agent-identity.ts +0 -15
- package/src/agent-prompt.ts +12 -0
- package/src/attached-projects.ts +0 -72
- package/src/backend-origin.ts +10 -0
- package/src/bridge-contract-input.ts +2 -5
- package/src/bridge-contract.ts +2 -0
- package/src/capture-contract.ts +53 -11
- package/src/cloud.ts +4 -9
- package/src/dev-server-open.ts +0 -99
- package/src/home-paths.ts +2 -3
- package/src/host/agent-host.ts +82 -36
- package/src/host/bridge-host.ts +5 -11
- package/src/host/fetch-proxy-handler.ts +19 -43
- package/src/is-record.ts +4 -0
- package/src/menu.ts +82 -37
- package/src/metro-fingerprint-registry.ts +1 -4
- package/src/metro-production-bundle.ts +1 -1
- package/src/plist.ts +1 -4
- package/src/public-brand.ts +5 -3
- package/src/render-mode.ts +0 -12
- package/src/runtime-assets.ts +9 -2
- package/src/screenshots/compose.ts +7 -1
- package/src/screenshots/frame-compose.ts +21 -10
- package/src/screenshots/schema.ts +1 -4
- package/src/sim-client.ts +2 -4
- package/src/skills/builtin/maestro-test.ts +1 -4
- package/src/sleep.ts +4 -0
- package/src/swift-compile.ts +209 -47
- package/src/swift-icu.ts +140 -0
- package/src/swift-package.ts +26 -2
- package/src/swift-submit.ts +84 -4
- package/src/swift.ts +2 -0
- package/src/vite-plugin-kotlin.ts +376 -0
- package/src/vite-plugin-one.ts +224 -36
- package/src/vite-plugin-swift.ts +382 -96
- package/src/vite-plugin.ts +121 -134
- package/src/vite.d.ts +10 -0
- package/cli/resolve-assets.ts +0 -63
- package/src/rolldown-plugin-swift.ts +0 -244
- package/src/team-machine-prompt.ts +0 -73
|
@@ -86,31 +86,6 @@ function applyFetchProxyCors(res: ServerResponse) {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
function formatFetchProxyError(targetUrl: string, err: unknown): string {
|
|
90
|
-
const details: string[] = []
|
|
91
|
-
const error = err as
|
|
92
|
-
| {
|
|
93
|
-
message?: string
|
|
94
|
-
code?: string
|
|
95
|
-
cause?: { message?: string; code?: string }
|
|
96
|
-
}
|
|
97
|
-
| undefined
|
|
98
|
-
|
|
99
|
-
if (error?.code) details.push(error.code)
|
|
100
|
-
if (error?.message) details.push(error.message)
|
|
101
|
-
if (error?.cause?.code) details.push(error.cause.code)
|
|
102
|
-
if (error?.cause?.message) details.push(error.cause.message)
|
|
103
|
-
|
|
104
|
-
const uniqueDetails = [...new Set(details.filter(Boolean))]
|
|
105
|
-
const message = uniqueDetails.join(' | ') || String(err)
|
|
106
|
-
|
|
107
|
-
if (targetUrl.includes('stored-in-.env.local')) {
|
|
108
|
-
return `${message} | upstream url still contains placeholder env values`
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return message
|
|
112
|
-
}
|
|
113
|
-
|
|
114
89
|
export function buildFetchProxyHeaders(
|
|
115
90
|
reqHeaders: Record<string, string | string[] | undefined>,
|
|
116
91
|
targetUrl?: URL,
|
|
@@ -141,7 +116,7 @@ export function buildAppApiProxyHeaders(
|
|
|
141
116
|
headers[key] = value
|
|
142
117
|
}
|
|
143
118
|
headers.host = targetUrl.host
|
|
144
|
-
if (!Object.keys(headers).some((name) => name.toLowerCase() === '
|
|
119
|
+
if (!Object.keys(headers).some((name) => name.toLowerCase() === 'mobile-origin')) {
|
|
145
120
|
headers.origin = targetUrl.origin
|
|
146
121
|
headers.referer = `${targetUrl.origin}/`
|
|
147
122
|
}
|
|
@@ -307,12 +282,12 @@ export async function handleFetchProxyRequest(
|
|
|
307
282
|
try {
|
|
308
283
|
await proxy(upstreamUrl, originalMethod, body, 0)
|
|
309
284
|
} catch (err) {
|
|
310
|
-
if (res.headersSent) {
|
|
311
|
-
res.destroy(err instanceof Error ? err : new Error(String(err)))
|
|
312
|
-
return
|
|
313
|
-
}
|
|
314
285
|
const norm = normalizeHostname(upstreamUrl.hostname)
|
|
315
|
-
if (
|
|
286
|
+
if (
|
|
287
|
+
!res.headersSent &&
|
|
288
|
+
isLoopbackHost(norm) &&
|
|
289
|
+
(err as { code?: string })?.code === 'ECONNREFUSED'
|
|
290
|
+
) {
|
|
316
291
|
const altHost =
|
|
317
292
|
norm === '::1' ? '127.0.0.1' : norm === '127.0.0.1' ? '::1' : '127.0.0.1'
|
|
318
293
|
if (altHost !== norm) {
|
|
@@ -324,9 +299,13 @@ export async function handleFetchProxyRequest(
|
|
|
324
299
|
} catch {}
|
|
325
300
|
}
|
|
326
301
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
302
|
+
// an upstream that cannot be reached fails the page's request the way a
|
|
303
|
+
// direct fetch would: as a network error, never a response. a synthetic
|
|
304
|
+
// 5xx reads as a live server answering (the bundle loader then polls it as
|
|
305
|
+
// a cold build) and hands guest code a Response where React Native rejects.
|
|
306
|
+
// close the connection itself: bun's node:http answers a destroyed
|
|
307
|
+
// response that never sent headers with an empty 200.
|
|
308
|
+
req.socket.destroy()
|
|
330
309
|
}
|
|
331
310
|
}
|
|
332
311
|
|
|
@@ -364,11 +343,9 @@ export async function handleBundleProxyRequest(
|
|
|
364
343
|
}
|
|
365
344
|
|
|
366
345
|
export function handleAppApiRequest(req: IncomingMessage, res: ServerResponse): boolean {
|
|
367
|
-
// returns true if it handled the request, false if
|
|
368
|
-
//
|
|
369
|
-
//
|
|
370
|
-
// middleware uses its own out-of-band origin registry; the daemon has
|
|
371
|
-
// no such registry so it returns 400).
|
|
346
|
+
// returns true if it handled the request, false if it is not an /__app-api
|
|
347
|
+
// request. the proxy is stateless: a request without an `origin` query
|
|
348
|
+
// param gets a 400.
|
|
372
349
|
|
|
373
350
|
const reqUrl = req.url || ''
|
|
374
351
|
|
|
@@ -441,10 +418,9 @@ export function handleAppApiRequest(req: IncomingMessage, res: ServerResponse):
|
|
|
441
418
|
proxyRes.pipe(res)
|
|
442
419
|
},
|
|
443
420
|
)
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
})
|
|
421
|
+
// an unreachable app server is a network error, never a 502 (see
|
|
422
|
+
// handleFetchProxyRequest)
|
|
423
|
+
proxyReq.on('error', () => req.socket.destroy())
|
|
448
424
|
req.pipe(proxyReq)
|
|
449
425
|
return true
|
|
450
426
|
}
|
package/src/is-record.ts
ADDED
package/src/menu.ts
CHANGED
|
@@ -29,26 +29,23 @@ export const SOOTSIM_AMBIENT_GLOW_LABELS: Record<SootSimAmbientGlowLevel, string
|
|
|
29
29
|
full: 'Full',
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
export
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
export function isSootSimHomeScreenVersion(
|
|
32
|
+
// render resolution: how many pixels peach renders per iphone point. one
|
|
33
|
+
// setting with three states, carried by every host exactly like the ambient
|
|
34
|
+
// glow level above: the menus, the persisted setting, the ?renderResolution=
|
|
35
|
+
// boot seed, the live set-render-resolution push, electron's shared config.
|
|
36
|
+
export type SootSimRenderResolution = 'high' | 'medium' | 'low'
|
|
37
|
+
export const SOOTSIM_RENDER_RESOLUTION_LEVELS = ['high', 'medium', 'low'] as const
|
|
38
|
+
|
|
39
|
+
export function isSootSimRenderResolution(
|
|
41
40
|
value: unknown,
|
|
42
|
-
): value is
|
|
43
|
-
return value === '
|
|
41
|
+
): value is SootSimRenderResolution {
|
|
42
|
+
return value === 'high' || value === 'medium' || value === 'low'
|
|
44
43
|
}
|
|
45
44
|
|
|
46
|
-
export const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
ios26: 'iOS 26',
|
|
51
|
-
ios27: 'iOS 27',
|
|
45
|
+
export const SOOTSIM_RENDER_RESOLUTION_LABELS: Record<SootSimRenderResolution, string> = {
|
|
46
|
+
high: 'High',
|
|
47
|
+
medium: 'Medium',
|
|
48
|
+
low: 'Low',
|
|
52
49
|
}
|
|
53
50
|
|
|
54
51
|
export type SootSimMenuItemDef =
|
|
@@ -78,16 +75,16 @@ export type SootSimMenuDef = {
|
|
|
78
75
|
|
|
79
76
|
export interface SootSimMenuState {
|
|
80
77
|
currentDevice: string
|
|
81
|
-
homeScreenVersion: SootSimHomeScreenVersion
|
|
82
78
|
// whether this surface is a contributor development surface. unreleased
|
|
83
|
-
// simulator surfaces (the iPhone Duo
|
|
84
|
-
//
|
|
79
|
+
// simulator surfaces (the iPhone Duo) are offered here and nowhere a user
|
|
80
|
+
// can reach.
|
|
85
81
|
developmentMode: boolean
|
|
86
82
|
showBezels: boolean
|
|
87
83
|
showMenuBar: boolean
|
|
88
84
|
showRail: boolean
|
|
89
85
|
solidWindow: boolean
|
|
90
86
|
ambientGlow: SootSimAmbientGlowLevel
|
|
87
|
+
renderResolution: SootSimRenderResolution
|
|
91
88
|
// presentation: the real rendering, or the clean structural wireframe
|
|
92
89
|
wireframe: boolean
|
|
93
90
|
screenshotMode: boolean
|
|
@@ -99,6 +96,12 @@ export interface SootSimMenuState {
|
|
|
99
96
|
sendToBoxCarryAuth: boolean
|
|
100
97
|
stayOnTop: boolean
|
|
101
98
|
scaleMode: SootSimScaleMode
|
|
99
|
+
// fold posture for devices with more than one physical state; null when
|
|
100
|
+
// the current device has none. drives the open/close menu item.
|
|
101
|
+
devicePosture: 'closed' | 'open' | null
|
|
102
|
+
// whether flipping orientation changes the current spec's geometry.
|
|
103
|
+
// computed engine-side; the menu never reads the device catalog.
|
|
104
|
+
canRotate: boolean
|
|
102
105
|
}
|
|
103
106
|
|
|
104
107
|
export interface SootSimMenuDeviceChoice {
|
|
@@ -129,6 +132,7 @@ export type MenuItemDef = SootSimMenuItemDef
|
|
|
129
132
|
export type MenuDef = SootSimMenuDef
|
|
130
133
|
export type MenuState = SootSimMenuState
|
|
131
134
|
export type AmbientGlowLevel = SootSimAmbientGlowLevel
|
|
135
|
+
export type RenderResolution = SootSimRenderResolution
|
|
132
136
|
export type ShortcutPlatform = SootSimMenuPlatform
|
|
133
137
|
export type MenuShortcutEntry = SootSimMenuShortcutEntry
|
|
134
138
|
export type ShortcutDocEntry = SootSimShortcutDocEntry
|
|
@@ -321,6 +325,47 @@ function deviceMenu(
|
|
|
321
325
|
},
|
|
322
326
|
{ type: 'separator' },
|
|
323
327
|
{ label: 'Trigger Screenshot', action: 'screenshot' },
|
|
328
|
+
// pose controls for the current device: open/close on foldables,
|
|
329
|
+
// rotate left/right wherever a turn changes geometry, the way ios
|
|
330
|
+
// simulator holds them (rotate left is cmd+left, rotate right
|
|
331
|
+
// cmd+right). truthiness, not !== null: a partial state must hide
|
|
332
|
+
// these, never show duo items for the wrong device.
|
|
333
|
+
...(state.devicePosture || state.canRotate
|
|
334
|
+
? [
|
|
335
|
+
{ type: 'separator' } as const,
|
|
336
|
+
...(state.devicePosture
|
|
337
|
+
? [
|
|
338
|
+
{
|
|
339
|
+
label:
|
|
340
|
+
state.devicePosture === 'open'
|
|
341
|
+
? 'Close iPhone Duo'
|
|
342
|
+
: 'Open iPhone Duo',
|
|
343
|
+
action: 'toggle-duo-posture',
|
|
344
|
+
},
|
|
345
|
+
]
|
|
346
|
+
: []),
|
|
347
|
+
...(state.canRotate
|
|
348
|
+
? [
|
|
349
|
+
{
|
|
350
|
+
label: 'Rotate Left',
|
|
351
|
+
accelerator: 'CmdOrCtrl+Left',
|
|
352
|
+
action: 'rotate-left',
|
|
353
|
+
browserAccelerator: true,
|
|
354
|
+
shortcutLabel: 'rotate left',
|
|
355
|
+
showInShortcutDocs: true,
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
label: 'Rotate Right',
|
|
359
|
+
accelerator: 'CmdOrCtrl+Right',
|
|
360
|
+
action: 'rotate-right',
|
|
361
|
+
browserAccelerator: true,
|
|
362
|
+
shortcutLabel: 'rotate right',
|
|
363
|
+
showInShortcutDocs: true,
|
|
364
|
+
},
|
|
365
|
+
]
|
|
366
|
+
: []),
|
|
367
|
+
]
|
|
368
|
+
: []),
|
|
324
369
|
{ type: 'separator' },
|
|
325
370
|
...buildSootSimDeviceChoiceDefs(state.currentDevice, deviceChoices, {
|
|
326
371
|
action: 'switch-device',
|
|
@@ -386,6 +431,11 @@ function debugMenu(state: SootSimMenuState): SootSimMenuDef {
|
|
|
386
431
|
checked: state.alwaysShowLiveWebViews,
|
|
387
432
|
action: 'toggle-live-webviews',
|
|
388
433
|
},
|
|
434
|
+
{
|
|
435
|
+
label: 'Share This Tab for WebViews',
|
|
436
|
+
action: 'capture-webviews',
|
|
437
|
+
hiddenIn: 'electron',
|
|
438
|
+
},
|
|
389
439
|
{
|
|
390
440
|
label: 'Send to Box Carries Sign-In',
|
|
391
441
|
type: 'checkbox',
|
|
@@ -482,23 +532,18 @@ function windowMenu(state: SootSimMenuState): SootSimMenuDef {
|
|
|
482
532
|
actionPayload: level,
|
|
483
533
|
})),
|
|
484
534
|
},
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
actionPayload: version,
|
|
498
|
-
})),
|
|
499
|
-
},
|
|
500
|
-
]
|
|
501
|
-
: []),
|
|
535
|
+
{
|
|
536
|
+
// every host: the level only changes backing-store sizes, so the
|
|
537
|
+
// device-shaped electron window shows it the same as the browser.
|
|
538
|
+
label: 'Render Resolution',
|
|
539
|
+
items: SOOTSIM_RENDER_RESOLUTION_LEVELS.map((level) => ({
|
|
540
|
+
label: SOOTSIM_RENDER_RESOLUTION_LABELS[level],
|
|
541
|
+
type: 'radio' as const,
|
|
542
|
+
checked: state.renderResolution === level,
|
|
543
|
+
action: 'set-render-resolution',
|
|
544
|
+
actionPayload: level,
|
|
545
|
+
})),
|
|
546
|
+
},
|
|
502
547
|
{
|
|
503
548
|
label: 'Wireframe',
|
|
504
549
|
type: 'checkbox',
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isRecord } from './is-record'
|
|
1
2
|
export const RNX_METRO_FINGERPRINT_SCHEMA_VERSION = 2
|
|
2
3
|
export const RNX_METRO_FINGERPRINT_POINTER_PATH = '/compile/metro-fingerprints.json'
|
|
3
4
|
export const RNX_METRO_FINGERPRINT_MAX_POINTER_BYTES = 16 * 1024
|
|
@@ -55,10 +56,6 @@ export interface RNXMetroFingerprintPointer {
|
|
|
55
56
|
payloadUrl: string
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
59
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
59
|
function isSha256(value: unknown): value is string {
|
|
63
60
|
return typeof value === 'string' && /^[a-f0-9]{64}$/.test(value)
|
|
64
61
|
}
|
|
@@ -72,7 +72,7 @@ export interface RNXMetroDetachedSourceMapUrl {
|
|
|
72
72
|
export function detachRNXMetroSourceMapUrl(
|
|
73
73
|
bundleText: string,
|
|
74
74
|
): RNXMetroDetachedSourceMapUrl {
|
|
75
|
-
const trimmedEnd = bundleText.
|
|
75
|
+
const trimmedEnd = bundleText.trimEnd()
|
|
76
76
|
const commentStart = trimmedEnd.lastIndexOf(`\n${RNXSIM_METRO_SOURCE_MAP_COMMENT}`)
|
|
77
77
|
if (commentStart < 0) return { source: bundleText, sourceMappingUrl: null }
|
|
78
78
|
const url = trimmedEnd.slice(commentStart + 1 + RNXSIM_METRO_SOURCE_MAP_COMMENT.length)
|
package/src/plist.ts
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import plistModule from '@expo/plist'
|
|
2
|
-
|
|
3
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
4
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
5
|
-
}
|
|
2
|
+
import { isRecord } from './is-record'
|
|
6
3
|
|
|
7
4
|
function resolvePlistParser(value: unknown): ((source: string) => unknown) | null {
|
|
8
5
|
if (!isRecord(value)) return null
|
package/src/public-brand.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
const name = 'Peach'
|
|
2
2
|
|
|
3
|
-
export const
|
|
3
|
+
export const peachPublicBrand = Object.freeze({
|
|
4
4
|
name,
|
|
5
5
|
packageName: 'peachsim',
|
|
6
6
|
commandName: 'peach',
|
|
7
|
-
domain: '
|
|
8
|
-
origin: 'https://
|
|
7
|
+
domain: 'peachsim.com',
|
|
8
|
+
origin: 'https://peachsim.com',
|
|
9
9
|
})
|
|
10
|
+
|
|
11
|
+
export const rnxPublicBrand = peachPublicBrand
|
package/src/render-mode.ts
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
export type RenderMode = 'worker'
|
|
2
2
|
|
|
3
|
-
// worker rendering is now the only supported mode.
|
|
4
|
-
export function getRenderMode(_searchParams: URLSearchParams): RenderMode {
|
|
5
|
-
return 'worker'
|
|
6
|
-
}
|
|
7
|
-
|
|
8
3
|
export function isRenderWorkerMode(_searchParams: URLSearchParams): boolean {
|
|
9
4
|
return true
|
|
10
5
|
}
|
|
@@ -18,10 +13,3 @@ export function hasRenderWorkerBootstrap(
|
|
|
18
13
|
): boolean {
|
|
19
14
|
return typeof workerModuleBaseUrl === 'string' && workerModuleBaseUrl.length > 0
|
|
20
15
|
}
|
|
21
|
-
|
|
22
|
-
export function shouldEnableRenderWorker(
|
|
23
|
-
_searchParams: URLSearchParams,
|
|
24
|
-
workerModuleBaseUrl: string | null | undefined,
|
|
25
|
-
): boolean {
|
|
26
|
-
return hasRenderWorkerBootstrap(workerModuleBaseUrl)
|
|
27
|
-
}
|
package/src/runtime-assets.ts
CHANGED
|
@@ -25,7 +25,7 @@ export const SOOTSIM_RUNTIME_MISSING_MESSAGE =
|
|
|
25
25
|
// until a manual publish — the silent trap that cost a full night of
|
|
26
26
|
// already-fixed bugs re-reproducing.
|
|
27
27
|
//
|
|
28
|
-
// the isolated CI headless path (scripts/local-publish-runtime.sh) sets
|
|
28
|
+
// the isolated CI headless path (scripts/dev/local-publish-runtime.sh) sets
|
|
29
29
|
// PEACH_HOME to a dedicated dir precisely to validate the published-runtime
|
|
30
30
|
// code path; keying off that EXISTING isolation env keeps it (and packaged
|
|
31
31
|
// electron with a custom home) on the installed runtime.
|
|
@@ -112,6 +112,9 @@ const ROOT_RUNTIME_PATHS = [
|
|
|
112
112
|
'/test-wallpaper.jpg',
|
|
113
113
|
'/preview-sw.js',
|
|
114
114
|
'/sootsim.svg',
|
|
115
|
+
'/bedrock-sqlite.wasm',
|
|
116
|
+
'/bedrock-sqlite.js',
|
|
117
|
+
'/sootsim/',
|
|
115
118
|
]
|
|
116
119
|
|
|
117
120
|
export function resolveActiveRuntimeRoot(): string | null {
|
|
@@ -198,7 +201,11 @@ export function resolveRuntimeFilePath(
|
|
|
198
201
|
if (segment === '..') return null
|
|
199
202
|
}
|
|
200
203
|
|
|
201
|
-
const
|
|
204
|
+
const normalized = pathname.startsWith('/sootsim/')
|
|
205
|
+
? pathname.slice('/sootsim'.length)
|
|
206
|
+
: pathname
|
|
207
|
+
|
|
208
|
+
const fullPath = path.resolve(runtimeRoot, normalized.replace(/^\/+/, ''))
|
|
202
209
|
const rootWithSep = runtimeRoot.endsWith(path.sep)
|
|
203
210
|
? runtimeRoot
|
|
204
211
|
: runtimeRoot + path.sep
|
|
@@ -352,7 +352,13 @@ export async function composeMarketingScreenshots(
|
|
|
352
352
|
})
|
|
353
353
|
})())
|
|
354
354
|
try {
|
|
355
|
-
|
|
355
|
+
// each slide's canvas width/height is the authored output raster (the
|
|
356
|
+
// composed image is written at exactly that size), so density stays 1:1
|
|
357
|
+
// and the page is only the compositor host.
|
|
358
|
+
const context = await browser.newContext({
|
|
359
|
+
viewport: { width: 1280, height: 720 },
|
|
360
|
+
deviceScaleFactor: 1,
|
|
361
|
+
})
|
|
356
362
|
try {
|
|
357
363
|
for (const slideSource of slides) {
|
|
358
364
|
const buffer = readFileSync(slideSource.imagePath)
|
|
@@ -27,7 +27,7 @@ const METALLIC_RING_SHADOW =
|
|
|
27
27
|
const FRAME_OUTLINE_BLEED = 1
|
|
28
28
|
|
|
29
29
|
type FrameButtonLayout = {
|
|
30
|
-
side: 'left' | 'right'
|
|
30
|
+
side: 'left' | 'right' | 'top' | 'bottom'
|
|
31
31
|
top: number
|
|
32
32
|
height: number
|
|
33
33
|
width: number
|
|
@@ -184,21 +184,32 @@ export function getFramedScreenshotLayout(model: DeviceModel): FramedScreenshotL
|
|
|
184
184
|
|
|
185
185
|
function renderSideButtonHtml(button: FrameButtonLayout): string {
|
|
186
186
|
const isRight = button.side === 'right'
|
|
187
|
-
const
|
|
187
|
+
const isBottom = button.side === 'bottom'
|
|
188
|
+
const horizontal = button.side === 'top' || isBottom
|
|
189
|
+
const clip = horizontal
|
|
190
|
+
? isBottom
|
|
191
|
+
? 'inset(0 -3px -3px -3px)'
|
|
192
|
+
: 'inset(-3px -3px 0 -3px)'
|
|
193
|
+
: isRight
|
|
194
|
+
? 'inset(-3px -3px -3px 0)'
|
|
195
|
+
: 'inset(-3px 0 -3px -3px)'
|
|
188
196
|
return `
|
|
189
197
|
<div
|
|
190
198
|
aria-hidden="true"
|
|
191
199
|
style="
|
|
192
200
|
position:absolute;
|
|
193
|
-
${
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
201
|
+
${
|
|
202
|
+
horizontal
|
|
203
|
+
? `left:${button.top}px;${isBottom ? `bottom:-${button.width}px;` : `top:-${button.width}px;`}`
|
|
204
|
+
: `${isRight ? `right:-${button.width}px;` : `left:-${button.width}px;`}top:${button.top}px;`
|
|
205
|
+
}
|
|
206
|
+
width:${horizontal ? button.height : button.width}px;
|
|
207
|
+
height:${horizontal ? button.width : button.height}px;
|
|
197
208
|
background-color:#212121;
|
|
198
|
-
border-top-left-radius:${isRight ? 0 : 1.5}px;
|
|
199
|
-
border-bottom-left-radius:${isRight ? 0 : 1.5}px;
|
|
200
|
-
border-top-right-radius:${
|
|
201
|
-
border-bottom-right-radius:${
|
|
209
|
+
border-top-left-radius:${isRight || isBottom ? 0 : 1.5}px;
|
|
210
|
+
border-bottom-left-radius:${isRight || button.side === 'top' ? 0 : 1.5}px;
|
|
211
|
+
border-top-right-radius:${button.side === 'left' || isBottom ? 0 : 1.5}px;
|
|
212
|
+
border-bottom-right-radius:${button.side === 'left' || button.side === 'top' ? 0 : 1.5}px;
|
|
202
213
|
box-shadow:
|
|
203
214
|
inset 0 0.5px 0 #616161,
|
|
204
215
|
inset 0 -0.5px 0 #3F3F3F,
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
type PosePresetName,
|
|
17
17
|
type TextPresetName,
|
|
18
18
|
} from './registry'
|
|
19
|
+
import { isRecord } from '../is-record'
|
|
19
20
|
|
|
20
21
|
export type CaptureMode = 'raw' | 'framed' | 'raw+framed'
|
|
21
22
|
export type CapturePathMode = 'auto' | 'plan' | 'flow'
|
|
@@ -69,10 +70,6 @@ export interface NormalizedScreenshotsPlan {
|
|
|
69
70
|
compose: NormalizedComposePlan
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
73
|
-
return !!value && typeof value === 'object' && !Array.isArray(value)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
73
|
function expectRecord(value: unknown, label: string): Record<string, unknown> {
|
|
77
74
|
if (!isRecord(value)) throw new Error(`${label} must be an object`)
|
|
78
75
|
return value
|
package/src/sim-client.ts
CHANGED
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
type WaitForSootsimIdleResult,
|
|
49
49
|
} from '../cli/commands/inspect/settling'
|
|
50
50
|
import { openAppUrl } from './app-url'
|
|
51
|
+
import { sleep } from './sleep'
|
|
51
52
|
import type { BridgeClaimResult, BridgeSimInfo, WsBridge } from '../cli/ws-bridge'
|
|
52
53
|
import type {
|
|
53
54
|
PerformOptions,
|
|
@@ -159,10 +160,6 @@ export interface SimClientOptions {
|
|
|
159
160
|
commandTimeoutMs?: number
|
|
160
161
|
}
|
|
161
162
|
|
|
162
|
-
function sleep(ms: number): Promise<void> {
|
|
163
|
-
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
164
|
-
}
|
|
165
|
-
|
|
166
163
|
function decodePngDataUrl(dataUrl: unknown): SimScreenshot {
|
|
167
164
|
if (typeof dataUrl !== 'string' || !dataUrl.startsWith('data:image/png;base64,')) {
|
|
168
165
|
throw new SimClientError(
|
|
@@ -192,6 +189,7 @@ const TARGET_SEARCH_PARAMS = [
|
|
|
192
189
|
'inspectOpen',
|
|
193
190
|
'appFonts',
|
|
194
191
|
'appSplash',
|
|
192
|
+
'appCatalog',
|
|
195
193
|
]
|
|
196
194
|
|
|
197
195
|
export function buildSimOpenUrl(currentUrl: string, target: number | string): string {
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
// built-in skill: run Maestro tests against the app
|
|
2
2
|
|
|
3
|
+
import { sleep } from '../../sleep'
|
|
3
4
|
import type { RNXSkill } from '../types'
|
|
4
5
|
|
|
5
|
-
function sleep(ms: number) {
|
|
6
|
-
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
7
|
-
}
|
|
8
|
-
|
|
9
6
|
export const skill: RNXSkill = {
|
|
10
7
|
name: 'maestro-test',
|
|
11
8
|
description: 'Run Maestro YAML tests against the app',
|
package/src/sleep.ts
ADDED