Skip to content

Commit 1e9e011

Browse files
authored
Overhaul SimDeck documentation for user workflows (#37)
1 parent 41cb5c2 commit 1e9e011

27 files changed

Lines changed: 1337 additions & 2911 deletions

docs/.vitepress/config.mts

Lines changed: 24 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const siteUrl = "https://simdeck.nativescript.org";
77
export default defineConfig({
88
title: "SimDeck",
99
description:
10-
"A local-first iOS Simulator control plane with a browser UI, REST API, and WebRTC video.",
10+
"Stream, inspect, and automate iOS Simulators and Android emulators from a browser, CLI, or test.",
1111
lang: "en-US",
1212
cleanUrls: true,
1313
lastUpdated: true,
@@ -21,7 +21,7 @@ export default defineConfig({
2121
{
2222
property: "og:description",
2323
content:
24-
"A local iOS Simulator control plane with a browser UI, REST API, and WebRTC video.",
24+
"Stream, inspect, and automate iOS Simulators and Android emulators from a browser, CLI, or test.",
2525
},
2626
],
2727
["meta", { property: "og:url", content: `${siteUrl}/` }],
@@ -36,7 +36,7 @@ export default defineConfig({
3636
{ text: "CLI", link: "/cli/", activeMatch: "/cli/" },
3737
{ text: "API", link: "/api/rest", activeMatch: "/api/" },
3838
{
39-
text: "Inspector",
39+
text: "Inspectors",
4040
link: "/inspector/",
4141
activeMatch: "/inspector/",
4242
},
@@ -46,45 +46,39 @@ export default defineConfig({
4646
activeMatch: "/extensions/",
4747
},
4848
{
49-
text: "0.1.0",
49+
text: "0.1.5",
5050
items: [
51-
{
52-
text: "Changelog",
53-
link: `${githubUrl}/releases`,
54-
},
55-
{
56-
text: "Contributing",
57-
link: "/contributing",
58-
},
51+
{ text: "Changelog", link: `${githubUrl}/releases` },
52+
{ text: "Contributing", link: "/contributing" },
5953
],
6054
},
6155
],
6256

6357
sidebar: {
6458
"/guide/": [
6559
{
66-
text: "Getting Started",
60+
text: "Start",
6761
items: [
68-
{ text: "Introduction", link: "/guide/" },
69-
{ text: "Installation", link: "/guide/installation" },
62+
{ text: "Overview", link: "/guide/" },
63+
{ text: "Install", link: "/guide/installation" },
7064
{ text: "Quick Start", link: "/guide/quick-start" },
7165
],
7266
},
7367
{
74-
text: "Concepts",
68+
text: "Use",
7569
items: [
76-
{ text: "Architecture", link: "/guide/architecture" },
77-
{ text: "Video Pipeline", link: "/guide/video" },
70+
{ text: "Daemon", link: "/guide/daemon" },
71+
{ text: "Video & Streaming", link: "/guide/video" },
7872
{ text: "LAN Access", link: "/guide/lan-access" },
79-
{ text: "Project Daemon", link: "/guide/daemon" },
8073
{ text: "Testing", link: "/guide/testing" },
8174
{ text: "GitHub Actions", link: "/guide/github-actions" },
8275
],
8376
},
8477
{
85-
text: "Operating SimDeck",
78+
text: "Reference",
8679
items: [
8780
{ text: "Troubleshooting", link: "/guide/troubleshooting" },
81+
{ text: "How It Works", link: "/guide/architecture" },
8882
{ text: "Contributing", link: "/contributing" },
8983
],
9084
},
@@ -95,50 +89,33 @@ export default defineConfig({
9589
text: "CLI",
9690
items: [
9791
{ text: "Overview", link: "/cli/" },
98-
{ text: "Command Reference", link: "/cli/commands" },
99-
{ text: "Flags & Options", link: "/cli/flags" },
92+
{ text: "Commands", link: "/cli/commands" },
93+
{ text: "Flags", link: "/cli/flags" },
10094
],
10195
},
10296
],
10397

10498
"/api/": [
10599
{
106-
text: "HTTP API",
100+
text: "API",
107101
items: [
108-
{ text: "REST Endpoints", link: "/api/rest" },
102+
{ text: "REST", link: "/api/rest" },
109103
{ text: "Health & Metrics", link: "/api/health" },
110-
],
111-
},
112-
{
113-
text: "Inspectors",
114-
items: [
115104
{ text: "Inspector Protocol", link: "/api/inspector-protocol" },
116105
],
117106
},
118107
],
119108

120109
"/inspector/": [
121110
{
122-
text: "Inspector",
111+
text: "Inspectors",
123112
items: [
124113
{ text: "Overview", link: "/inspector/" },
125114
{ text: "Accessibility", link: "/inspector/accessibility" },
126-
{
127-
text: "Swift In-App Agent",
128-
link: "/inspector/swift",
129-
},
130-
{
131-
text: "NativeScript Runtime",
132-
link: "/inspector/nativescript",
133-
},
134-
{
135-
text: "React Native Runtime",
136-
link: "/inspector/react-native",
137-
},
138-
{
139-
text: "Flutter Runtime",
140-
link: "/inspector/flutter",
141-
},
115+
{ text: "Swift", link: "/inspector/swift" },
116+
{ text: "NativeScript", link: "/inspector/nativescript" },
117+
{ text: "React Native", link: "/inspector/react-native" },
118+
{ text: "Flutter", link: "/inspector/flutter" },
142119
],
143120
},
144121
],
@@ -167,7 +144,7 @@ export default defineConfig({
167144

168145
footer: {
169146
message: "Released under the Apache-2.0 License.",
170-
copyright: `Copyright © 2026 SimDeck contributors.`,
147+
copyright: "Copyright (c) 2026 SimDeck contributors.",
171148
},
172149

173150
outline: {

docs/api/health.md

Lines changed: 42 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
# Health & Metrics
22

3-
Two endpoints expose every observable signal SimDeck collects: `GET /api/health` for the bootstrap surface and `GET /api/metrics` for the running counters.
3+
Use these endpoints to check whether a daemon is reachable and to diagnose stream performance.
44

5-
## `GET /api/health`
5+
## Health
66

7-
Returns the static bootstrap information the browser client needs, plus a freshness timestamp.
7+
```http
8+
GET /api/health
9+
```
10+
11+
Example:
812

913
```json
1014
{
@@ -13,125 +17,54 @@ Returns the static bootstrap information the browser client needs, plus a freshn
1317
"timestamp": 1714094761.234,
1418
"videoCodec": "auto",
1519
"lowLatency": false,
20+
"realtimeStream": true,
21+
"localStreamFps": 60,
22+
"streamQuality": {
23+
"profile": "full"
24+
},
1625
"webRtc": {
1726
"iceServers": [{ "urls": ["stun:stun.l.google.com:19302"] }],
1827
"iceTransportPolicy": "all"
1928
}
2029
}
2130
```
2231

23-
| Field | Notes |
24-
| --------------------------- | ----------------------------------------------------------------------------------------------------- |
25-
| `ok` | Always `true` if the route is reachable. Network failures are signalled by HTTP errors. |
26-
| `httpPort` | HTTP port for the REST API, browser UI, and WebRTC offer endpoint. |
27-
| `timestamp` | Server-side `time.now()` as a fractional Unix epoch in seconds. |
28-
| `videoCodec` | Requested encoder mode. One of `auto`, `hardware`, or `software`. See [Video Pipeline](/guide/video). |
29-
| `lowLatency` | `true` when software H.264 low-latency mode was enabled at daemon startup. |
30-
| `realtimeStream` | `true` when the WebRTC stream is configured to favor freshness and realtime pacing. |
31-
| `localStreamFps` | Local quality stream frame target, from 15 to 240 fps. Defaults to 60. |
32-
| `streamQuality` | Active realtime quality profile and encoder limits such as `maxEdge`, `fps`, and bitrate. |
33-
| `webRtc.iceServers` | ICE servers the browser should use when creating the WebRTC peer connection. |
34-
| `webRtc.iceTransportPolicy` | Browser ICE transport policy. One of `all` or `relay`. |
32+
Important fields:
3533

36-
The default access token is regenerated every time the server restarts. A client should refetch `/api/health` after any disconnection.
34+
| Field | Meaning |
35+
| --------------- | ------------------------------------------------------- |
36+
| `ok` | Server is alive |
37+
| `httpPort` | Port serving UI and API |
38+
| `videoCodec` | Requested codec mode: `auto`, `hardware`, or `software` |
39+
| `streamQuality` | Active stream profile and limits |
40+
| `webRtc` | ICE settings the browser should use |
3741

38-
## `GET /api/metrics`
42+
## Metrics
3943

40-
Returns a snapshot of every server-side counter and the rolling buffer of client-reported stats:
41-
42-
```json
43-
{
44-
"frames_encoded": 12039,
45-
"keyframes_encoded": 17,
46-
"frames_sent": 11982,
47-
"frames_dropped_server": 21,
48-
"keyframe_requests": 4,
49-
"active_streams": 1,
50-
"subscribers_connected": 3,
51-
"subscribers_disconnected": 2,
52-
"avg_send_queue_depth": 0.91,
53-
"max_send_queue_depth": 2,
54-
"latest_first_frame_ms": 412,
55-
"encoders": [
56-
{
57-
"udid": "9D7E5BB7-...",
58-
"encoder": {
59-
"encoderMode": "auto",
60-
"activeEncoderMode": "hardware",
61-
"clientForeground": true,
62-
"autoSoftwareFallbackActive": false,
63-
"hardwareAccelerated": true,
64-
"overloadState": "nominal",
65-
"averageEncoderLoadPercent": 42.1,
66-
"averageEncodeLatencyUs": 7021.0,
67-
"encoderBudgetUs": 16667,
68-
"overloadEvents": 0
69-
}
70-
}
71-
],
72-
"client_streams": [
73-
{
74-
"clientId": "browser-ABC",
75-
"kind": "viewport",
76-
"udid": "9D7E5BB7-...",
77-
"timestampMs": 1714094761234.0,
78-
"codec": "h264",
79-
"width": 1170,
80-
"height": 2532,
81-
"decodedFps": 59.7,
82-
"renderedFps": 59.7,
83-
"droppedFps": 0.0,
84-
"latestRenderMs": 6.2
85-
}
86-
]
87-
}
44+
```http
45+
GET /api/metrics
8846
```
8947

90-
### Counter glossary
91-
92-
| Counter | Increments when… |
93-
| -------------------------- | --------------------------------------------------------------------------------------- |
94-
| `frames_encoded` | The native bridge produces a frame. |
95-
| `keyframes_encoded` | The encoder emits a keyframe (always a subset of `frames_encoded`). |
96-
| `frames_sent` | A frame is written to a WebRTC client. |
97-
| `frames_dropped_server` | A client is too slow and the broadcast channel skips frames for them. |
98-
| `keyframe_requests` | The transport hub asks the encoder for a fresh keyframe (e.g. on reconnect or refresh). |
99-
| `active_streams` | Currently open WebRTC streams. |
100-
| `subscribers_connected` | Lifetime count of WebRTC streams opened. |
101-
| `subscribers_disconnected` | Lifetime count of WebRTC streams closed. |
102-
| `avg_send_queue_depth` | Running average of broadcast channel pressure. |
103-
| `max_send_queue_depth` | Peak broadcast channel pressure. |
104-
| `latest_first_frame_ms` | First-frame latency for the most recent connect, in milliseconds. |
105-
106-
### Encoder overload state
107-
108-
`encoders` contains one entry per active simulator session. `encoder.overloadState`
109-
is derived from native VideoToolbox submit-to-output latency:
48+
Useful fields:
11049

111-
| State | Meaning |
112-
| ------------ | --------------------------------------------------------------------------------------- |
113-
| `nominal` | Smoothed encode latency is comfortably below the active frame budget. |
114-
| `strained` | Smoothed latency is near the frame budget or several frames are close to budget. |
115-
| `overloaded` | Smoothed latency exceeds the budget or several frames in a row took longer than budget. |
50+
| Field | What to look for |
51+
| ---------------------------------- | -------------------------------------------- |
52+
| `latest_first_frame_ms` | First-frame startup time |
53+
| `frames_dropped_server` | Server dropping stale frames to stay current |
54+
| `keyframe_requests` | Stream refresh or recovery activity |
55+
| `active_streams` | Open browser streams |
56+
| `encoders[].encoder.overloadState` | `nominal`, `strained`, or `overloaded` |
57+
| `client_streams` | Recent browser decoder and render reports |
11658

117-
This is an inferred pressure signal rather than a private macOS hardware queue
118-
counter. It is useful for deciding when to lower stream resolution/FPS or switch
119-
from hardware to software encoding. When `encoderMode` is `auto`, SimDeck uses
120-
this signal to temporarily rebuild the active session with software H.264 if the
121-
hardware encoder is overloaded, then retries hardware after a cooldown. The
122-
`activeEncoderMode`, `autoSoftwareFallbackActive`, `autoSoftwareFallbacks`, and
123-
`autoHardwareRetries` fields expose that state. `clientForeground` is `false`
124-
when all current browser viewers for the simulator are hidden or unfocused; in
125-
that state the native session uses software H.264 until a viewer returns
126-
foreground.
59+
If `overloadState` is `overloaded` or dropped frames keep increasing, lower stream quality or restart with software encoding:
12760

128-
### Client stream stats
129-
130-
`client_streams` is a rolling buffer of the most recent reports from browser stream transports. WebRTC clients normally send reports over the telemetry data channel, H.264 WebSocket clients send them on the stream socket, and simple clients can still post to `POST /api/client-stream-stats`. The server keeps the last 48 entries per `(clientId, kind)` pair.
61+
```sh
62+
simdeck daemon restart --video-codec software --stream-quality low
63+
```
13164

132-
The browser client uses these to render its in-app diagnostics overlay and to size its decoder workers. Every field is optional except `clientId` and `kind`; see [`ClientStreamStats`](https://github.com/NativeScript/SimDeck/blob/main/server/src/metrics/counters.rs) for the full schema.
65+
## Submit Client Stats
13366

134-
## Submitting client stats
67+
Custom clients can report their own stream stats:
13568

13669
```http
13770
POST /api/client-stream-stats
@@ -142,31 +75,16 @@ Content-Type: application/json
14275
"kind": "viewport",
14376
"udid": "9D7E5BB7-...",
14477
"codec": "h264",
145-
"width": 1170,
146-
"height": 2532,
14778
"decodedFps": 59.7,
14879
"droppedFps": 0.0,
14980
"latestRenderMs": 6.2
15081
}
15182
```
15283

153-
Required fields:
154-
155-
- `clientId` — any stable identifier you pick.
156-
- `kind` — what slice of the client is reporting (`viewport`, `decoder`, `renderer`, …).
84+
Required fields are `clientId` and `kind`.
15785

158-
Anything else is optional but typed; unknown fields are rejected.
86+
Read only the client buffer:
15987

160-
A successful submission returns:
161-
162-
```json
163-
{ "ok": true }
164-
```
165-
166-
## `GET /api/client-stream-stats`
167-
168-
Returns the same `clientStreams` array that `GET /api/metrics` includes, in case you only want the client-side view:
169-
170-
```json
171-
{ "clientStreams": [ ... ] }
88+
```http
89+
GET /api/client-stream-stats
17290
```

0 commit comments

Comments
 (0)