Skip to content

Commit ce1fd7c

Browse files
authored
Update SimDeck CLI and UI lifecycle docs (#8)
1 parent f33c7b3 commit ce1fd7c

23 files changed

Lines changed: 518 additions & 108 deletions

AGENTS.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,26 +75,32 @@ The current repo uses the private boot path, private display bridge, and private
7575

7676
## Build and Run
7777

78-
Build the browser bundle:
78+
Build the native CLI and browser bundle:
7979

8080
```sh
81-
./scripts/build-client.sh
81+
npm run build
8282
```
8383

84-
Build the native CLI:
84+
Build individual pieces when needed:
8585

8686
```sh
87-
./scripts/build-cli.sh
87+
npm run build:cli
88+
npm run build:client
89+
npm run build:all
90+
npm run package:vscode
8891
```
8992

9093
This now builds the Rust server in `server/` and copies the resulting binary to `build/simdeck`.
9194

9295
Run the local daemon:
9396

9497
```sh
98+
./build/simdeck
9599
./build/simdeck daemon start --port 4310
96100
```
97101

102+
Running without a subcommand starts a foreground workspace daemon, prints local and LAN browser URLs, and stops when the command exits. Pass a simulator name or UDID as the only argument to select it by default in the UI. Use `./build/simdeck -d`, `./build/simdeck -k`, and `./build/simdeck -r` as detached start, kill, and restart shortcuts.
103+
98104
Use software H.264 when macOS screen recording starves the hardware encoder:
99105

100106
```sh

README.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,11 @@ view inside the editor.
3131
## Build
3232

3333
```sh
34-
./scripts/build-client.sh
35-
./scripts/build-cli.sh
34+
npm run build
3635
```
3736

37+
This builds the native CLI and browser client. To build companion packages too, run `npm run build:all`.
38+
3839
Requirements:
3940

4041
- macOS
@@ -71,11 +72,18 @@ Full documentation lives at [simdeck.nativescript.org](https://simdeck.nativescr
7172
## Run
7273

7374
```sh
74-
simdeck ui --open
75+
simdeck
76+
```
77+
78+
This starts a workspace-local foreground daemon, prints local and LAN browser URLs, and stops when you press Ctrl-C.
79+
To focus a specific simulator by name or UDID, pass it as the only argument:
80+
81+
```sh
82+
simdeck "iPhone 17 Pro Max"
7583
```
7684

77-
This starts or reuses the project daemon, enables the browser UI, and opens the authenticated local URL.
78-
To focus a specific simulator, add `?device=UDID` to the opened URL.
85+
Use `simdeck ui --open` or `simdeck daemon start` when you want a reusable background daemon instead.
86+
The no-subcommand lifecycle shortcuts are `simdeck -d` for detached start, `simdeck -k` to kill the background daemon, and `simdeck -r` to restart it.
7987
SimDeck Cloud uses the same server binary as its GitHub Actions provider. The
8088
provider workflow starts `simdeck serve` on the runner, exposes it through a
8189
tunnel, and lets the hosted control plane connect to the simulator with a
@@ -257,6 +265,7 @@ npm run package:vscode-extension
257265
```
258266

259267
This writes `build/vscode/simdeck-vscode.vsix`.
268+
The shorter aliases `npm run package:vscode` and `npm run package:vsix` do the same thing.
260269

261270
Install that local package into VS Code:
262271

bin/simdeck.mjs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env node
22

3-
import { spawnSync } from "node:child_process";
3+
import { spawn } from "node:child_process";
44
import { existsSync } from "node:fs";
55
import path from "node:path";
66
import { fileURLToPath } from "node:url";
@@ -23,14 +23,28 @@ if (!existsSync(binaryPath)) {
2323
process.exit(1);
2424
}
2525

26-
const result = spawnSync(binaryPath, process.argv.slice(2), {
26+
const child = spawn(binaryPath, process.argv.slice(2), {
2727
cwd: process.cwd(),
2828
stdio: "inherit",
2929
});
3030

31-
if (result.error) {
32-
console.error(result.error.message);
31+
child.on("error", (error) => {
32+
console.error(error.message);
3333
process.exit(1);
34+
});
35+
36+
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
37+
process.once(signal, () => {
38+
if (!child.killed) {
39+
child.kill(signal);
40+
}
41+
});
3442
}
3543

36-
process.exit(result.status ?? 1);
44+
child.on("exit", (code, signal) => {
45+
if (signal) {
46+
process.kill(process.pid, signal);
47+
return;
48+
}
49+
process.exit(code ?? 1);
50+
});

cli/DFPrivateSimulatorDisplayBridge.m

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,14 @@ static id DFInitSimDeviceScreen(Class screenClass, id device, uint32_t screenID,
577577
return unwrapped;
578578
}
579579

580+
static NSDictionary<NSNumber *, id> * DFReadAvailableAdapterScreens(id adapterHost, id adapter) {
581+
NSDictionary<NSNumber *, id> *screens = DFReadAdapterScreens(adapterHost);
582+
if (screens.count == 0) {
583+
screens = DFReadAdapterScreens(adapter);
584+
}
585+
return screens;
586+
}
587+
580588
static uint32_t __attribute__((unused)) DFCallSwiftSelfGetterU32(id selfObject, const char *symbolName) {
581589
if (selfObject == nil) {
582590
return 0;
@@ -2268,11 +2276,11 @@ - (nullable instancetype)initWithUDID:(NSString *)udid
22682276
// the bootstrap SimDeviceScreen is allocated — they trickle in over a few
22692277
// seconds (or only after Simulator.app primes the device). Poll instead of
22702278
// relying on a fixed 0.5s sleep.
2271-
NSDictionary<NSNumber *, id> *screens = DFReadAdapterScreens(_screenAdapterHost);
2279+
NSDictionary<NSNumber *, id> *screens = DFReadAvailableAdapterScreens(_screenAdapterHost, _screenAdapter);
22722280
NSDate *screenDeadline = [NSDate dateWithTimeIntervalSinceNow:10.0];
22732281
while (screens.count == 0 && [screenDeadline timeIntervalSinceNow] > 0) {
22742282
DFSpinRunLoop(0.1);
2275-
screens = DFReadAdapterScreens(_screenAdapterHost);
2283+
screens = DFReadAvailableAdapterScreens(_screenAdapterHost, _screenAdapter);
22762284
}
22772285
if (screens.count == 0) {
22782286
DFLog(@"SimulatorKit screen adapter exposed no live screens for %@", udid);
@@ -2958,7 +2966,7 @@ - (void)disconnect {
29582966
((void(*)(id, SEL, id))objc_msgSend)(self->_screenAdapter, sel_registerName("unregisterScreenAdapterCallbacksWithUUID:"), self->_screenAdapterCallbackUUID);
29592967
}
29602968

2961-
NSDictionary<NSNumber *, id> *screens = DFReadAdapterScreens(self->_screenAdapterHost);
2969+
NSDictionary<NSNumber *, id> *screens = DFReadAvailableAdapterScreens(self->_screenAdapterHost, self->_screenAdapter);
29622970
id rawScreen = screens[@(self->_activeScreenID)];
29632971
if (rawScreen != nil && self->_screenCallbackUUID != nil && [rawScreen respondsToSelector:sel_registerName("unregisterScreenCallbacksWithUUID:")]) {
29642972
((void(*)(id, SEL, id))objc_msgSend)(rawScreen, sel_registerName("unregisterScreenCallbacksWithUUID:"), self->_screenCallbackUUID);

cli/XCWChromeRenderer.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ + (nullable NSData *)screenMaskPNGDataForDeviceName:(NSString *)deviceName
268268
CGFloat cornerRadius = chromeCornerRadius;
269269
CGFloat maskCornerRadius = [self framebufferMaskCornerRadiusForChromeInfo:chromeInfo
270270
pointScreenWidth:pointScreenWidth];
271-
if (maskCornerRadius > 0.0) {
271+
if (!phoneProfile && maskCornerRadius > 0.0) {
272272
cornerRadius = maskCornerRadius * radiusScale;
273273
}
274274

client/src/api/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export interface SimulatorMetadata {
1111
name: string;
1212
runtimeName?: string;
1313
runtimeIdentifier?: string;
14+
deviceTypeName?: string;
1415
deviceTypeIdentifier?: string;
1516
isBooted: boolean;
1617
privateDisplay?: PrivateDisplayInfo;

0 commit comments

Comments
 (0)