peachsim 0.1.659 → 0.1.661
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/README.md +10 -15
- package/cli/cli-update.ts +53 -21
- package/cli/commands/compat.ts +6 -2
- package/cli/commands/control.ts +12 -3
- package/cli/commands/inspect.ts +16 -14
- package/cli/commands/upgrade.ts +4 -2
- package/cli/current-sim.ts +4 -0
- package/cli/drivers/linux-browser-libs.lock.json +1088 -0
- package/cli/drivers/linux-browser-libs.ts +182 -0
- package/cli/drivers/playwright-provisioning.ts +20 -98
- package/cli/drivers/playwright-sim-host.ts +9 -0
- package/cli/drivers/playwright.ts +2 -1
- 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 +18 -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 +45 -41
- 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 +5 -1
- 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 +8 -3
- 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 +1 -1
- package/dist-lib/native-app-identity.cjs +1 -1
- package/dist-lib/native-app-identity.mjs +1 -1
- 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 +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +1339 -180
- 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 +1 -1
- package/dist-lib/vite.cjs +1 -1
- package/kotlin/AppSources/android/app/Activity.kt +48 -3
- package/kotlin/AppSources/android/app/Application.kt +4 -0
- package/kotlin/AppSources/android/content/res/Configuration.kt +4 -0
- package/kotlin/AppSources/android/content/res/Resources.kt +3 -1
- package/kotlin/AppSources/android/view/Window.kt +15 -9
- package/kotlin/AppSources/androidx/activity/ComponentActivity.kt +36 -3
- package/kotlin/AppSources/dev/peach/app/PeachApp.kt +233 -86
- package/kotlin/ComposeSources/PeachComposeHost.kt +15 -3
- package/kotlin/fork/README.md +1 -0
- package/kotlin/fork/compose-worker-globals.patch +26 -0
- package/kotlin/fork/fork.json +1 -1
- package/kotlin/toolchain.json +3 -3
- package/package.json +2 -1
- package/src/android-theme.ts +14 -0
- package/src/backend-origin.ts +25 -0
- package/src/host/bridge-host.ts +24 -37
- package/src/kotlin-app.ts +29 -4
- package/src/kotlin-host.ts +7 -2
package/src/host/bridge-host.ts
CHANGED
|
@@ -592,24 +592,11 @@ export class SootSimBridgeHost {
|
|
|
592
592
|
// subscriptions get cleaned up automatically on close.
|
|
593
593
|
this.agentHost.registerSocket(ws)
|
|
594
594
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
connectedAt: Date.now(),
|
|
601
|
-
lastSeenAt: Date.now(),
|
|
602
|
-
lastActiveAt: 0,
|
|
603
|
-
recentActions: [],
|
|
604
|
-
}
|
|
605
|
-
this.sims.set(sim.id, sim)
|
|
606
|
-
this.writeConnectedRuntimeSnapshot()
|
|
607
|
-
if (this.shouldPromoteSim(sim)) {
|
|
608
|
-
this.primarySimId = sim.id
|
|
609
|
-
}
|
|
610
|
-
this.broadcastSimAssignments()
|
|
611
|
-
this.broadcastSimClientStates()
|
|
612
|
-
} else {
|
|
595
|
+
// a browser socket becomes a sim when its page registers. the shell's
|
|
596
|
+
// agent bridge and other page helpers share this endpoint without ever
|
|
597
|
+
// registering, so giving them a sim id at connect listed ghost sims
|
|
598
|
+
// that took an id and could be neither driven nor closed.
|
|
599
|
+
if (role === 'cli') {
|
|
613
600
|
const fallbackKey = `ws-${this.nextCliFallbackId++}`
|
|
614
601
|
this.cliIdentityKeyBySocket.set(ws, fallbackKey)
|
|
615
602
|
}
|
|
@@ -709,26 +696,30 @@ export class SootSimBridgeHost {
|
|
|
709
696
|
// Origin-based role before this socket has registered a page. This reuses the existing
|
|
710
697
|
// daemon transport and call envelope; it does not add a second routing
|
|
711
698
|
// surface or let a registered sim switch roles mid-connection.
|
|
712
|
-
if (role === 'sim' && msg.type === 'bridge:hello' &&
|
|
713
|
-
const provisionalId = sim.id
|
|
714
|
-
this.sims.delete(provisionalId)
|
|
715
|
-
if (this.primarySimId === provisionalId) {
|
|
716
|
-
this.primarySimId = this.getOpenSim()?.id ?? null
|
|
717
|
-
}
|
|
718
|
-
sim = null
|
|
699
|
+
if (role === 'sim' && msg.type === 'bridge:hello' && !sim) {
|
|
719
700
|
role = 'cli'
|
|
720
701
|
this.cliIdentityKeyBySocket.set(ws, `ws-${this.nextCliFallbackId++}`)
|
|
721
|
-
this.writeConnectedRuntimeSnapshot()
|
|
722
|
-
this.broadcastSimAssignments()
|
|
723
|
-
this.broadcastSimClientStates()
|
|
724
702
|
}
|
|
725
703
|
|
|
726
704
|
if (role === 'sim') {
|
|
727
705
|
if (sim) {
|
|
728
706
|
sim.lastSeenAt = Date.now()
|
|
729
707
|
}
|
|
730
|
-
if (msg.type === 'bridge:register'
|
|
708
|
+
if (msg.type === 'bridge:register') {
|
|
731
709
|
const registration = msg as BridgeSimRegistrationMessage
|
|
710
|
+
const firstRegistration = !sim
|
|
711
|
+
if (!sim) {
|
|
712
|
+
sim = {
|
|
713
|
+
id: this.allocateSimId(),
|
|
714
|
+
ws,
|
|
715
|
+
origin,
|
|
716
|
+
connectedAt: Date.now(),
|
|
717
|
+
lastSeenAt: Date.now(),
|
|
718
|
+
lastActiveAt: 0,
|
|
719
|
+
recentActions: [],
|
|
720
|
+
}
|
|
721
|
+
this.sims.set(sim.id, sim)
|
|
722
|
+
}
|
|
732
723
|
const restored = this.tryRestoreSimId(sim, registration.simId)
|
|
733
724
|
sim.url = registration.url
|
|
734
725
|
sim.title = registration.title
|
|
@@ -743,16 +734,12 @@ export class SootSimBridgeHost {
|
|
|
743
734
|
if (registration.meta && typeof registration.meta === 'object') {
|
|
744
735
|
sim.meta = registration.meta as Record<string, unknown>
|
|
745
736
|
}
|
|
746
|
-
//
|
|
747
|
-
//
|
|
748
|
-
//
|
|
749
|
-
// last-resort fallback (or a stale zombie still holding it)
|
|
750
|
-
// would never be superseded by this freshly-registered, actually
|
|
751
|
-
// driveable sim (QA F19-2). only adopt a *different* sim so a
|
|
752
|
-
// routine heartbeat re-register doesn't churn the assignment.
|
|
737
|
+
// elect primary once the sim has a page; only adopt a *different*
|
|
738
|
+
// sim so a routine heartbeat re-register doesn't churn the
|
|
739
|
+
// assignment.
|
|
753
740
|
const reElected = this.primarySimId !== sim.id && this.shouldPromoteSim(sim)
|
|
754
741
|
if (reElected) this.primarySimId = sim.id
|
|
755
|
-
if (restored || reElected) {
|
|
742
|
+
if (firstRegistration || restored || reElected) {
|
|
756
743
|
this.broadcastSimAssignments()
|
|
757
744
|
this.broadcastSimClientStates()
|
|
758
745
|
}
|
package/src/kotlin-app.ts
CHANGED
|
@@ -5,7 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
import fs from 'node:fs'
|
|
7
7
|
import path from 'node:path'
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
frameworkColor,
|
|
10
|
+
readResourceValues,
|
|
11
|
+
resolveThemeAttribute,
|
|
12
|
+
} from './android-theme'
|
|
9
13
|
import { parseXml, type XmlElement } from './android-xml'
|
|
10
14
|
import frameworkThemes from './generated/android-framework-themes.json' with { type: 'json' }
|
|
11
15
|
|
|
@@ -18,10 +22,11 @@ export interface KotlinAppProject {
|
|
|
18
22
|
namespace: string
|
|
19
23
|
targetSdk: number
|
|
20
24
|
// the launched activity's window as its theme and target SDK configure it;
|
|
21
|
-
// statusBarColor
|
|
25
|
+
// statusBarColor and navigationBarColor are ARGB
|
|
22
26
|
window: {
|
|
23
27
|
edgeToEdgeEnforced: boolean
|
|
24
28
|
statusBarColor: number
|
|
29
|
+
navigationBarColor: number
|
|
25
30
|
statusBarContrastEnforced: boolean
|
|
26
31
|
navigationBarContrastEnforced: boolean
|
|
27
32
|
lightStatusBar: boolean
|
|
@@ -32,6 +37,9 @@ export interface KotlinAppProject {
|
|
|
32
37
|
// back reaches the activity through its window's back dispatch rather than
|
|
33
38
|
// the back key's onBackPressed
|
|
34
39
|
predictiveBack: boolean
|
|
40
|
+
// the activity's configChanges includes uiMode: a night mode change reaches
|
|
41
|
+
// onConfigurationChanged instead of relaunching it
|
|
42
|
+
handlesUiMode: boolean
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
export interface ComposeAndroidPins {
|
|
@@ -218,9 +226,22 @@ function readWindowTheme(
|
|
|
218
226
|
lightStatusBar: flag('windowLightStatusBar'),
|
|
219
227
|
lightNavigationBar: flag('windowLightNavigationBar'),
|
|
220
228
|
}
|
|
229
|
+
// the theme's navigation bar color; left at the framework's transparent
|
|
230
|
+
// default, a window that is not edge to edge takes the compatible color
|
|
231
|
+
// unless the device's config_navBarDefaultTransparent is set, which it is
|
|
232
|
+
// not on the pixel 8 android 17 device the oracle measures
|
|
233
|
+
const navigationColor = resolveThemeAttribute(name, 'navigationBarColor', sources)
|
|
234
|
+
if (typeof navigationColor === 'boolean')
|
|
235
|
+
throw new Error(`${name}: navigationBarColor is not a color`)
|
|
236
|
+
const navigationDefault = frameworkColor('navigation_bar_default', sources)
|
|
237
|
+
const navigationTheme = navigationColor ?? navigationDefault
|
|
238
|
+
const navigationBarColor =
|
|
239
|
+
navigationTheme === navigationDefault && !edgeToEdgeEnforced
|
|
240
|
+
? frameworkColor('navigation_bar_compatible', sources)
|
|
241
|
+
: navigationTheme
|
|
221
242
|
// PhoneWindow enforces edge to edge apart from whether the theme draws bar
|
|
222
243
|
// backgrounds: either way the bar is transparent
|
|
223
|
-
if (edgeToEdgeEnforced) return { ...window, statusBarColor: 0 }
|
|
244
|
+
if (edgeToEdgeEnforced) return { ...window, statusBarColor: 0, navigationBarColor }
|
|
224
245
|
if (!flag('windowDrawsSystemBarBackgrounds'))
|
|
225
246
|
throw new Error(
|
|
226
247
|
`${name} does not draw system bar backgrounds; peach kotlin draws only theme status bar colors`,
|
|
@@ -228,7 +249,7 @@ function readWindowTheme(
|
|
|
228
249
|
const color = resolveThemeAttribute(name, 'statusBarColor', sources)
|
|
229
250
|
if (typeof color === 'boolean')
|
|
230
251
|
throw new Error(`${name}: statusBarColor is not a color`)
|
|
231
|
-
return { ...window, statusBarColor: color ?? 0xff000000 }
|
|
252
|
+
return { ...window, statusBarColor: color ?? 0xff000000, navigationBarColor }
|
|
232
253
|
}
|
|
233
254
|
|
|
234
255
|
function enabled(element: XmlElement): boolean {
|
|
@@ -402,6 +423,9 @@ export function readKotlinApp(
|
|
|
402
423
|
(themeOwner?.attributes[`${ANDROID_NS}enableOnBackInvokedCallback`] ??
|
|
403
424
|
application.attributes[`${ANDROID_NS}enableOnBackInvokedCallback`] ??
|
|
404
425
|
String(targetSdk > 35)) === 'true'
|
|
426
|
+
const handlesUiMode = (themeOwner?.attributes[`${ANDROID_NS}configChanges`] ?? '')
|
|
427
|
+
.split('|')
|
|
428
|
+
.includes('uiMode')
|
|
405
429
|
|
|
406
430
|
const sources: string[] = []
|
|
407
431
|
for (const set of ['java', 'kotlin']) {
|
|
@@ -432,5 +456,6 @@ export function readKotlinApp(
|
|
|
432
456
|
applicationClass: applicationName ? className(applicationName) : null,
|
|
433
457
|
launcher: { name: launcherName, targetClass },
|
|
434
458
|
predictiveBack,
|
|
459
|
+
handlesUiMode,
|
|
435
460
|
}
|
|
436
461
|
}
|
package/src/kotlin-host.ts
CHANGED
|
@@ -337,20 +337,22 @@ export async function kotlinAppArtifact(
|
|
|
337
337
|
'',
|
|
338
338
|
'@JsExport',
|
|
339
339
|
'fun peachAppMount(density: Float, fontScale: Float, locale: String, backend: String, stencilBits: Int,',
|
|
340
|
-
' regular: String, medium: String, bold: String,
|
|
340
|
+
' regular: String, medium: String, bold: String, insets: String, night: Boolean,',
|
|
341
341
|
' invalidate: (Boolean) -> Unit): String = dev.peach.app.peachStartApp(',
|
|
342
342
|
' dev.peach.app.PeachAppSpec(',
|
|
343
343
|
` android.content.ComponentName(${kotlinString(app.packageName)}, ${kotlinString(app.launcher.name)}),`,
|
|
344
344
|
` android.content.ComponentName(${kotlinString(app.packageName)}, ${kotlinString(app.launcher.targetClass)}),`,
|
|
345
345
|
' android.view.WindowTheme(',
|
|
346
346
|
` ${app.window.edgeToEdgeEnforced}, 0x${app.window.statusBarColor.toString(16).padStart(8, '0')}.toInt(),`,
|
|
347
|
+
` 0x${app.window.navigationBarColor.toString(16).padStart(8, '0')}.toInt(),`,
|
|
347
348
|
` ${app.window.statusBarContrastEnforced}, ${app.window.navigationBarContrastEnforced},`,
|
|
348
349
|
` ${app.window.lightStatusBar}, ${app.window.lightNavigationBar},`,
|
|
349
350
|
' ),',
|
|
350
351
|
` { ${app.applicationClass ?? 'android.app.Application'}() },`,
|
|
351
352
|
` { ${app.launcher.targetClass}() },`,
|
|
353
|
+
` ${app.handlesUiMode},`,
|
|
352
354
|
' ),',
|
|
353
|
-
' density, fontScale, locale, backend, stencilBits, regular, medium, bold,
|
|
355
|
+
' density, fontScale, locale, backend, stencilBits, regular, medium, bold, insets, night, invalidate,',
|
|
354
356
|
')',
|
|
355
357
|
'',
|
|
356
358
|
'@JsExport',
|
|
@@ -359,6 +361,9 @@ export async function kotlinAppArtifact(
|
|
|
359
361
|
'@JsExport',
|
|
360
362
|
'fun peachAppTask(foreground: Boolean) = dev.peach.app.peachAppTask(foreground)',
|
|
361
363
|
'',
|
|
364
|
+
'@JsExport',
|
|
365
|
+
'fun peachAppNight(night: Boolean) = dev.peach.app.peachAppNight(night)',
|
|
366
|
+
'',
|
|
362
367
|
].join('\n'),
|
|
363
368
|
}
|
|
364
369
|
const shims = [
|