diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..6edd6985 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,65 @@ +name: Deploy Docs + +on: + push: + branches: + - main + paths: + - "docs/**" + - "package.json" + - "package-lock.json" + - ".github/workflows/docs.yml" + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + name: Build VitePress site + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + cache-dependency-path: package-lock.json + + - name: Configure GitHub Pages + uses: actions/configure-pages@v5 + + - name: Install root dependencies + run: npm ci --ignore-scripts + + - name: Build docs + run: npm run docs:build + + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs/.vitepress/dist + + deploy: + name: Deploy to GitHub Pages + needs: build + runs-on: ubuntu-latest + + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 672f4baa..d0c858d5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ client/dist/ packages/inspector-agent/.build/ packages/inspector-agent/.swiftpm/ packages/nativescript-inspector/dist/ +docs/.vitepress/dist/ +docs/.vitepress/cache/ # Package manager installs and caches node_modules/ diff --git a/.prettierignore b/.prettierignore index c9d7112d..128e084f 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,3 +7,5 @@ packages/inspector-agent/.build packages/nativescript-inspector/dist node_modules server/target +docs/.vitepress/dist +docs/.vitepress/cache diff --git a/AGENTS.md b/AGENTS.md index f191b925..e38b3d35 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,6 +8,7 @@ This repository is a local-first simulator control plane. The product goal is a - `client/` is the browser UI. - `skills/xcode-canvas-web/SKILL.md` is the operator guide for using the tool from Codex. - `scripts/` holds repeatable build entrypoints. +- `docs/` is the public VitePress documentation site (deployed to GitHub Pages by `.github/workflows/docs.yml`). The native side should own anything that depends on macOS frameworks, `xcrun simctl`, or private CoreSimulator/SimulatorKit APIs. The web client should stay thin and consume the CLI API. @@ -113,6 +114,7 @@ Useful direct commands: - If you add an API route, add the matching client affordance or document why it stays CLI-only. - If you change the CLI invocation shape, update `README.md` and `skills/xcode-canvas-web/SKILL.md` in the same pass. +- If you change a CLI flag, REST route, packet format, or inspector method, update the matching page under `docs/` in the same pass. - If you expand the private framework bridge, document the Xcode/runtime assumptions here. - If a feature depends on a booted simulator, fail with a clear JSON error instead of silently returning an empty asset. - Do not reintroduce legacy `/stream.h264` handling. The supported live path is Rust-managed WebTransport. diff --git a/README.md b/README.md index 51977bb7..05a74c51 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,10 @@ npm install -g . After a global install, use the `xcode-canvas-web` command directly. From a local checkout, you can also run `./build/xcode-canvas-web`. +## Documentation + +Full documentation lives at [djdeveloperr.github.io/xcode-canvas-web](https://djdeveloperr.github.io/xcode-canvas-web/), with guides, the CLI reference, the REST API, the WebTransport video pipeline, and the inspector protocols. The source for the site lives in [`docs/`](docs/) — preview it locally with `npm run docs:dev`. + ## Run ```sh diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts new file mode 100644 index 00000000..6cfc230e --- /dev/null +++ b/docs/.vitepress/config.mts @@ -0,0 +1,179 @@ +import { defineConfig } from "vitepress"; + +const repoName = "xcode-canvas-web"; +const githubUrl = `https://github.com/DjDeveloperr/${repoName}`; + +export default defineConfig({ + title: "SimDeck", + description: + "A local-first iOS Simulator control plane with a browser UI, REST API, and WebTransport video.", + lang: "en-US", + cleanUrls: true, + lastUpdated: true, + base: `/${repoName}/`, + + head: [ + ["meta", { name: "theme-color", content: "#0a84ff" }], + ["meta", { property: "og:type", content: "website" }], + ["meta", { property: "og:title", content: "SimDeck" }], + [ + "meta", + { + property: "og:description", + content: + "A local iOS Simulator control plane with a browser UI, REST API, and WebTransport video.", + }, + ], + [ + "meta", + { + property: "og:url", + content: `https://djdeveloperr.github.io/${repoName}/`, + }, + ], + ], + + themeConfig: { + siteTitle: "SimDeck", + + nav: [ + { text: "Guide", link: "/guide/", activeMatch: "/guide/" }, + { text: "CLI", link: "/cli/", activeMatch: "/cli/" }, + { text: "API", link: "/api/rest", activeMatch: "/api/" }, + { + text: "Inspector", + link: "/inspector/", + activeMatch: "/inspector/", + }, + { + text: "Extensions", + link: "/extensions/vscode", + activeMatch: "/extensions/", + }, + { + text: "0.1.0", + items: [ + { + text: "Changelog", + link: `${githubUrl}/releases`, + }, + { + text: "Contributing", + link: "/contributing", + }, + ], + }, + ], + + sidebar: { + "/guide/": [ + { + text: "Getting Started", + items: [ + { text: "Introduction", link: "/guide/" }, + { text: "Installation", link: "/guide/installation" }, + { text: "Quick Start", link: "/guide/quick-start" }, + ], + }, + { + text: "Concepts", + items: [ + { text: "Architecture", link: "/guide/architecture" }, + { text: "Video Pipeline", link: "/guide/video" }, + { text: "LAN Access", link: "/guide/lan-access" }, + { text: "Background Service", link: "/guide/service" }, + ], + }, + { + text: "Operating SimDeck", + items: [ + { text: "Troubleshooting", link: "/guide/troubleshooting" }, + { text: "Contributing", link: "/contributing" }, + ], + }, + ], + + "/cli/": [ + { + text: "CLI", + items: [ + { text: "Overview", link: "/cli/" }, + { text: "Command Reference", link: "/cli/commands" }, + { text: "Flags & Options", link: "/cli/flags" }, + ], + }, + ], + + "/api/": [ + { + text: "HTTP API", + items: [ + { text: "REST Endpoints", link: "/api/rest" }, + { text: "Health & Metrics", link: "/api/health" }, + ], + }, + { + text: "Live Video", + items: [ + { text: "WebTransport", link: "/api/webtransport" }, + { text: "Packet Format", link: "/api/packet-format" }, + ], + }, + { + text: "Inspectors", + items: [ + { text: "Inspector Protocol", link: "/api/inspector-protocol" }, + ], + }, + ], + + "/inspector/": [ + { + text: "Inspector", + items: [ + { text: "Overview", link: "/inspector/" }, + { text: "Accessibility (AXe)", link: "/inspector/accessibility" }, + { + text: "Swift In-App Agent", + link: "/inspector/swift", + }, + { + text: "NativeScript Runtime", + link: "/inspector/nativescript", + }, + ], + }, + ], + + "/extensions/": [ + { + text: "Extensions", + items: [ + { text: "VS Code", link: "/extensions/vscode" }, + { text: "Browser Client", link: "/extensions/browser-client" }, + ], + }, + ], + }, + + socialLinks: [{ icon: "github", link: githubUrl }], + + editLink: { + pattern: `${githubUrl}/edit/main/docs/:path`, + text: "Edit this page on GitHub", + }, + + search: { + provider: "local", + }, + + footer: { + message: "Released under the Apache-2.0 License.", + copyright: `Copyright © 2026 SimDeck contributors.`, + }, + + outline: { + level: [2, 3], + }, + }, +}); diff --git a/docs/api/health.md b/docs/api/health.md new file mode 100644 index 00000000..bb589676 --- /dev/null +++ b/docs/api/health.md @@ -0,0 +1,134 @@ +# Health & Metrics + +Two endpoints expose every observable signal SimDeck collects: `GET /api/health` for the bootstrap surface and `GET /api/metrics` for the running counters. + +## `GET /api/health` + +Returns the static bootstrap information the browser client needs to open a WebTransport session, plus a freshness timestamp. + +```json +{ + "ok": true, + "httpPort": 4310, + "wtPort": 4311, + "timestamp": 1714094761.234, + "videoCodec": "hevc", + "webTransport": { + "urlTemplate": "https://127.0.0.1:4311/wt/simulators/{udid}", + "certificateHash": { + "algorithm": "sha-256", + "value": "3f...e9" + }, + "packetVersion": 1 + } +} +``` + +| Field | Notes | +| ------------------------------------ | --------------------------------------------------------------------------------------------- | +| `ok` | Always `true` if the route is reachable. Network failures are signalled by HTTP errors. | +| `httpPort` / `wtPort` | Numeric ports for the HTTP and WebTransport servers. WebTransport is always `httpPort + 1`. | +| `timestamp` | Server-side `time.now()` as a fractional Unix epoch in seconds. | +| `videoCodec` | Active encoder. One of `hevc`, `h264`, `h264-software`. See [Video Pipeline](/guide/video). | +| `webTransport.urlTemplate` | URL with a `{udid}` placeholder for the simulator stream. Substitute and dial. | +| `webTransport.certificateHash.value` | SHA-256 of the server's self-signed cert. Pin via `serverCertificateHashes` in the WT client. | +| `webTransport.packetVersion` | The current binary packet protocol version. Clients should refuse to parse unknown versions. | + +The certificate is regenerated every time the server restarts. A client that caches the hash should refetch `/api/health` after any disconnection. + +## `GET /api/metrics` + +Returns a snapshot of every server-side counter and the rolling buffer of client-reported stats: + +```json +{ + "frames_encoded": 12039, + "keyframes_encoded": 17, + "frames_sent": 11982, + "frames_dropped_server": 21, + "keyframe_requests": 4, + "active_streams": 1, + "subscribers_connected": 3, + "subscribers_disconnected": 2, + "avg_send_queue_depth": 0.91, + "max_send_queue_depth": 2, + "latest_first_frame_ms": 412, + "client_streams": [ + { + "clientId": "browser-ABC", + "kind": "viewport", + "udid": "9D7E5BB7-...", + "timestampMs": 1714094761234.0, + "codec": "hevc", + "width": 1170, + "height": 2532, + "decodedFps": 59.7, + "renderedFps": 59.7, + "droppedFps": 0.0, + "latestRenderMs": 6.2 + } + ] +} +``` + +### Counter glossary + +| Counter | Increments when… | +| -------------------------- | --------------------------------------------------------------------------------------- | +| `frames_encoded` | The native bridge produces a frame. | +| `keyframes_encoded` | The encoder emits a keyframe (always a subset of `frames_encoded`). | +| `frames_sent` | A frame is written to a WebTransport client. | +| `frames_dropped_server` | A client is too slow and the broadcast channel skips frames for them. | +| `keyframe_requests` | The transport hub asks the encoder for a fresh keyframe (e.g. on reconnect or refresh). | +| `active_streams` | Currently open WebTransport sessions. | +| `subscribers_connected` | Lifetime count of WebTransport sessions opened. | +| `subscribers_disconnected` | Lifetime count of WebTransport sessions closed. | +| `avg_send_queue_depth` | Running average of broadcast channel pressure. | +| `max_send_queue_depth` | Peak broadcast channel pressure. | +| `latest_first_frame_ms` | First-frame latency for the most recent connect, in milliseconds. | + +### Client stream stats + +`client_streams` is a rolling buffer of the most recent reports a client posted to `POST /api/client-stream-stats`. The server keeps the last 48 entries per `(clientId, kind)` pair. + +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/DjDeveloperr/xcode-canvas-web/blob/main/server/src/metrics/counters.rs) for the full schema. + +## Submitting client stats + +```http +POST /api/client-stream-stats +Content-Type: application/json + +{ + "clientId": "browser-ABC", + "kind": "viewport", + "udid": "9D7E5BB7-...", + "codec": "hevc", + "width": 1170, + "height": 2532, + "decodedFps": 59.7, + "droppedFps": 0.0, + "latestRenderMs": 6.2 +} +``` + +Required fields: + +- `clientId` — any stable identifier you pick. +- `kind` — what slice of the client is reporting (`viewport`, `decoder`, `renderer`, …). + +Anything else is optional but typed; unknown fields are rejected. + +A successful submission returns: + +```json +{ "ok": true } +``` + +## `GET /api/client-stream-stats` + +Returns the same `clientStreams` array that `GET /api/metrics` includes, in case you only want the client-side view: + +```json +{ "clientStreams": [ ... ] } +``` diff --git a/docs/api/inspector-protocol.md b/docs/api/inspector-protocol.md new file mode 100644 index 00000000..55186907 --- /dev/null +++ b/docs/api/inspector-protocol.md @@ -0,0 +1,273 @@ +# Inspector Protocol + +In-app inspectors talk to SimDeck using a small newline-delimited JSON protocol called `XCWI/0.1`. Both transports (TCP and WebSocket) speak the same envelope and method set, so app-side code is interchangeable between them. + +## Transports + +There are two equivalent transports: + +### Newline-delimited TCP + +The original Swift in-app agent listens on TCP. The default port is `47370`; if it is busy the agent tries the next 32 ports and listens on the first free one. Clients should probe `47370–47402` and call `Inspector.getInfo` to disambiguate. + +```sh +printf '{"id":1,"method":"Inspector.getInfo"}\n' | nc 127.0.0.1 47370 +``` + +The agent also advertises Bonjour service type `_xcwinspector._tcp`. + +### WebSocket via the server + +NativeScript apps connect outbound to the SimDeck server: + +```text +GET /api/inspector/connect +``` + +After connection the server sends `Inspector.getInfo` and waits for a response that includes a `processIdentifier`. Once that arrives, the server treats the WebSocket as the preferred transport for that PID and routes inspector requests there. + +A polling fallback is available for environments without WebSocket support: + +- `GET /api/inspector/poll?processIdentifier=` — long-polls for the next request, returning `204 No Content` if nothing arrives within 25 seconds. +- `POST /api/inspector/response` — submits the response body. + +## Envelopes + +### Request + +```json +{ + "id": 1, + "method": "View.getHierarchy", + "params": { "includeHidden": false } +} +``` + +### Response + +```json +{ + "id": 1, + "result": { "protocolVersion": "0.1", "roots": [] } +} +``` + +### Error + +```json +{ + "id": 1, + "error": { + "code": -32004, + "message": "No view was found for id view:0x1234." + } +} +``` + +### Event + +```json +{ + "event": "Inspector.connected", + "params": { "protocolVersion": "0.1", "framing": "ndjson" } +} +``` + +If the agent is started with an `authToken`, every request must include a matching top-level `token` field. + +All point input and `frameInScreen` values use UIKit screen points, **not pixels**. Multiply by `displayScale` from `Inspector.getInfo` to convert to native pixels. + +## Methods + +The full method list. The SimDeck HTTP proxy at `POST /api/simulators/{udid}/inspector/request` only allows a curated subset (see [REST endpoints](/api/rest#post-api-simulators-udid-inspector-request)); direct TCP/WebSocket clients can call any of them. + +### `Runtime.ping` + +Quick connectivity check. Returns: + +```json +{ "result": { "ok": true } } +``` + +### `Inspector.getInfo` + +Returns protocol version, app process metadata, display scale, coordinate space, and the available method list. Required first call after connect. + +```json +{ + "result": { + "protocolVersion": "0.1", + "processIdentifier": 73214, + "bundleIdentifier": "com.example.MyApp", + "bundleName": "MyApp", + "displayScale": 3, + "coordinateSpace": "uikit-screen-points", + "appHierarchy": { + "available": true, + "source": "nativescript" + } + } +} +``` + +### `View.getHierarchy` + +Returns the current hierarchy rooted at every visible window. + +Params: + +```json +{ "includeHidden": false, "maxDepth": 20, "source": "uikit" } +``` + +By default the agent returns the published framework hierarchy (e.g. NativeScript) when one exists. Pass `"source": "uikit"` to force the raw UIKit tree. + +Published framework nodes may include `sourceLocation`: + +```json +{ + "type": "Label", + "title": "Continue", + "sourceLocation": { + "file": "src/app/home.component.html", + "line": 12, + "column": 5, + "offset": 238 + } +} +``` + +`line` and `column` are one-based when produced by the NativeScript publisher. + +### `View.get` + +Returns one view subtree by id: + +```json +{ + "id": 4, + "method": "View.get", + "params": { "id": "view:0x1234", "maxDepth": 2 } +} +``` + +IDs are process-local and valid until the underlying object is destroyed. + +### `View.hitTest` + +Returns the topmost hit-tested view for a screen point: + +```json +{ + "id": 5, + "method": "View.hitTest", + "params": { "x": 120, "y": 240, "maxDepth": 1 } +} +``` + +### `View.describeAtPoint` + +Returns the hit view plus its ancestor chain. + +```json +{ "id": 6, "method": "View.describeAtPoint", "params": { "x": 120, "y": 240 } } +``` + +### `View.listActions` + +Lists the safe interactions a view supports. + +```json +{ "id": 7, "method": "View.listActions", "params": { "id": "view:0x1234" } } +``` + +### `View.perform` + +Performs a high-level action on a view. + +Supported actions: `tap`, `focus`, `resignFirstResponder`, `accessibilityActivate`, `setText`, `setValue`, `toggle`, `scrollBy`, `scrollTo`. + +Examples: + +```json +{ + "id": 8, + "method": "View.perform", + "params": { "id": "view:0x1234", "action": "tap" } +} +``` + +```json +{ + "id": 9, + "method": "View.perform", + "params": { "id": "view:0x1234", "action": "setText", "value": "hello" } +} +``` + +```json +{ + "id": 10, + "method": "View.perform", + "params": { + "id": "view:0x1234", + "action": "scrollBy", + "y": 400, + "animated": true + } +} +``` + +### `View.getProperties` + +Returns editable runtime properties for a view: + +```json +{ "id": 11, "method": "View.getProperties", "params": { "id": "view:0x1234" } } +``` + +### `View.setProperty` + +Sets a UIKit property dynamically. This is a debug-only escape hatch; agents reject unsafe property names and coerce structured UIKit values such as `UIColor`, `CGRect`, `CGPoint`, `CGSize`, and `UIEdgeInsets`. + +```json +{ + "id": 12, + "method": "View.setProperty", + "params": { + "id": "view:0x1234", + "property": "backgroundColor", + "value": { "$type": "UIColor", "hex": "#FF6600FF" } + } +} +``` + +### `View.evaluateScript` + +Evaluates a small UIKit script against a view. Used by the browser inspector to run pre-canned diagnostics. + +## SwiftUI + +SwiftUI's value tree is not publicly enumerable at runtime. The agent therefore exposes SwiftUI in two ways: + +1. **Automatic detection.** UIKit bridge or hosting views whose runtime classes contain `SwiftUI` or `UIHosting` are reported with `swiftUI.isHost` or `swiftUI.isProbe` markers. +2. **Source-level tags.** Apps can tag SwiftUI views with `View.xcwInspectorTag(_:id:metadata:)` from the Swift agent. Tagged views appear as lightweight probe `UIView`s with `swiftUI.isProbe = true`. + +```swift +Text("Continue") + .xcwInspectorTag("continue-label", id: "onboarding.continue.label") +``` + +## Allowed proxy methods + +When you call the inspector via `POST /api/simulators/{udid}/inspector/request`, the SimDeck server enforces an allow-list to keep the HTTP surface small: + +- `Runtime.ping` +- `View.get` +- `View.evaluateScript` +- `View.getProperties` +- `View.setProperty` +- `View.listActions` +- `View.perform` + +For anything not in this list, talk directly to the inspector over TCP or WebSocket. diff --git a/docs/api/packet-format.md b/docs/api/packet-format.md new file mode 100644 index 00000000..6a653672 --- /dev/null +++ b/docs/api/packet-format.md @@ -0,0 +1,104 @@ +# Packet Format + +The SimDeck WebTransport video stream uses a small fixed-header binary packet format. Every packet has the same shape regardless of codec. + +## Layout + +| Offset | Size | Field | Notes | +| ------ | ---- | ------------------- | ----------------------------------------------------------------------------------------- | +| `0` | `1` | `version` | Protocol version. Currently `1`. | +| `1` | `1` | `flags` | Bitmask. See [Flags](#flags). | +| `2` | `2` | `reserved` | Always `0`. Reserved for future use. | +| `4` | `8` | `frameSequence` | Big-endian `u64`. Strictly monotonic per session. | +| `12` | `8` | `timestampUs` | Big-endian `u64`. Monotonic, in microseconds. | +| `20` | `4` | `width` | Big-endian `u32`. Frame width in pixels. | +| `24` | `4` | `height` | Big-endian `u32`. Frame height in pixels. | +| `28` | `4` | `descriptionLength` | Big-endian `u32`. Bytes of codec description that follow the header. | +| `32` | `4` | `dataLength` | Big-endian `u32`. Bytes of compressed video that follow the description. | +| `36` | `N` | `description` | Optional codec config blob. May be empty. | +| `36+N` | `M` | `data` | Compressed video data. May be empty for keyframes that only carry a configuration update. | + +The header is always exactly 36 bytes. Both `descriptionLength` and `dataLength` can be zero. The total packet length is `36 + descriptionLength + dataLength`. + +## Flags + +| Bit | Constant | Meaning | +| --- | -------------------- | -------------------------------------------------------------------- | +| `0` | `FLAG_KEYFRAME` | Frame is a keyframe (an IDR for H.264, an IDR or CRA for HEVC). | +| `1` | `FLAG_CONFIG` | A codec description blob is present. | +| `2` | `FLAG_DISCONTINUITY` | The previous packet was dropped — the client must flush its decoder. | + +Other bits are reserved and must be ignored by the client. + +## Description blob + +When `FLAG_CONFIG` is set the description bytes carry codec-specific configuration: + +- **HEVC.** Concatenated VPS, SPS, and PPS NAL units in Annex-B form (`00 00 00 01` start codes). +- **H.264 hardware.** Concatenated SPS and PPS NAL units in Annex-B form. +- **H.264 software.** SPS and PPS as Annex-B start-code units. + +A keyframe almost always also has `FLAG_CONFIG` so a client connecting mid-stream can build a fresh decoder without waiting for the next configuration update. + +## Frame data + +The `data` segment is the actual compressed frame: + +- **HEVC.** Annex-B HEVC bitstream. +- **H.264 (hardware or software).** Annex-B H.264 bitstream. + +There is no codec inside the packet to identify the codec — the `codec` field in the [`ControlHello`](/api/webtransport#control-hello) message at session start tells you which decoder to wire up. + +## Sequence numbers and timestamps + +`frameSequence` increments by one for each frame the encoder produces. The client should use it to detect drops and to validate the discontinuity flag. + +`timestampUs` is monotonic but does not necessarily match wall-clock time — it is meant for relative scheduling and pacing, not for synchronising with anything outside the stream. + +## Decoding a packet (pseudocode) + +```ts +function readPacket(view: DataView, offset: number) { + const version = view.getUint8(offset); + if (version !== 1) { + throw new Error(`Unsupported packet version ${version}`); + } + + const flags = view.getUint8(offset + 1); + const sequence = view.getBigUint64(offset + 4, false); + const timestampUs = view.getBigUint64(offset + 12, false); + const width = view.getUint32(offset + 20, false); + const height = view.getUint32(offset + 24, false); + const descriptionLength = view.getUint32(offset + 28, false); + const dataLength = view.getUint32(offset + 32, false); + const headerEnd = offset + 36; + + const description = new Uint8Array(view.buffer, headerEnd, descriptionLength); + const data = new Uint8Array( + view.buffer, + headerEnd + descriptionLength, + dataLength, + ); + + return { + version, + isKeyframe: (flags & 0x01) !== 0, + hasConfig: (flags & 0x02) !== 0, + discontinuity: (flags & 0x04) !== 0, + sequence, + timestampUs, + width, + height, + description, + data, + }; +} +``` + +## Versioning + +`PACKET_VERSION` is currently `1`. The server bumps this version when the wire layout changes in any incompatible way. Clients should: + +- Read the protocol version from `GET /api/health` (`webTransport.packetVersion`) at startup. +- Refuse to process packets whose `version` byte does not match the negotiated version. +- Surface an upgrade message rather than guessing how to parse the new layout. diff --git a/docs/api/rest.md b/docs/api/rest.md new file mode 100644 index 00000000..aae14974 --- /dev/null +++ b/docs/api/rest.md @@ -0,0 +1,377 @@ +# REST Endpoints + +The SimDeck server exposes one REST API over plain HTTP. Every route lives under `/api/`. Responses are JSON unless explicitly noted otherwise. Errors return a JSON body with `{"error": {"message": "..."}}` and an appropriate HTTP status. + +CORS is wide open (`Access-Control-Allow-Origin: *`) so you can call the API from any browser tab on the same network. + +## Conventions + +- Method casing follows REST conventions. `GET` for queries, `POST` for state changes. +- Path parameters use `{name}` notation in this reference. UDIDs come from `GET /api/simulators` (or `xcode-canvas-web list`). +- All control endpoints that mutate a simulator return the refreshed simulator metadata in `{ "simulator": ... }`. +- Times are reported as ISO-8601 strings unless explicitly numeric. + +## Health and metrics + +### `GET /api/health` + +Returns server health, the WebTransport URL template, and the certificate hash the client must pin. + +```json +{ + "ok": true, + "httpPort": 4310, + "wtPort": 4311, + "timestamp": 1714094761.234, + "videoCodec": "hevc", + "webTransport": { + "urlTemplate": "https://127.0.0.1:4311/wt/simulators/{udid}", + "certificateHash": { + "algorithm": "sha-256", + "value": "3f...e9" + }, + "packetVersion": 1 + } +} +``` + +The browser client polls this endpoint at startup and again after a long disconnect to detect server restarts (which rotate the certificate). + +### `GET /api/metrics` + +Returns server-side video stats and a rolling buffer of client-side stats. See [Video Pipeline](/guide/video#tuning-with-metrics) for an annotated example. + +### `GET /api/client-stream-stats` + +Returns just the client-side stats: + +```json +{ "clientStreams": [{ "clientId": "...", "kind": "viewport", ... }] } +``` + +### `POST /api/client-stream-stats` + +Submit a stats sample from a client. The server keeps the last 48 entries per `(clientId, kind)`: + +```http +POST /api/client-stream-stats +Content-Type: application/json + +{ + "clientId": "browser-ABC", + "kind": "viewport", + "codec": "hevc", + "width": 1170, + "height": 2532, + "decodedFps": 59.7, + "droppedFps": 0.0, + "latestRenderMs": 6.2 +} +``` + +Required fields: `clientId` and `kind`. Every other field is optional but typed in `ClientStreamStats`. + +## Simulator inventory + +### `GET /api/simulators` + +Returns every simulator known to the native bridge, enriched with any session state SimDeck has attached: + +```json +{ + "simulators": [ + { + "udid": "9D7E5BB7-...", + "name": "iPhone 15 Pro", + "runtimeName": "iOS 18.0", + "deviceTypeIdentifier": "com.apple.CoreSimulator.SimDeviceType.iPhone-15-Pro", + "isBooted": true, + "privateDisplay": { + "displayReady": true, + "displayStatus": "running", + "displayWidth": 1170, + "displayHeight": 2532, + "frameSequence": 8124 + } + } + ] +} +``` + +`privateDisplay` is `null` until a stream attaches. + +## Simulator lifecycle + +### `POST /api/simulators/{udid}/boot` + +Boots the simulator and returns the refreshed simulator metadata: + +```json +{ "simulator": { ... } } +``` + +### `POST /api/simulators/{udid}/shutdown` + +Tears down the live session (if any) and shuts the simulator down. + +### `POST /api/simulators/{udid}/toggle-appearance` + +Toggles between light and dark appearance via `simctl ui appearance`. + +### `POST /api/simulators/{udid}/refresh` + +Forces the encoder to emit a fresh keyframe. Useful after a discontinuity or when the client decoder drifts. + +```json +{ "ok": true } +``` + +### `POST /api/simulators/{udid}/open-url` + +Opens a URL inside the simulator: + +```http +POST /api/simulators/{udid}/open-url +Content-Type: application/json + +{ "url": "https://example.com" } +``` + +### `POST /api/simulators/{udid}/launch` + +Launches an installed app: + +```http +POST /api/simulators/{udid}/launch +Content-Type: application/json + +{ "bundleId": "com.apple.Preferences" } +``` + +## Input + +### `POST /api/simulators/{udid}/touch` + +Replays a single touch event. For drags, send `began`, one or more `moved`, then `ended` (or `cancelled`). + +```http +POST /api/simulators/{udid}/touch +Content-Type: application/json + +{ "x": 240.0, "y": 480.0, "phase": "began" } +``` + +Allowed `phase` values: `began`, `moved`, `ended`, `cancelled`. + +### `POST /api/simulators/{udid}/key` + +Replays a single keyboard event by HID key code: + +```http +POST /api/simulators/{udid}/key +Content-Type: application/json + +{ "keyCode": 4, "modifiers": 0 } +``` + +`keyCode` is the HID usage value. `modifiers` is a bitmask defined by the HID input subsystem (defaults to `0`). + +### `POST /api/simulators/{udid}/home` + +Presses the home button: + +```json +{ "ok": true } +``` + +### `POST /api/simulators/{udid}/app-switcher` + +Double-presses the home button to invoke the app switcher. + +### `POST /api/simulators/{udid}/rotate-left` + +Rotates the simulator 90° counter-clockwise. + +### `POST /api/simulators/{udid}/rotate-right` + +Rotates the simulator 90° clockwise. + +## Chrome rendering + +### `GET /api/simulators/{udid}/chrome-profile` + +Returns the bezel layout for the simulator: + +```json +{ + "totalWidth": 1240, + "totalHeight": 2602, + "screenX": 35, + "screenY": 35, + "screenWidth": 1170, + "screenHeight": 2532, + "cornerRadius": 220 +} +``` + +The browser client uses this to compose chrome around the live frame. + +### `GET /api/simulators/{udid}/chrome.png` + +Returns the rendered bezel as a PNG. Cache headers are set to `no-cache, no-store, must-revalidate` so changes (e.g. after a device rotation) are picked up immediately. + +## Accessibility + +### `GET /api/simulators/{udid}/accessibility-tree` + +Returns the current accessibility tree. The server merges three sources: NativeScript, Swift in-app agent, and AXe. Use the `source` query parameter to ask for a specific one: + +| `source` | Behaviour | +| ---------------------------- | ---------------------------------------------------------------------------------------------------- | +| `auto` _(default)_ / unset | Use the most accurate source available, falling back to AXe. | +| `nativescript` / `ns` | Force the NativeScript logical tree if a NativeScript inspector is connected for the foreground app. | +| `uikit` / `in-app-inspector` | Force the raw UIKit hierarchy from the in-app inspector agent (NativeScript or Swift). | +| `axe` | Always use the AXe accessibility snapshot. | + +The response always includes: + +```json +{ + "roots": [...], + "source": "nativescript|in-app-inspector|axe", + "availableSources": ["nativescript", "in-app-inspector", "axe"], + "fallbackReason": "...", + "inspector": { ... } +} +``` + +`fallbackReason` is only present when the server could not honour the requested source. + +### `GET /api/simulators/{udid}/accessibility-point?x=...&y=...` + +Returns the AXe-style accessibility description of the topmost element at a screen point. `x` and `y` are in UIKit screen points and must be finite, non-negative numbers. + +## Inspector proxy + +### `POST /api/simulators/{udid}/inspector/request` + +Proxies a single inspector method to the active in-app inspector (NativeScript or Swift) for the simulator. This is used by the browser client to fetch view properties, list available actions, and run debug-only edits. + +```http +POST /api/simulators/{udid}/inspector/request +Content-Type: application/json + +{ + "method": "View.getProperties", + "params": { "id": "view:0x1234" } +} +``` + +Allowed methods (the server enforces this allow-list): + +- `Runtime.ping` +- `View.get` +- `View.evaluateScript` +- `View.getProperties` +- `View.setProperty` +- `View.listActions` +- `View.perform` + +The response includes both the inspector's `result` and metadata about the inspector that handled the request: + +```json +{ + "result": { "id": "view:0x1234", "properties": [...] }, + "inspector": { + "bundleIdentifier": "com.example.MyApp", + "bundleName": "MyApp", + "transport": "websocket", + "processIdentifier": 73214, + "host": "127.0.0.1", + "port": null, + "displayScale": 3, + "protocolVersion": "0.1" + } +} +``` + +For the full method semantics, see the [Inspector Protocol](/api/inspector-protocol). + +## NativeScript inspector hub + +### `GET /api/inspector/connect` + +Upgrades to a WebSocket. Used by the [`@nativescript/xcode-canvas-inspector`](/inspector/nativescript) runtime to register itself as an in-app inspector. + +After connection the server sends `Inspector.getInfo` and waits for a response that includes a `processIdentifier`. Once registered, the server uses this socket as the preferred transport for `accessibility-tree` and `inspector/request` calls that target the same process. + +### `GET /api/inspector/poll?processIdentifier=...` + +Long-poll fallback for environments where the WebSocket transport is not viable. Returns the next pending request as JSON, or `204 No Content` after 25 seconds with no work. + +### `POST /api/inspector/response` + +Posts a response to a previous polled request: + +```http +POST /api/inspector/response +Content-Type: application/json + +{ + "processIdentifier": 73214, + "id": 12, + "result": { "ok": true } +} +``` + +Pass `error` instead of `result` to deliver an error. + +## Logs + +### `GET /api/simulators/{udid}/logs` + +Returns recent simulator logs. Without `backfill=true`, the server tails the live `os_log` stream it has already started for the simulator. With `backfill=true`, the server runs a fresh `simctl spawn ... log show` over the requested window. + +| Query parameter | Default | Notes | +| --------------- | ------- | ----------------------------------------------------------------------------- | +| `backfill` | `false` | When `true`, fetch a one-shot history instead of streaming. | +| `seconds` | `30` | Backfill window in seconds. Clamped to `[1, 1800]`. | +| `limit` | `250` | Max entries to return. Clamped to `[1, 1000]`. | +| `levels` | _none_ | Comma-separated list of log levels to keep (`debug,info,notice,error,fault`). | +| `processes` | _none_ | Comma-separated list of process names (case-insensitive substring matches). | +| `q` | _none_ | Free-text filter applied to the rendered log message. | + +```json +{ + "entries": [ + { + "timestamp": "2026-04-23T19:14:12.123Z", + "level": "info", + "process": "MyApp", + "subsystem": "com.example.MyApp", + "category": "ui", + "pid": 73214, + "message": "Loaded 12 items" + } + ] +} +``` + +## Errors + +Error bodies look like: + +```json +{ + "error": { + "message": "Unknown simulator 9D7E5BB7-..." + } +} +``` + +| Status | Cause | +| ------ | ------------------------------------------------------------------------------------------- | +| `400` | Bad request body or query parameter (e.g. missing `url`, invalid `x`/`y`). | +| `404` | Unknown simulator. | +| `408` | Timed out waiting for a downstream component (encoder keyframe, AXe, inspector). | +| `500` | Unhandled native bridge error. Always reported as JSON with the original message preserved. | diff --git a/docs/api/webtransport.md b/docs/api/webtransport.md new file mode 100644 index 00000000..fdb86302 --- /dev/null +++ b/docs/api/webtransport.md @@ -0,0 +1,119 @@ +# WebTransport + +Live video and the per-session control handshake travel over WebTransport. The hub listens on `port + 1` (so `4311` when HTTP is on `4310`) and serves one path per simulator UDID. + +## URL template + +The exact URL is reported by `GET /api/health`. The template looks like: + +```text +https://:/wt/simulators/{udid} +``` + +Replace `{udid}` with the simulator UDID from `GET /api/simulators`. + +## Certificate pinning + +The server generates a self-signed certificate at startup and exposes its SHA-256 hash on `GET /api/health`: + +```json +{ + "webTransport": { + "certificateHash": { + "algorithm": "sha-256", + "value": "3f...e9" + } + } +} +``` + +Pass the hash to the WebTransport API as `serverCertificateHashes`: + +```ts +const health = await fetch("/api/health").then((r) => r.json()); +const url = health.webTransport.urlTemplate.replace("{udid}", udid); +const transport = new WebTransport(url, { + serverCertificateHashes: [ + { + algorithm: "sha-256", + value: hexToUint8Array(health.webTransport.certificateHash.value), + }, + ], +}); +await transport.ready; +``` + +## Session handshake + +Once a WebTransport session connects to `/wt/simulators/{udid}`, the server: + +1. Resolves the per-UDID `SimulatorSession` (lazily creating one if needed). +2. Calls `ensure_started_async()` to spin up the encoder. +3. Calls `request_refresh()` to force a fresh keyframe. +4. Waits up to 3 seconds for the keyframe. +5. Opens a unidirectional **control** stream and writes one JSON object: the `ControlHello`. +6. Opens a unidirectional **video** stream and starts writing binary frame packets. + +Both streams are unidirectional from server to client. The client never writes back over WebTransport — control commands round-trip through the HTTP API instead. + +If no keyframe arrives within 3 seconds, the server returns an error and the connection is torn down. The client should retry after a short backoff. + +## Control hello + +The first stream the server opens carries one JSON object: + +```json +{ + "version": 1, + "simulatorUdid": "9D7E5BB7-...", + "width": 1170, + "height": 2532, + "codec": "hevc", + "packetFormat": "binary-video-v1" +} +``` + +| Field | Notes | +| ------------------ | -------------------------------------------------------------------------- | +| `version` | The packet protocol version. Always matches `webTransport.packetVersion`. | +| `simulatorUdid` | Echo of the URL UDID for client-side sanity checks. | +| `width` / `height` | Frame dimensions in pixels. | +| `codec` | One of `hevc`, `h264`, `h264-software`, or absent if the encoder is unset. | +| `packetFormat` | Always `binary-video-v1` for the current protocol. | + +The control stream then closes — no further messages are sent on it. + +## Video stream + +The video stream is a continuous sequence of frame packets. Each packet is three concatenated regions: + +| Region | Size | Purpose | +| ------------- | ---------------- | -------------------------------------------------------------------- | +| `header` | 36 bytes | Fixed-size metadata, including the lengths of the two regions below. | +| `description` | optional N bytes | Codec configuration blob (present on keyframes / config updates). | +| `frame data` | M bytes | Compressed video for this frame. | + +Both `N` and `M` are big-endian `u32`s in the header, so the client can frame the next packet without any additional state. + +For the exact byte layout, see [Packet Format](/api/packet-format). + +## Reconnect behaviour + +When a client disconnects (browser tab closed, network drop, server restart) and reconnects: + +- The hub starts a new session from scratch — there is no resume token. +- The server forces a fresh keyframe and re-emits a control hello. +- Client decoder state should be flushed before the first packet of the new session is decoded. + +If the server has been restarted in the meantime, the certificate hash on `/api/health` will have changed. Always refetch `/api/health` before retrying. + +## Backpressure and discontinuities + +Each WebTransport session subscribes to a server-side broadcast channel. If the client is too slow to keep up, the channel drops the oldest pending frames and the hub: + +1. Increments `frames_dropped_server` on the metrics counter. +2. Sets a "waiting for keyframe" flag and ignores subsequent non-keyframes until a fresh one arrives. +3. Calls `request_refresh()` so the encoder forces a keyframe. +4. Sets `FLAG_DISCONTINUITY` on the next packet header so the client knows to flush. + +If the client wants to force a keyframe explicitly (for example after a tab regains focus), it can call `POST /api/simulators/{udid}/refresh`. diff --git a/docs/cli/commands.md b/docs/cli/commands.md new file mode 100644 index 00000000..500ddd78 --- /dev/null +++ b/docs/cli/commands.md @@ -0,0 +1,151 @@ +# Command Reference + +Every subcommand exposed by `xcode-canvas-web`. All of them assume the binary is on your `PATH` after `npm install -g xcode-canvas-web`. Replace `xcode-canvas-web` with `./build/xcode-canvas-web` to run from a local checkout. + +## `serve` + +Start the HTTP and WebTransport servers in the foreground. This is the only command that holds the terminal open. + +```sh +xcode-canvas-web serve [--port ] [--bind ] [--advertise-host ] + [--client-root ] [--video-codec ] +``` + +| Flag | Default | Description | +| ------------------ | --------------------- | -------------------------------------------------------------------------------------- | +| `--port` | `4310` | HTTP port. WebTransport listens on `port + 1`. | +| `--bind` | `127.0.0.1` | Bind address (`0.0.0.0` for [LAN access](/guide/lan-access), `::` for IPv6). | +| `--advertise-host` | matches `--bind` | Hostname or IP advertised to remote clients in the WebTransport URL template and cert. | +| `--client-root` | bundled `client/dist` | Override the static client directory. | +| `--video-codec` | `hevc` | One of `hevc`, `h264`, `h264-software`. See [Video Pipeline](/guide/video). | + +When the server is up it prints something like: + +```text +HTTP listening on http://127.0.0.1:4310 +WebTransport listening on https://127.0.0.1:4311/wt/simulators/{udid} +Serving client from /usr/local/lib/node_modules/xcode-canvas-web/client/dist +``` + +`Ctrl-C` shuts both servers down cleanly. + +## `service on` + +Install SimDeck as a per-user `launchd` service. Same flags as `serve`: + +```sh +xcode-canvas-web service on [--port ] [--bind ] [--advertise-host ] + [--client-root ] [--video-codec ] +``` + +The command writes `~/Library/LaunchAgents/dev.nativescript.xcode-canvas-web.plist`, bootstraps it into `gui/`, and immediately kickstarts it. See [Background Service](/guide/service) for details. + +Output (JSON): + +```json +{ + "ok": true, + "service": "dev.nativescript.xcode-canvas-web", + "plist": "/Users/you/Library/LaunchAgents/dev.nativescript.xcode-canvas-web.plist", + "stdoutLog": "/Users/you/Library/Logs/xcode-canvas-web.log", + "stderrLog": "/Users/you/Library/Logs/xcode-canvas-web.err.log" +} +``` + +## `service off` + +Remove the launchd service: + +```sh +xcode-canvas-web service off +``` + +Output (JSON): + +```json +{ + "ok": true, + "service": "dev.nativescript.xcode-canvas-web", + "plist": "/Users/you/Library/LaunchAgents/dev.nativescript.xcode-canvas-web.plist" +} +``` + +The plist is removed, but the log files under `~/Library/Logs` are kept. + +## `list` + +Print every simulator the native bridge can see, as JSON: + +```sh +xcode-canvas-web list +``` + +```json +{ + "simulators": [ + { + "udid": "9D7E5BB7-...", + "name": "iPhone 15 Pro", + "runtimeName": "iOS 18.0", + "deviceTypeIdentifier": "com.apple.CoreSimulator.SimDeviceType.iPhone-15-Pro", + "isBooted": true + } + ] +} +``` + +This is roughly equivalent to `xcrun simctl list devices --json`, but the output is filtered down to the fields SimDeck exposes through `GET /api/simulators`. + +## `boot` + +Boot a simulator by UDID: + +```sh +xcode-canvas-web boot +``` + +```json +{ "ok": true, "udid": "9D7E5BB7-...", "action": "boot" } +``` + +The native bridge prefers a private `CoreSimulator` direct boot when available and falls back to `xcrun simctl boot` otherwise. + +## `shutdown` + +Shut a simulator down by UDID: + +```sh +xcode-canvas-web shutdown +``` + +```json +{ "ok": true, "udid": "9D7E5BB7-...", "action": "shutdown" } +``` + +If the server has a live session attached for the UDID, the registry tears it down before issuing the shutdown. + +## `open-url` + +Open a URL inside the simulator. This goes through `xcrun simctl openurl`, which routes `https://` and `http://` to MobileSafari and any other scheme to whichever app handles it: + +```sh +xcode-canvas-web open-url https://example.com +``` + +```json +{ "ok": true, "udid": "9D7E5BB7-...", "url": "https://example.com" } +``` + +## `launch` + +Launch an installed app by its bundle identifier. This goes through `xcrun simctl launch`: + +```sh +xcode-canvas-web launch com.apple.Preferences +``` + +```json +{ "ok": true, "udid": "9D7E5BB7-...", "bundleId": "com.apple.Preferences" } +``` + +If the simulator is shut down the command fails with a clear error. Boot it first with `xcode-canvas-web boot `. diff --git a/docs/cli/flags.md b/docs/cli/flags.md new file mode 100644 index 00000000..40f02c12 --- /dev/null +++ b/docs/cli/flags.md @@ -0,0 +1,89 @@ +# Flags & Options + +A consolidated list of every flag accepted by the SimDeck CLI, grouped by where it applies. + +## Global flags + +There are currently no global flags. Every option is scoped to a subcommand. + +::: tip Help output +Pass `--help` to any subcommand to see the same flag list directly from the binary, including any flags that may have been added after this page was written: + +```sh +xcode-canvas-web serve --help +xcode-canvas-web service on --help +``` + +::: + +## `serve` and `service on` flags + +These two subcommands accept the same flags because the service command writes them straight into the launchd plist. + +### `--port ` + +| Default | `4310` | +| ------- | --------------- | +| Type | unsigned 16-bit | + +The HTTP port. WebTransport listens on `port + 1`. + +### `--bind ` + +| Default | `127.0.0.1` | +| ------- | ------------ | +| Type | IPv4 or IPv6 | + +Bind address for both the HTTP server and the WebTransport server. Common values: + +- `127.0.0.1` — localhost only. +- `0.0.0.0` — every IPv4 interface. +- `::` — every IPv4 and IPv6 interface (when supported by the OS dual-stack config). +- A specific interface IP. + +### `--advertise-host ` + +| Default | matches `--bind` (or `localhost` for unspecified addresses) | +| ------- | ----------------------------------------------------------- | +| Type | hostname or IP literal | + +Hostname or IP that gets baked into the WebTransport URL template advertised at `GET /api/health`, and added to the certificate's Subject Alternative Names. + +If you bind to `0.0.0.0` and don't pass `--advertise-host`, SimDeck warns at startup because the default `localhost` won't work for remote clients. + +### `--client-root ` + +| Default | `client/dist` next to the binary, falling back to `./client/dist` | +| ------- | ----------------------------------------------------------------- | +| Type | filesystem path | + +Override the static client directory. The Rust server serves the contents at the HTTP root and falls through to a 404 for missing files. + +### `--video-codec ` + +| Default | `hevc` | +| ------- | -------------------------------------- | +| Type | one of `hevc`, `h264`, `h264-software` | + +Encoder used by the native bridge. See [Video Pipeline](/guide/video) for when to switch. + +## Positional arguments + +Subcommands that take positionals expect them in the order shown: + +| Command | Positionals | Notes | +| ---------- | ------------------- | -------------------------------------------- | +| `boot` | `` | Simulator UDID from `xcode-canvas-web list`. | +| `shutdown` | `` | | +| `open-url` | ` ` | Any URL scheme accepted by `simctl openurl`. | +| `launch` | ` ` | App must already be installed. | + +## Exit codes + +| Exit code | Meaning | +| --------- | -------------------------------------------------------------------------- | +| `0` | Success. | +| `1` | Command-level failure (bad usage, missing simulator, native bridge error). | +| `2` | Reserved by Clap for usage / parser errors. | + +Errors print a short message to stderr; structured JSON is reserved for success output. diff --git a/docs/cli/index.md b/docs/cli/index.md new file mode 100644 index 00000000..bcdd5c50 --- /dev/null +++ b/docs/cli/index.md @@ -0,0 +1,39 @@ +# CLI + +The `xcode-canvas-web` binary is the only entrypoint SimDeck ships. It hosts the HTTP server, manages the launchd service, and exposes a small set of simulator-control subcommands that are convenient from scripts. + +## Synopsis + +```sh +xcode-canvas-web [OPTIONS] +``` + +## Top-level commands + +| Command | Purpose | +| -------------------------- | ------------------------------------------------------------------------------------------- | +| `serve` | Start the HTTP and WebTransport servers in the foreground. | +| `service on/off` | Install or remove the per-user `launchd` service. See [Background Service](/guide/service). | +| `list` | Print every simulator known to the native bridge as JSON. | +| `boot ` | Boot the given simulator. | +| `shutdown ` | Shut the given simulator down. | +| `open-url ` | Open a URL inside the simulator (Safari for `https://`, deep links otherwise). | +| `launch ` | Launch an installed app by its bundle identifier. | + +Every subcommand returns an exit code that follows shell conventions: zero on success, non-zero on failure. + +## Output format + +Most subcommands print JSON. This makes the CLI easy to consume from scripts: + +```sh +xcode-canvas-web list | jq '.simulators[] | select(.isBooted)' +``` + +Errors print a short human-readable message to stderr and a non-zero exit code. They do not print structured JSON for failure cases. + +## See also + +- **[Command Reference](/cli/commands)** — every subcommand in detail. +- **[Flags & Options](/cli/flags)** — global flags and per-subcommand options. +- **[REST API](/api/rest)** — the HTTP equivalent of every CLI subcommand. diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 00000000..60ba53fe --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,153 @@ +# Contributing + +SimDeck welcomes contributions. This page covers the toolchain, the layout, and the working rules to follow when proposing a change. + +## Toolchain + +You'll need: + +- **macOS 13+** with the iOS Simulator runtimes installed. +- **Xcode command-line tools**: `xcode-select --install`. +- **Node.js ≥ 18** and npm. +- **Rust stable** via [rustup](https://rustup.rs/). + +Optional: + +- **`prettier`** for formatting (installed via `npm install`). +- **`cargo fmt`** and **`cargo clippy`** for Rust formatting and lints (ship with rustup). +- **AXe** if you want to test the accessibility fallback path. + +## First-time setup + +Clone, install dependencies, and build everything: + +```sh +git clone https://github.com/DjDeveloperr/xcode-canvas-web.git +cd xcode-canvas-web +npm install +npm run build +``` + +`npm install` runs the postinstall hook that compiles the Rust + Objective-C CLI. `npm run build` rebuilds everything top-to-bottom (Rust binary, React bundle, NativeScript inspector). + +## Running locally + +```sh +npm run dev +``` + +This starts the Rust server in the background and runs the Vite dev server for the React client. The server log lands at `build/cli.log`. + +To run only the production server: + +```sh +./build/xcode-canvas-web serve --port 4310 +``` + +## Layout + +| Folder | What lives here | +| ---------------------------------- | -------------------------------------------------------------------------------------- | +| `server/` | Rust HTTP server, WebTransport hub, inspector hub, registry, metrics, launchd service. | +| `cli/` | Objective-C native bridge for private CoreSimulator and SimulatorKit APIs. | +| `client/` | React UI served at `/`. | +| `packages/nativescript-inspector/` | TypeScript runtime for the NativeScript inspector. | +| `packages/inspector-agent/` | Swift Package for the Swift in-app inspector agent. | +| `packages/vscode-extension/` | VS Code extension that opens the simulator inside an editor panel. | +| `scripts/` | Repeatable build entrypoints used by both local dev and CI. | +| `bin/` | Node launcher that locates and runs the compiled binary. | +| `docs/` | This documentation site (VitePress). | + +## Working rules + +If you contribute, keep these invariants in mind. They are also enforced by the `AGENTS.md` guide that lives at the repo root. + +- Simulator-native logic stays in Objective-C under `cli/`. +- Rust server logic stays under `server/`. +- Browser-only presentation logic stays in `client/`. +- NativeScript app runtime inspection logic stays in `packages/nativescript-inspector/`. +- Prefer adding a server endpoint before adding client-only assumptions. +- Don't add a Node or Swift dependency to solve work that already fits in Foundation/AppKit. +- When touching private API usage, keep the adaptation small and explicit and document any simulator/runtime assumptions in `AGENTS.md`. +- Prefer stable CLI subcommands over hidden environment variables. +- The supported live video path is WebTransport-only. Do not bring back legacy `/stream.h264` handling. +- If a feature depends on a booted simulator, fail with a clear JSON error instead of silently returning an empty asset. + +## Linting and formatting + +Format the entire repo: + +```sh +npm run format +``` + +Check formatting in CI mode (no writes): + +```sh +npm run format:check +``` + +Run all lints: + +```sh +npm run lint +``` + +This runs: + +- `prettier --check .` +- `cargo fmt --check` +- `cargo clippy --all-targets -- -D warnings` +- `tsc --noEmit` for the React client. + +## Tests + +```sh +npm run test +``` + +This runs the Cargo test suite for the server and the Vitest suite for the client. + +## Full CI pipeline + +```sh +npm run ci +``` + +This is the same script that GitHub Actions runs: + +1. `npm run lint` — formatting and lint checks. +2. `npm run build` — Rust + Objective-C, React client, NativeScript inspector. +3. `npm run test` — Rust and TypeScript tests. +4. `npm run package:vscode-extension` — VS Code `.vsix`. + +A clean `npm run ci` is required for any PR. + +## Documentation + +This site is a VitePress project under `docs/`. To preview it: + +```sh +npm run docs:dev +``` + +To build the static site: + +```sh +npm run docs:build +``` + +The build artefact lands at `docs/.vitepress/dist`. The docs deploy workflow (`.github/workflows/docs.yml`) publishes that directory to GitHub Pages on every push to `main`. + +When you change something in the repo that the docs already cover — a CLI flag, a route, a packet field, an inspector method — please update the matching docs page in the same PR. + +## Filing issues and PRs + +- Open an issue for anything that requires discussion before code. +- For straightforward fixes, a PR is fine without a paired issue. +- Include reproduction steps and the macOS / Xcode version when filing simulator-related bugs. +- Include the server log (foreground or `~/Library/Logs/xcode-canvas-web*.log`) when filing video-stream bugs. + +## License + +SimDeck is licensed under the Apache License 2.0. By contributing you agree to license your changes under the same terms. diff --git a/docs/extensions/browser-client.md b/docs/extensions/browser-client.md new file mode 100644 index 00000000..4d7f5c96 --- /dev/null +++ b/docs/extensions/browser-client.md @@ -0,0 +1,106 @@ +# Browser Client + +The default UI SimDeck serves at `/` is a React app built with Vite. It lives at `client/` in this repo and is bundled into `client/dist/` for production. The Rust server serves the bundle as static assets. + +You almost certainly don't need to know about the internals — the client just works. This page is for contributors and for anyone who wants to embed the same surface somewhere else. + +## Tech stack + +- **React 19** — view layer. +- **TypeScript** — strict typing for everything in `client/src/`. +- **Vite** — dev server and production build. +- **Vitest** — unit tests. + +## Layout + +```text +client/ +├── index.html +├── vite.config.js +├── package.json +└── src/ + ├── main.tsx + ├── app/ + │ ├── App.tsx + │ └── AppShell.tsx + ├── api/ + │ ├── client.ts + │ ├── controls.ts + │ ├── simulators.ts + │ └── types.ts + ├── features/ + │ ├── simulators/ + │ ├── viewport/ + │ ├── stream/ + │ ├── input/ + │ ├── accessibility/ + │ └── toolbar/ + ├── shared/ + ├── styles/ + └── workers/ +``` + +| Folder | Responsibility | +| ------------------------- | ----------------------------------------------------------------------- | +| `api/` | Typed wrappers around the SimDeck REST API and shared TypeScript types. | +| `features/simulators/` | Sidebar list of simulators plus boot/shutdown affordances. | +| `features/viewport/` | Frame canvas, chrome compositing, hit testing. | +| `features/stream/` | WebTransport reader, decoder workers, frame queueing. | +| `features/input/` | Touch / keyboard / hardware-button affordances. | +| `features/accessibility/` | Accessibility tree pane and source switcher. | +| `features/toolbar/` | Top toolbar (rotate, home, app switcher, dark mode toggle, refresh). | +| `workers/` | Off-main-thread video decoder workers. | + +## Bootstrap flow + +1. The browser fetches `index.html` from the Rust server. +2. `main.tsx` mounts the React tree at `#root`. +3. `AppShell` calls `GET /api/health` to learn the WebTransport URL template, certificate hash, and packet version. +4. The simulator sidebar fetches `GET /api/simulators` and renders the list. +5. Selecting a simulator opens a WebTransport session at `wss://.../wt/simulators/` pinned by the cert hash. +6. The decoder worker parses [packet headers](/api/packet-format), reassembles description + data, and pushes decoded frames to the renderer. +7. Touch and key events round-trip through `POST /api/simulators//touch` and `/key`. + +## Dev workflow + +The repo's `npm run dev` script runs the server and Vite together: + +```sh +npm run dev +``` + +This: + +- Builds the Rust CLI if it isn't built. +- Stops any stale SimDeck server listening on `4310` or `4311`. +- Starts the Rust server in the background, logging to `build/cli.log`. +- Runs `vite` from `client/` against the local server, with hot-module reload. + +Vite serves on `http://127.0.0.1:5173` and proxies API calls to the Rust server on `4310`. + +## Tests and types + +```sh +npm run --prefix client typecheck +npm run --prefix client test +``` + +`typecheck` runs `tsc --noEmit` against the strict client config. `test` runs the Vitest suite in `client/src/`. + +## Replacing the client + +The Rust server takes a `--client-root ` flag. You can ship a completely different UI by pointing it at a directory of static files: + +```sh +xcode-canvas-web serve --port 4310 --client-root /path/to/your/dist +``` + +As long as your client speaks the documented [REST API](/api/rest), [WebTransport](/api/webtransport), and [Packet Format](/api/packet-format), it will work end to end. + +## Embedding in another app + +The browser client is designed to live inside any container that can host a webview. The bundled VS Code extension is one example; embedding it in an Electron app, a Tauri shell, or a custom dashboard works the same way: + +1. Point the host at `http://:/`. +2. Allow the host to talk to the same WebTransport endpoint exposed by the server. +3. Optionally, gate the host behind your own auth — SimDeck assumes a trusted local network. diff --git a/docs/extensions/vscode.md b/docs/extensions/vscode.md new file mode 100644 index 00000000..df584e67 --- /dev/null +++ b/docs/extensions/vscode.md @@ -0,0 +1,68 @@ +# VS Code Extension + +A bundled VS Code extension opens the SimDeck browser client inside an editor panel and auto-starts the local server when needed. The extension lives in `packages/vscode-extension/` and ships pre-bundled with the npm package. + +## Install + +The fastest path is to package the extension from this checkout and install it locally with the VS Code CLI: + +```sh +npm run package:vscode-extension +npm run install:vscode-extension +``` + +This: + +1. Builds a `.vsix` at `build/vscode/xcode-canvas-web-vscode.vsix`. +2. Installs it via `code --install-extension build/vscode/xcode-canvas-web-vscode.vsix --force`. + +If the `code` command isn't on your `PATH`, install it from VS Code via **Shell Command: Install 'code' command in PATH** in the command palette. + +## Use it + +After installing, open the command palette and run: + +```text +Xcode Canvas Web: Open Simulator View +``` + +The extension opens a webview panel pointed at the configured server URL. If the server isn't running, the extension auto-launches it (see settings below). + +Two more commands round out the surface: + +- **Xcode Canvas Web: Stop Managed Server** — stops the server the extension started. +- **Xcode Canvas Web: Show Output** — opens the extension's output channel for debugging. + +## Settings + +All settings live under the `xcodeCanvasWeb.*` namespace in VS Code settings: + +| Setting | Default | Notes | +| -------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------- | +| `xcodeCanvasWeb.serverUrl` | `http://127.0.0.1:4310` | URL the webview loads. Change this to point at a remote SimDeck instance. | +| `xcodeCanvasWeb.cliPath` | _empty_ | Optional explicit path to the `xcode-canvas-web` CLI. Empty means: workspace `build/`, then `PATH`. | +| `xcodeCanvasWeb.port` | `4310` | Port used when auto-starting the server. | +| `xcodeCanvasWeb.bindAddress` | `127.0.0.1` | Bind address used when auto-starting the server. | +| `xcodeCanvasWeb.autoStartServer` | `true` | If `true`, the extension starts the local server when opening the simulator view if it isn't reachable. | + +## Resolving the CLI + +When the extension needs to start the server it looks in this order: + +1. The explicit `xcodeCanvasWeb.cliPath` setting. +2. The workspace's `build/xcode-canvas-web` (handy when developing on this repo). +3. `xcode-canvas-web` from `PATH`. + +If none of those resolve, the extension surfaces an error in the output channel pointing you at this documentation. + +## Talking to a remote server + +Set `xcodeCanvasWeb.serverUrl` to any reachable SimDeck endpoint. The extension is purely a webview shell — it doesn't validate the URL, doesn't open WebTransport itself, and doesn't ship its own version of the React client. Whatever the server responds with is what you get. + +For [LAN-reachable servers](/guide/lan-access), point the extension at `http://:` and disable `autoStartServer` so the extension doesn't try to spawn a local CLI. + +## Troubleshooting + +- **The webview shows a blank panel.** Open the output channel; the extension logs whatever HTTP error it saw when probing `/api/health`. +- **Auto-start doesn't work.** Ensure the resolved `cliPath` exists and is executable. The extension shows the resolved path in the output channel. +- **Stale managed server stays running.** Use **Xcode Canvas Web: Stop Managed Server** to terminate the server the extension spawned, then reopen the simulator view. diff --git a/docs/guide/architecture.md b/docs/guide/architecture.md new file mode 100644 index 00000000..f82f9daf --- /dev/null +++ b/docs/guide/architecture.md @@ -0,0 +1,124 @@ +# Architecture + +SimDeck is intentionally split into a small number of clearly-scoped layers. Every layer has a single concern and a single owner directory in the repo. + +## High-level layout + +SimDeck has three layers stacked between the browser and the iOS Simulator: + +1. **Browser / VS Code** runs the React client from `client/`. It speaks HTTP for control and WebTransport for live video, both served by the Rust server. +2. **The Rust server** (`server/`, built on `axum` + `tokio`) owns REST routes (`api/`), the WebTransport hub and packet codec (`transport/`), the inspector WebSocket hub (`inspector.rs`), the per-UDID session registry (`simulators/`), metrics, log streaming, and the `launchd` service installer. +3. **The Objective-C bridge** (`cli/`) is reached through a narrow C ABI in `cli/native/XCWNativeBridge.*`. It wraps `xcrun simctl`, the private `CoreSimulator` direct-boot path, the per-session HEVC/H.264 encoder, the headless display bridge that produces frames and accepts HID input, and the device-chrome renderer. + +Underneath all of that is the iOS Simulator itself — `CoreSimulator` for lifecycle, `SimulatorKit` for chrome assets. + +## Layer responsibilities + +### `server/` — Rust HTTP and WebTransport + +Owns the public CLI shape (`xcode-canvas-web serve`, `boot`, `shutdown`, …), the HTTP API, the WebTransport hub, the inspector hub, log streaming, and metrics. + +Key modules: + +| Module | Responsibility | +| -------------------------------------- | -------------------------------------------------------------------------------------- | +| `server/src/main.rs` | CLI entrypoint, AppKit main-thread shim, tokio runtime bootstrap. | +| `server/src/api/routes.rs` | Every `/api/*` route, including simulator control, accessibility, and inspector proxy. | +| `server/src/transport/webtransport.rs` | WebTransport server, per-session frame fanout, keyframe handshake. | +| `server/src/transport/packet.rs` | Binary video packet header (`PACKET_VERSION`, flags, layout). | +| `server/src/inspector.rs` | WebSocket hub for the NativeScript runtime inspector. | +| `server/src/simulators/registry.rs` | Per-UDID session registry with lazy attachment to the native bridge. | +| `server/src/simulators/session.rs` | Frame broadcast channel, keyframe gating, refresh requests. | +| `server/src/metrics/counters.rs` | Atomic counters and per-client stream stats accepted via `/api/client-stream-stats`. | +| `server/src/logs.rs` | `os_log` log streaming and filtering. | +| `server/src/service.rs` | `xcode-canvas-web service on/off` — generates and bootstraps a `LaunchAgents` plist. | + +The Rust server runs the tokio runtime on a worker thread while the AppKit main loop spins on the main thread. The native bridge needs the main loop to deliver display callbacks and HID events. + +### `cli/` — Objective-C native bridge + +Anything that depends on macOS frameworks, `xcrun simctl`, or private `CoreSimulator` / `SimulatorKit` APIs lives here. The Rust side talks to it through a narrow C ABI: + +- `cli/native/XCWNativeBridge.{h,m}` — exported C functions for simulator control, chrome rendering, and frame callbacks. +- `cli/native/XCWNativeSession.{h,m}` — wraps one Objective-C private simulator session handle for the Rust registry. + +Inside the bridge: + +- **`XCWSimctl.{h,m}`** wraps `xcrun simctl` for discovery, lifecycle management, app launching, URL opening, and screenshot capture. +- **`XCWPrivateSimulatorBooter.{h,m}`** uses private `CoreSimulator` APIs for direct simulator boot when available, with `simctl` as the fallback path. +- **`DFPrivateSimulatorDisplayBridge.{h,m}`** owns headless private display frames plus HID-based touch and keyboard injection. +- **`XCWPrivateSimulatorSession.{h,m}`** owns one private display bridge per booted simulator plus a selectable HEVC or H.264 encoder. +- **`XCWPrivateSimulatorChromeBridge.{h,m}`** is an experimental private `SimulatorKit` chrome bridge kept nearby as a reference. +- **`XCWChromeRenderer.{h,m}`** renders Apple's CoreSimulator device-type PDF chrome assets into PNGs for the browser. +- **`XCWH264Encoder.{h,m}`** software / hardware H.264 encode used as a fallback when HEVC is starved. + +### `client/` — React browser UI + +The React app served at `/` is a thin shell that calls the REST API and consumes binary video over WebTransport. + +Layout under `client/src/`: + +- `app/AppShell.tsx` — top-level shell. +- `api/` — typed wrappers around `/api/*` (`client.ts`, `controls.ts`, `simulators.ts`, `types.ts`). +- `features/stream/` — WebTransport reader, decoder workers, frame renderer. +- `features/viewport/` — frame canvas, hit testing, chrome compositing. +- `features/input/` — touch/keyboard/hardware button affordances. +- `features/accessibility/` — accessibility tree pane and source switcher. +- `features/simulators/` — simulator list, boot/shutdown affordances. +- `features/toolbar/` — top toolbar (rotate, home, app switcher, dark mode toggle). +- `workers/` — video decode workers. + +The client never depends on private APIs and never assumes anything not exposed by the HTTP API. + +### `packages/` — companion packages + +- **`packages/nativescript-inspector/`** ships `@nativescript/xcode-canvas-inspector`, a TypeScript runtime that connects from a NativeScript app to the server's WebSocket inspector hub. See [NativeScript Runtime](/inspector/nativescript). +- **`packages/inspector-agent/`** ships `XcodeCanvasInspectorAgent`, a Swift Package you can link from a debug iOS app to expose its UIKit hierarchy. See [Swift In-App Agent](/inspector/swift). +- **`packages/vscode-extension/`** is the VS Code extension that opens the browser client inside a webview panel and auto-starts the server. + +## Data flow + +### Simulator control + +Most control endpoints follow the same path: a typed Rust handler in `server/src/api/routes.rs` calls `SessionRegistry::bridge()`, which dispatches into `cli/native/XCWNativeBridge.*` over the C ABI. From there the call lands in the matching Objective-C unit — for example, `POST /api/simulators/{udid}/boot` ends up in `XCWPrivateSimulatorBooter`, which uses private `CoreSimulator` APIs for direct boot and falls back to `simctl` if that fails. + +### Live video + +The browser opens a WebTransport session at `https://host:4311/wt/simulators/{udid}`. The handler in `transport::webtransport::handle_session` ensures the per-UDID `SimulatorSession` is started, waits up to ~3 s for the first keyframe, then opens two unidirectional streams to the client: a control stream that carries a single JSON `ControlHello` describing the codec, and a video stream that carries binary frame packets fanned out from `SimulatorSession.subscribe()`. + +Each binary packet has a fixed-size 36-byte header followed by an optional codec configuration (description) blob and the encoded video data. See [WebTransport](/api/webtransport) and [Packet Format](/api/packet-format) for the wire layout. + +### Input + +Touch and keyboard events POST to `/api/simulators/{udid}/touch` and `/key`. The handler resolves the active session and replays the event through the private display bridge using HID. + +### Inspectors + +The accessibility tree endpoint blends three sources, in priority order: + +1. **NativeScript runtime inspector** — preferred when the foreground app has connected to `/api/inspector/connect` over WebSocket. +2. **Swift in-app inspector agent** — used when the foreground app links the `XcodeCanvasInspectorAgent` Swift Package and listens on a TCP port discovered between `47370` and `47402`. +3. **AXe accessibility snapshot** — a final fallback that shells out to the `axe describe-ui` CLI. Always available as long as `axe` is on `PATH`. + +The server discovers which inspectors are reachable for a given Simulator and surfaces the available list in the `availableSources` field on every accessibility-tree response. + +## Process model + +SimDeck stays in one OS process. The Rust binary: + +1. Calls `xcw_native_initialize_app()` so AppKit creates an `NSApplication` on the main thread. +2. Spawns a tokio runtime on a worker thread that owns the HTTP server, WebTransport server, inspector hub, and registry. +3. Spins the AppKit main loop in 50 ms slices on the main thread to dispatch display and HID callbacks. + +`launchctl bootstrap` is the only sub-process SimDeck launches itself, and only when you opt into the [background service](/guide/service). + +## Working rules + +If you contribute, keep the following invariants in mind: + +- Simulator-native logic stays in Objective-C under `cli/`. +- Rust server logic stays under `server/`. +- Browser-only presentation logic stays in `client/`. +- NativeScript app runtime inspection logic stays in `packages/nativescript-inspector/`. +- Add a server endpoint before adding client-only assumptions. +- The supported live video path is WebTransport-only — do not bring back legacy `/stream.h264` handling. diff --git a/docs/guide/index.md b/docs/guide/index.md new file mode 100644 index 00000000..1f069b03 --- /dev/null +++ b/docs/guide/index.md @@ -0,0 +1,51 @@ +# Introduction + +SimDeck is a local-first control plane for the iOS Simulator. It bundles a Rust HTTP server, a native Objective-C bridge for private CoreSimulator and SimulatorKit APIs, and a React client into a single CLI you can run on any modern Mac. + +The goal is simple: turn a booted Simulator into a streamable, scriptable surface that any tool — a browser, VS Code, a NativeScript runtime, an automation script — can drive over plain HTTP. + +## Why SimDeck? + +The default Simulator is great when it sits in front of you. It is much less great when: + +- You want to see your app running while writing code in another window. +- You want to drive a Simulator from a remote machine on your LAN. +- You want to build automation around `simctl` without stitching together shell pipelines. +- You want to inspect a NativeScript or Swift app's view hierarchy without linking the Xcode debugger. +- You want a single, predictable URL that always points at "the Simulator on this Mac". + +SimDeck addresses all of those by exposing one HTTP server, one WebTransport endpoint, and one CLI binary. + +## What's in the box + +SimDeck ships as a single npm package (`xcode-canvas-web`, soon to be renamed) that installs three things: + +1. **A native CLI server.** Rust + Objective-C, compiled on install. It serves the HTTP API and a self-signed WebTransport endpoint for live video frames. +2. **A bundled React client.** Talks to the local server, renders a streamable Simulator surface, and ships the inspector UIs. +3. **A `launchd` integration.** A single `service on` flag installs a per-user background service that survives logout and restart. + +Optional companion packages: + +- [`@nativescript/xcode-canvas-inspector`](/inspector/nativescript) — a debug-only NativeScript inspector runtime. +- [`packages/inspector-agent`](/inspector/swift) — a Swift Package you can link from your iOS app to expose its UIKit hierarchy. +- [`packages/vscode-extension`](/extensions/vscode) — opens the simulator inside a VS Code panel. + +## High-level architecture + +The repository splits cleanly along the layers SimDeck talks to: + +- **`server/`** holds the Rust HTTP server, WebTransport hub, inspector hub, and metrics. It serves the REST API at `/api/*`, live video at `/wt/simulators/{udid}`, and the inspector WebSocket at `/api/inspector/connect`. +- **`cli/`** holds the Objective-C native bridge that links private `CoreSimulator` and `SimulatorKit` APIs. The Rust server calls into it through a narrow C ABI for boot, frame capture, encode, and HID input. +- **`client/`** holds the React UI that renders the streamed simulator and the inspector tools. +- **`packages/`** holds optional companion packages (NativeScript inspector, Swift inspector agent, VS Code extension). +- **`scripts/`** holds repeatable build entrypoints used both locally and by CI. + +For a full breakdown, see [Architecture](/guide/architecture). + +## Where to next + +- **[Installation](/guide/installation)** — how to install the CLI from npm or build it from source. +- **[Quick Start](/guide/quick-start)** — boot a simulator and stream it to your browser in under a minute. +- **[Architecture](/guide/architecture)** — the layout, data flow, and private-API surface. +- **[CLI Reference](/cli/commands)** — every command with its flags. +- **[HTTP API](/api/rest)** — every REST endpoint, with response shapes. diff --git a/docs/guide/installation.md b/docs/guide/installation.md new file mode 100644 index 00000000..a16744b6 --- /dev/null +++ b/docs/guide/installation.md @@ -0,0 +1,117 @@ +# Installation + +SimDeck ships as a single npm package that contains the launcher, the client bundle, and a postinstall hook that compiles the native CLI on macOS. + +## Prerequisites + +SimDeck only runs on macOS. The native bridge links private `CoreSimulator` and `SimulatorKit` frameworks, so it cannot run on Linux or Windows. + +| Requirement | Why | +| ---------------------------------- | ------------------------------------------------------------------------------------ | +| **macOS 13+** | Required for current `CoreSimulator` and Apple's HEVC hardware encoder. | +| **Xcode + iOS Simulator runtimes** | The native bridge invokes `xcrun simctl` and the Simulator app. | +| **Node.js ≥ 18** | The launcher (`bin/xcode-canvas-web.mjs`) and the bundled client tooling. | +| **Rust (stable)** | Required only when building from source. Installed via [rustup](https://rustup.rs/). | + +The package is published as `darwin`-only via the `os` field, so `npm install` on Linux will succeed but skip the native build with a warning. + +## Install from npm + +The fastest path is the published CLI: + +```sh +npm install -g xcode-canvas-web +``` + +This installs the launcher to your global `node_modules` and runs the native build automatically. After it finishes: + +```sh +xcode-canvas-web --help +``` + +::: tip Future package name +SimDeck is being renamed. The npm package will move from `xcode-canvas-web` to a new name once the rename lands; the install command will switch to: + +```sh +npm install -g simdeck +``` + +Until then, the documentation refers to the product as **SimDeck** and the package/binary as `xcode-canvas-web`. +::: + +## Install from source + +Clone the repo and install dependencies: + +```sh +git clone https://github.com/DjDeveloperr/xcode-canvas-web.git +cd xcode-canvas-web +npm install +``` + +The root `npm install` triggers `scripts/npm-postinstall.mjs`, which compiles the native CLI in release mode via `scripts/build-cli.sh`. When it finishes, the binary lives at: + +```text +build/xcode-canvas-web +build/xcode-canvas-web-bin +``` + +You can then run the local checkout directly: + +```sh +./build/xcode-canvas-web serve --port 4310 +``` + +Or install the local checkout globally: + +```sh +npm install -g . +``` + +After a global install you can call `xcode-canvas-web` from anywhere. + +## Build the React client + +The client bundle ships pre-built when installed from npm. When working from source, build it explicitly: + +```sh +./scripts/build-client.sh +``` + +This calls `npm install` and `npm run build` inside the `client/` workspace and writes the production bundle to `client/dist`. The Rust server serves that bundle at the HTTP root. + +## Build everything + +The root `package.json` exposes a one-shot build that compiles every component: + +```sh +npm run build +``` + +This runs: + +- `npm run build:cli` — Rust server + Objective-C bridge → `build/xcode-canvas-web` +- `npm run build:client` — Vite production build → `client/dist` +- `npm run build:nativescript-inspector` — TypeScript build of the NativeScript inspector + +You can also run any one of those scripts on its own. + +## Update or uninstall + +To update from npm: + +```sh +npm install -g xcode-canvas-web@latest +``` + +To remove the global install: + +```sh +npm uninstall -g xcode-canvas-web +``` + +If you enabled the [background service](/guide/service), disable it first so launchd does not restart a deleted binary: + +```sh +xcode-canvas-web service off +``` diff --git a/docs/guide/lan-access.md b/docs/guide/lan-access.md new file mode 100644 index 00000000..662cb7e8 --- /dev/null +++ b/docs/guide/lan-access.md @@ -0,0 +1,92 @@ +# LAN Access + +SimDeck binds to `127.0.0.1` by default. You can move it to a LAN-reachable interface so other devices on your network — another Mac, an iPad, a phone — can stream the simulator. + +## Bind to all interfaces + +Use `--bind` to listen on a non-loopback address: + +```sh +xcode-canvas-web serve --port 4310 --bind 0.0.0.0 +``` + +Both the HTTP server and the WebTransport server bind to the requested address. The HTTP server is plain HTTP, so any browser on the LAN can reach it through `http://:4310`. + +## Advertise the right host + +WebTransport needs a hostname or IP that matches the certificate the server generates. By default SimDeck advertises `localhost`, which only works for browsers running on the same Mac. + +Tell the server what host to advertise to remote clients: + +```sh +xcode-canvas-web serve \ + --port 4310 \ + --bind 0.0.0.0 \ + --advertise-host 192.168.1.50 +``` + +The advertised host shows up in three places: + +- The `webTransport.urlTemplate` field on `GET /api/health`. +- The Subject Alternative Name list on the self-signed WebTransport certificate. +- The certificate hash that the client pins by SHA-256. + +If you skip `--advertise-host` while binding to `0.0.0.0`, the server prints a warning at startup because it will still tell remote clients to dial `localhost`. + +## Pick a hostname or IP + +You can advertise either a DNS name (preferred when you have a stable mDNS or DHCP entry) or an IP literal. Examples: + +```sh +xcode-canvas-web serve --bind 0.0.0.0 --advertise-host my-mac.lan +xcode-canvas-web serve --bind 0.0.0.0 --advertise-host 192.168.1.50 +xcode-canvas-web serve --bind :: --advertise-host my-mac.local +``` + +Whatever you advertise must be resolvable from the remote client. + +## Certificate handling + +The server generates a fresh self-signed certificate every time it starts. The certificate's SHA-256 hash is exposed on `GET /api/health`: + +```json +{ + "ok": true, + "httpPort": 4310, + "wtPort": 4311, + "videoCodec": "hevc", + "webTransport": { + "urlTemplate": "https://192.168.1.50:4311/wt/simulators/{udid}", + "certificateHash": { + "algorithm": "sha-256", + "value": "..." + }, + "packetVersion": 1 + } +} +``` + +The browser client passes that hash to the WebTransport API as the `serverCertificateHashes` option, so no certificate trust prompts are involved. As long as the client fetched the hash through HTTP and dialled the same advertised host, the WebTransport handshake completes without any user interaction. + +Restarting the server invalidates the previous certificate. Open clients reconnect automatically as soon as `/api/health` reports the new hash. + +## Authentication and security + +SimDeck assumes a trusted local network. The HTTP API has no built-in authentication and the WebTransport endpoint accepts any client that knows the URL. + +Recommended practice for shared networks: + +- Run SimDeck only on networks you control. +- Combine with macOS Application Firewall to restrict inbound access to known peers. +- For shared NativeScript inspectors, set an `authToken` when starting the [Swift in-app agent](/inspector/swift#auth-token) so app-side requests must include the token. + +A roadmap item is to add an optional bearer-token header for the HTTP API and WebTransport `?token=` parameter. Until that lands, treat SimDeck like any unauthenticated dev tool. + +## Quick checklist + +To make a SimDeck server reachable from another device: + +1. `--bind 0.0.0.0` (or `--bind ::`). +2. `--advertise-host `. +3. Allow the chosen ports through any firewalls. +4. Visit `http://:` from the remote device. diff --git a/docs/guide/quick-start.md b/docs/guide/quick-start.md new file mode 100644 index 00000000..eabe34e5 --- /dev/null +++ b/docs/guide/quick-start.md @@ -0,0 +1,74 @@ +# Quick Start + +This guide walks you from a fresh install to a Simulator streaming in your browser in three steps. + +## 1. Start the server + +After [installing](/guide/installation), launch the local server: + +```sh +xcode-canvas-web serve --port 4310 +``` + +The server prints the HTTP and WebTransport URLs as it boots: + +```text +HTTP listening on http://127.0.0.1:4310 +WebTransport listening on https://127.0.0.1:4311/wt/simulators/{udid} +Serving client from /usr/local/lib/node_modules/xcode-canvas-web/client/dist +``` + +Two listeners come up: + +- **HTTP** on `--port` (default `4310`) for the REST API and the static React client. +- **WebTransport** on `port + 1` (default `4311`) for binary video frames. The server generates a self-signed certificate per session and advertises its hash through `GET /api/health`. + +Leave the server running in a terminal, or move it into the [background service](/guide/service). + +## 2. Open the client + +Visit: + +```text +http://127.0.0.1:4310 +``` + +The React client connects to `GET /api/health` to discover the WebTransport URL template and certificate hash, lists every Simulator on the machine, and lets you boot, stream, and interact with one. + +::: tip First-frame delay +On a cold boot the server has to launch the Simulator, attach the private display bridge, and wait for a keyframe before any video flows. The first frame typically shows up within a second; subsequent reloads of the same Simulator are near-instant. +::: + +## 3. Boot a simulator from the CLI + +You can drive simulators directly from the command line in addition to the browser: + +```sh +xcode-canvas-web list +xcode-canvas-web boot +xcode-canvas-web open-url https://example.com +xcode-canvas-web launch com.apple.Preferences +xcode-canvas-web shutdown +``` + +`list` returns the same data the React client renders — including which simulators are booted and which have an attached private display session. + +See the full [CLI Reference](/cli/commands) for every command and flag. + +## What just happened? + +When you opened the browser: + +1. The browser fetched `/api/health` and learned where the WebTransport endpoint lives. +2. It opened a WebTransport session at `wss://127.0.0.1:4311/wt/simulators/` and pinned the self-signed certificate by hash. +3. The Rust transport hub asked the native bridge for a fresh keyframe and started forwarding binary video packets. +4. Touch and keyboard events round-trip through `POST /api/simulators//touch` and `/key`, which the native bridge replays through HID. + +You can read more about the layers involved in [Architecture](/guide/architecture) and [WebTransport](/api/webtransport). + +## Common follow-ups + +- **Run the server on a LAN-reachable port.** See [LAN Access](/guide/lan-access). +- **Keep the server running across logouts.** See [Background Service](/guide/service). +- **Stream is choppy or stuck on a black frame.** See [Video Pipeline](/guide/video) and [Troubleshooting](/guide/troubleshooting). +- **Embed the client in VS Code.** See the [VS Code extension](/extensions/vscode). diff --git a/docs/guide/service.md b/docs/guide/service.md new file mode 100644 index 00000000..1a316464 --- /dev/null +++ b/docs/guide/service.md @@ -0,0 +1,118 @@ +# Background Service + +SimDeck can install itself as a per-user `launchd` service so the server starts automatically at login and survives terminal closures, crashes, and reboots. + +The service runs under your user account, not as `root`. It can therefore reach private CoreSimulator APIs and the iOS Simulator just like an interactive shell session would. + +## Enable + +Turn the service on with the same flags you would pass to `serve`: + +```sh +xcode-canvas-web service on --port 4310 +``` + +You can pass any of: + +| Flag | Default | Notes | +| ------------------ | --------------------- | ------------------------------------------------------------------ | +| `--port ` | `4310` | HTTP port. WebTransport listens on `port + 1`. | +| `--bind ` | `127.0.0.1` | Bind address (use `0.0.0.0` for [LAN access](/guide/lan-access)). | +| `--advertise-host` | matches `--bind` | Hostname advertised to remote clients. | +| `--client-root` | bundled `client/dist` | Override the static client directory. | +| `--video-codec` | `hevc` | One of `hevc`, `h264`, `h264-software`. See [Video](/guide/video). | + +The command: + +1. Resolves the absolute path to the current `xcode-canvas-web` binary. +2. Writes a launchd plist to `~/Library/LaunchAgents/dev.nativescript.xcode-canvas-web.plist`. +3. Bootstraps the service into `gui/` and immediately kickstarts it. +4. Prints the resulting paths as JSON, including the stdout and stderr log paths under `~/Library/Logs/`. + +After the command finishes you can close the terminal — the service keeps running. + +## Inspect + +The plist lives at: + +```text +~/Library/LaunchAgents/dev.nativescript.xcode-canvas-web.plist +``` + +Logs land at: + +```text +~/Library/Logs/xcode-canvas-web.log +~/Library/Logs/xcode-canvas-web.err.log +``` + +You can verify the service is up with `launchctl`: + +```sh +launchctl print "gui/$(id -u)/dev.nativescript.xcode-canvas-web" +``` + +Or just by hitting the health endpoint: + +```sh +curl http://127.0.0.1:4310/api/health +``` + +## Disable + +Stop and remove the service: + +```sh +xcode-canvas-web service off +``` + +This: + +1. Calls `launchctl bootout` for the service in your `gui/` domain. +2. Removes the plist from `~/Library/LaunchAgents`. +3. Prints the removed plist path as JSON. + +Logs under `~/Library/Logs` are kept so you can review past output. + +## Updating the service + +When you install a new version of SimDeck, the launchd service still points at the old binary path until you re-bootstrap it: + +```sh +xcode-canvas-web service off +xcode-canvas-web service on --port 4310 +``` + +Re-run with the same flags you used originally. The plist is regenerated against the new binary. + +## What's in the plist + +The generated plist mirrors the CLI invocation you used: + +```xml +Label +dev.nativescript.xcode-canvas-web +ProgramArguments + + /usr/local/bin/xcode-canvas-web + serve + --port + 4310 + --bind + 127.0.0.1 + --client-root + /usr/local/lib/node_modules/xcode-canvas-web/client/dist + --video-codec + hevc + +RunAtLoad + +KeepAlive + +StandardOutPath +/Users/you/Library/Logs/xcode-canvas-web.log +StandardErrorPath +/Users/you/Library/Logs/xcode-canvas-web.err.log +``` + +You can hand-edit the plist if needed, but the easier path is to re-run `service on` with new flags. diff --git a/docs/guide/troubleshooting.md b/docs/guide/troubleshooting.md new file mode 100644 index 00000000..2214dad9 --- /dev/null +++ b/docs/guide/troubleshooting.md @@ -0,0 +1,124 @@ +# Troubleshooting + +Most SimDeck issues fall into one of three buckets: simulator boot, video stream, or accessibility/inspector. This page lists the symptoms and fixes for the ones we hit most often. + +## Server won't start + +### `bind HTTP listener on 127.0.0.1:4310` + +Another process already owns the HTTP port. Pick a different one: + +```sh +xcode-canvas-web serve --port 4320 +``` + +Or find what's holding it: + +```sh +lsof -nP -iTCP:4310 -sTCP:LISTEN +``` + +If the holder is an old SimDeck instance, the bundled `npm run dev` script auto-kills stale listeners on `4310` and `4311` for you. + +### `xcode-canvas-web is not built yet` + +The launcher script could not find the compiled binary. Reinstall the package or run the local build: + +```sh +npm install -g xcode-canvas-web +# or, from a checkout: +./scripts/build-cli.sh +``` + +### Native build fails on install + +The postinstall hook runs `cargo build --release` and Apple's Clang against the Objective-C bridge. The most common failures are: + +- **Rust missing.** Install via [rustup](https://rustup.rs/), then reinstall. +- **Xcode command-line tools missing.** Run `xcode-select --install`. +- **Sandboxed CI without macOS frameworks.** Postinstall warns and exits cleanly on non-Darwin platforms; the binary just isn't installed. + +## Simulator never boots + +### `xcrun simctl` errors + +The native bridge falls back to `xcrun simctl boot` when private CoreSimulator APIs are unavailable. Try the same command directly to surface the underlying error: + +```sh +xcrun simctl boot +``` + +If `simctl` succeeds but SimDeck still fails, capture the server log and file an issue. + +### CoreSimulator service unhealthy + +If `simctl list` itself hangs or returns garbage, the macOS `com.apple.CoreSimulator.CoreSimulatorService` is wedged. Restart it: + +```sh +killall -9 com.apple.CoreSimulator.CoreSimulatorService +``` + +CoreSimulator restarts on demand. Re-run `simctl list` to confirm before retrying. + +### Multiple Xcode installs + +When more than one Xcode is installed, `xcrun simctl` uses whichever Xcode is selected by `xcode-select`. Pick the one whose runtimes you care about: + +```sh +sudo xcode-select -s /Applications/Xcode.app +``` + +## Stream is black or stuck + +### "Timed out waiting for initial simulator keyframe" + +The encoder did not produce a keyframe within 3 seconds. The most common causes: + +- **VideoToolbox is busy.** macOS screen recording can starve the HEVC encoder. Switch to software H.264: + + ```sh + xcode-canvas-web serve --port 4310 --video-codec h264-software + ``` + +- **The Simulator window is minimised or off-screen.** The private display bridge captures from a headless context, so this is rare, but if you see it after waking from sleep, shut the simulator down and boot it again. +- **The simulator is mid-shutdown.** Wait for `xcode-canvas-web list` to report `isBooted: true`. + +### Frequent stutter or "Refresh stream" loops + +The transport hub forces a keyframe whenever a client falls behind. If `frames_dropped_server` on `/api/metrics` climbs steadily, the bottleneck is between the encoder and the decoder. + +- Bring the client closer (LAN with low latency vs Wi-Fi mesh hops). +- Switch to `h264` instead of `hevc` if the client decoder is slow. +- Check `client_streams` in `/api/metrics`. If `decodedFps` is much lower than `packetFps`, the client decoder is the bottleneck. + +## Inspector returns AXe instead of NativeScript / UIKit + +The accessibility tree endpoint blends three inspector sources and falls back to AXe when none of the others are reachable. The response includes both a `source` field and a `fallbackReason` field that explains what happened. + +Common reasons: + +| `fallbackReason` | Fix | +| ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | +| `The in-app inspector process is not the foreground app.` | Bring the inspector-enabled app to the foreground. | +| `NativeScript hierarchy is not published by the app.` | Make sure the app calls `startXcodeCanvasInspector(...)` before bootstrapping. | +| `No connected NativeScript inspector ...` | The NativeScript inspector hasn't completed its WebSocket handshake yet. Reload the app. | +| `No in-app inspector found ... on ports 47370-47402` | The Swift agent isn't listening; confirm the app links and starts the agent in DEBUG. | +| `Unable to run \`axe describe-ui\`. Install AXe or ensure on PATH.` | Install AXe; the fallback can't run without it. | + +For more on the inspector matrix, see the [Inspector Overview](/inspector/). + +## NativeScript inspector won't connect + +- Confirm `startXcodeCanvasInspector({ port: 4310 })` runs in the simulator app's main thread before bootstrap. +- Confirm the simulator can reach the host: from inside the app, `fetch('http://127.0.0.1:4310/api/health')` should succeed. +- For Angular apps, make sure `startXcodeCanvasInspector(...)` runs **before** `runNativeScriptAngularApp(...)`. +- Watch the server log for messages such as `Registered NativeScript inspector for process …`. If you don't see one, the WebSocket never completed. + +## Logs + +When all else fails, capture the server log: + +- Foreground server: redirect output to a file. +- Background service: read `~/Library/Logs/xcode-canvas-web.log` and `~/Library/Logs/xcode-canvas-web.err.log`. + +Include both files when filing an issue, along with `xcode-canvas-web --version` (when implemented), the macOS version, and the Xcode version. diff --git a/docs/guide/video.md b/docs/guide/video.md new file mode 100644 index 00000000..cac2123c --- /dev/null +++ b/docs/guide/video.md @@ -0,0 +1,109 @@ +# Video Pipeline + +SimDeck streams the iOS Simulator over WebTransport using a binary frame protocol. This page walks through the encoder choices, the keyframe handshake, and the metrics you can use to tune them. + +## Codec selection + +The server can encode the simulator display in three modes, picked at startup with `--video-codec`: + +| Value | Encoder | When to use it | +| ------------------ | ------------------------------------------- | --------------------------------------------------------------------------- | +| `hevc` _(default)_ | Hardware HEVC via VideoToolbox | Best quality and bandwidth on modern Apple Silicon. The default everywhere. | +| `h264` | Hardware H.264 via VideoToolbox | Falls back if a downstream client cannot decode HEVC. | +| `h264-software` | Software H.264 (libavcodec / openh264 path) | Use when macOS screen recording starves the hardware encoder. | + +You can switch at any time by restarting the server with a different flag: + +```sh +xcode-canvas-web serve --port 4310 --video-codec h264-software +``` + +The chosen codec is reported to clients in two places: + +- The JSON `videoCodec` field on `GET /api/health`. +- The `codec` field of the [`ControlHello`](/api/webtransport#control-hello) message that the WebTransport hub sends as soon as a session attaches. + +## Keyframe handshake + +When a browser opens a WebTransport session at `/wt/simulators/{udid}`: + +1. The server ensures the `SimulatorSession` is started and asks the encoder for an immediate refresh. +2. It waits up to 3 seconds for the next keyframe. +3. As soon as a keyframe arrives, it writes a JSON `ControlHello` on a control unidirectional stream and the keyframe itself on a video unidirectional stream. +4. Subsequent frames stream until the client disconnects. + +If the encoder cannot deliver a keyframe within 3 seconds, the server tears the session down with a clear error so the client can retry. This usually happens only when CoreSimulator is itself stuck. + +## Drop and lag handling + +The transport hub uses a tokio broadcast channel to fan out frames. If a slow client misses frames the hub: + +1. Increments `frames_dropped_server` on the metrics counter. +2. Sets a "waiting for keyframe" flag and skips non-keyframes until a fresh one arrives. +3. Calls `request_refresh()` on the session so the encoder forces a keyframe. + +Discontinuities are signalled to the client through the `FLAG_DISCONTINUITY` bit in the packet header. The client reacts by flushing its decoder queue and waiting for the next keyframe. + +## Picking a codec + +A few practical guidelines: + +- **Start on the default.** HEVC delivers the best quality-per-bit and the lowest CPU on M-series Macs. +- **Switch to `h264` when a remote client cannot decode HEVC.** Some browsers on older Apple devices are H.264-only. +- **Switch to `h264-software` when the hardware encoder stalls.** macOS screen recording can monopolise the VideoToolbox HEVC encoder. If you see "encoder unavailable" errors in the server log while QuickTime or `screencapture` is active, switch to `h264-software`. + +## Tuning with metrics + +`GET /api/metrics` returns a snapshot of every counter the server keeps: + +```json +{ + "frames_encoded": 12039, + "keyframes_encoded": 17, + "frames_sent": 11982, + "frames_dropped_server": 21, + "keyframe_requests": 4, + "active_streams": 1, + "subscribers_connected": 3, + "subscribers_disconnected": 2, + "max_send_queue_depth": 1, + "latest_first_frame_ms": 412 +} +``` + +Useful signals: + +| Counter | What to look at | +| ----------------------- | --------------------------------------------------------------------------------- | +| `latest_first_frame_ms` | First-frame latency for the most recent connect. Should be a few hundred ms. | +| `frames_dropped_server` | If this climbs while a stream is open, the client cannot keep up. | +| `keyframe_requests` | Goes up every time the server forces a refresh. Frequent spikes mean rough seeks. | +| `active_streams` | Number of WebTransport sessions currently subscribed. | + +Clients can also push their decoder/renderer stats back to the server: + +```http +POST /api/client-stream-stats +Content-Type: application/json + +{ + "clientId": "browser-ABC", + "kind": "viewport", + "codec": "hevc", + "decodedFps": 59.7, + "droppedFps": 0.1, + "latestRenderMs": 6.2 +} +``` + +The server keeps the last 48 entries per `(clientId, kind)` pair and returns them from `GET /api/client-stream-stats`. The browser client uses these to render the in-app diagnostics overlay. + +## Refreshing a stuck stream + +If a client suspects it has fallen too far behind, it can call: + +```http +POST /api/simulators/{udid}/refresh +``` + +The server starts the session if needed and asks the encoder to emit a keyframe immediately. The browser client wires this to a "Refresh stream" affordance in its toolbar. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..f0c76e31 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,72 @@ +--- +layout: home + +hero: + name: SimDeck + text: Local iOS Simulator control plane + tagline: A Rust server, native bridge, and browser client that turn the iOS Simulator into a streamable, scriptable surface for everything from VS Code to NativeScript apps. + actions: + - theme: brand + text: Get Started + link: /guide/quick-start + - theme: alt + text: Why SimDeck? + link: /guide/ + - theme: alt + text: View on GitHub + link: https://github.com/DjDeveloperr/xcode-canvas-web + +features: + - icon: + src: /icons/monitor-smartphone.svg + width: 28 + height: 28 + title: Browser-first simulator + details: A React UI streams a live iOS Simulator over WebTransport with full touch, keyboard, hardware-button, and rotation input. No screen recording, no Xcode window required. + - icon: + src: /icons/zap.svg + width: 28 + height: 28 + title: Native macOS performance + details: A Rust HTTP server fronts an Objective-C bridge that talks to private CoreSimulator and SimulatorKit APIs for direct boot, headless display capture, and HEVC or H.264 encode. + - icon: + src: /icons/network.svg + width: 28 + height: 28 + title: Stable HTTP control plane + details: One server exposes simulator lifecycle, input, accessibility, logs, and inspector control through a single REST API on a predictable port. + - icon: + src: /icons/scan-search.svg + width: 28 + height: 28 + title: First-class inspectors + details: Choose between AXe accessibility snapshots, the Swift in-app inspector agent, or the NativeScript runtime inspector — SimDeck routes the right one for each request. + - icon: + src: /icons/puzzle.svg + width: 28 + height: 28 + title: Built-in extensions + details: A VS Code extension opens the simulator inside the editor, and a launchd service keeps the server running across logins. + - icon: + src: /icons/shield-check.svg + width: 28 + height: 28 + title: Local-first by default + details: Binds to 127.0.0.1, runs without a cloud account, and exposes a self-signed WebTransport endpoint that only your browser uses. +--- + +
+ +## What you can do with SimDeck + +SimDeck packages a full simulator workflow into one cross-tool surface: + +- **Stream a Simulator into a browser tab.** No more juggling Xcode windows or screen recordings. +- **Drive Simulators from JavaScript.** A REST API plus the NativeScript inspector turn any iOS app into a programmable target. +- **Embed a Simulator in your editor.** The bundled VS Code extension opens the same surface inside a panel. +- **Run Simulators on your LAN.** Bind to `0.0.0.0`, advertise a host, and connect from any other Mac, iPad, or laptop on the network. +- **Replace ad-hoc `simctl` scripts.** A single CLI handles `boot`, `shutdown`, `open-url`, `launch`, and a managed background service. + +Read [Architecture](/guide/architecture) for a deeper tour, or jump straight into [Quick Start](/guide/quick-start). + +
diff --git a/docs/inspector/accessibility.md b/docs/inspector/accessibility.md new file mode 100644 index 00000000..2b155582 --- /dev/null +++ b/docs/inspector/accessibility.md @@ -0,0 +1,65 @@ +# Accessibility (AXe) + +SimDeck's universal fallback inspector uses [AXe](https://github.com/cameroncooke/AXe), an open-source CLI that wraps the iOS Simulator accessibility APIs. It works against any simulator app — no SDK linking required — but only reports what the system accessibility stack exposes. + +## Install AXe + +Follow the AXe project's install instructions. The SimDeck server expects an `axe` binary on `PATH`: + +```sh +which axe +axe --help +``` + +If `axe` isn't installed, `GET /api/simulators/{udid}/accessibility-tree` falls back to whichever in-app inspector is reachable. With no in-app inspector connected either, the request fails with a clear error. + +## What it does + +The server shells out to: + +```sh +axe describe-ui --udid +``` + +…and parses the JSON response. The result is normalised into the same `roots[]` shape as the in-app inspectors, with `"source": "axe"`. + +For point queries: + +```sh +axe describe-ui --udid --point , +``` + +…which the browser client uses for the "describe element under cursor" affordance. + +## Coverage + +AXe reports anything UIKit publishes through the accessibility tree: + +- `AXLabel`, `AXIdentifier`, `AXValue`, `help` (hint). +- `bounds` and `frameInScreen` in UIKit screen points. +- Role, sub-role, and the small set of custom accessibility actions a view exposes. + +It does **not** see: + +- SwiftUI value-tree internals. +- NativeScript logical tree nodes. +- UIView properties that aren't part of the accessibility surface. + +For those, you need to link the [Swift in-app agent](/inspector/swift) or use the [NativeScript runtime inspector](/inspector/nativescript). + +## When AXe is the right call + +- You're inspecting an app you do not control (a system app, a third-party binary). +- You only need to find an element by label or ID and tap it. +- You're building accessibility QA workflows where the accessibility surface is the actual surface you care about. + +## Limitations and gotchas + +- **AXe is a separate process.** Each call spawns `axe` with an 8 second timeout. Repeated calls incur per-process startup cost. +- **Foreground app only.** Like the iOS accessibility stack, AXe sees the foreground app at the time of the call. If you switch apps mid-call, the snapshot may straddle two processes. +- **Coordinates are in UIKit points.** Multiply by `displayScale` (from the inspector metadata or the simulator's device profile) when correlating with pixel-space frames. +- **Errors surface as plain text.** Failures are returned as JSON `{"error":{"message":"..."}}` from the SimDeck endpoint with HTTP `500`. The original AXe stderr is included verbatim. + +## Combining with in-app inspectors + +When both AXe and an in-app inspector are available, the response includes both in `availableSources`. The browser client offers a source selector so you can compare the trees side by side. This is especially useful when you suspect a NativeScript element is not making it into the accessibility tree the way you expect. diff --git a/docs/inspector/index.md b/docs/inspector/index.md new file mode 100644 index 00000000..16f89bb0 --- /dev/null +++ b/docs/inspector/index.md @@ -0,0 +1,57 @@ +# Inspector Overview + +SimDeck blends three different ways to inspect what an iOS app is rendering: + +| Source | Coverage | When to use it | +| ------------------------ | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| **AXe** | Any simulator app via the system accessibility stack. | Default fallback. Always available if [AXe](https://github.com/cameroncooke/AXe) is on `PATH`. | +| **Swift in-app agent** | Apps that link `XcodeCanvasInspectorAgent` in DEBUG. | Best for native iOS apps you control. | +| **NativeScript runtime** | NativeScript apps that import `@nativescript/xcode-canvas-inspector`. | Best for NativeScript apps — exposes the logical view tree, not just UIKit. | + +The HTTP API picks the most specific source available, falls back to the next one when something goes wrong, and tells the client which sources were available so the UI can offer a switch. + +## How the server picks a source + +`GET /api/simulators/{udid}/accessibility-tree` accepts a `source` query parameter: + +| `source` | Behaviour | +| ---------------------------- | ---------------------------------------------------------------------------------------------------- | +| `auto` _(default)_ / unset | Use the most accurate source available, falling back to AXe. | +| `nativescript` / `ns` | Force the NativeScript logical tree if a NativeScript inspector is connected for the foreground app. | +| `uikit` / `in-app-inspector` | Force the raw UIKit hierarchy from any in-app inspector (NativeScript or Swift agent). | +| `axe` | Always use the AXe accessibility snapshot. | + +The server resolves which inspector belongs to the simulator by: + +1. Checking every connected NativeScript inspector for a process running on the target UDID. +2. Probing TCP `47370–47402` on `127.0.0.1` for a Swift inspector agent and matching its `processIdentifier` against the UDID's processes via `ps`. +3. Falling back to AXe when neither matches. + +Every accessibility tree response includes: + +```json +{ + "source": "nativescript|in-app-inspector|axe", + "availableSources": ["nativescript", "in-app-inspector", "axe"], + "fallbackReason": "...", + "inspector": { "bundleIdentifier": "...", "processIdentifier": 73214 } +} +``` + +`fallbackReason` only appears when the requested source could not be honoured. The browser client uses it to render a banner explaining what happened. + +## Choosing the right inspector + +- **You own the iOS app and write Swift / Objective-C.** Link the [Swift in-app agent](/inspector/swift). It exposes the most semantic data — UIView properties, SwiftUI probes, custom actions — and lets the browser client edit values in place. +- **You ship a NativeScript app.** Use the [NativeScript runtime inspector](/inspector/nativescript). It connects outbound to the SimDeck server and publishes both the NativeScript logical tree and the underlying UIKit hierarchy. +- **You can't link anything into the app.** Stick with [AXe](/inspector/accessibility). It only sees what the iOS accessibility stack exposes, but it works for every app. + +## Editing properties + +When the in-app inspector is reachable, the browser client can: + +- List actions a view supports (`tap`, `setText`, `scrollBy`, …) and trigger them. +- Read editable runtime properties (`alpha`, `backgroundColor`, `clipsToBounds`, `text`, …). +- Set those properties live, with structured value coercion for `UIColor`, `CGRect`, `CGPoint`, `CGSize`, and `UIEdgeInsets`. + +These calls go through the HTTP proxy at `POST /api/simulators/{udid}/inspector/request`, which only accepts a small allow-list of methods. Direct TCP and WebSocket clients can use the full method list documented in the [Inspector Protocol](/api/inspector-protocol). diff --git a/docs/inspector/nativescript.md b/docs/inspector/nativescript.md new file mode 100644 index 00000000..c5a1374e --- /dev/null +++ b/docs/inspector/nativescript.md @@ -0,0 +1,151 @@ +# NativeScript Runtime Inspector + +`@nativescript/xcode-canvas-inspector` is the runtime that connects a NativeScript app's view hierarchy to the SimDeck server without linking the Swift inspector framework. It implements the same [Inspector Protocol](/api/inspector-protocol) as the Swift agent but ships as a TypeScript package and runs entirely inside the NativeScript runtime. + +The package source lives at `packages/nativescript-inspector/` in this repo. + +## Install + +```sh +npm install @nativescript/xcode-canvas-inspector +``` + +The package is `darwin`-friendly because the underlying NativeScript runtime is iOS-only; it works fine in NativeScript-iOS builds. + +## Start the inspector + +Call `startXcodeCanvasInspector(...)` as early as possible in app startup, ideally before any view is bootstrapped. + +### NativeScript Core + +```ts +import { startXcodeCanvasInspector } from "@nativescript/xcode-canvas-inspector"; + +if (__DEV__) { + startXcodeCanvasInspector({ port: 4310 }); +} +``` + +::: tip Port semantics +`port` here is the **SimDeck server port**, not a per-app inspector port. NativeScript apps do not need to choose a unique local inspector port — they connect outbound to the server's WebSocket hub. +::: + +### NativeScript + Angular + +For Angular NativeScript apps, call `startXcodeCanvasInspector()` **before** `runNativeScriptAngularApp()`: + +```ts +import { startXcodeCanvasInspector } from "@nativescript/xcode-canvas-inspector"; + +startXcodeCanvasInspector({ port: 4310 }); + +runNativeScriptAngularApp({ + appModuleBootstrap: () => + bootstrapApplication(AppComponent, { + providers: [ + provideNativeScriptHttpClient(withInterceptorsFromDi()), + provideNativeScriptRouter(routes), + ], + }), +}); +``` + +The package installs a small compatibility shim for Angular 20 dev-mode template source locations on NativeScript views. + +## What it exposes + +`View.getHierarchy` returns the NativeScript logical tree by default — the actual `View` nodes you wrote, not just their UIKit backings. Pass `"source": "uikit"` to force the raw UIKit hierarchy. + +Each NativeScript node carries: + +- `type` — the view's class name (`Label`, `StackLayout`, `GridLayout`, …). +- `title` — derived text content or accessibility label when available. +- `bounds` and `frameInScreen` — UIKit screen points. +- `nativeScript` — NativeScript-specific metadata (CSS class names, IDs, bindings). +- `sourceLocation` — file/line/column when source maps are available. + +When the in-app NativeScript inspector is the source, the SimDeck server returns `"source": "nativescript"` on the accessibility tree response and surfaces the matching `bundleIdentifier`, `processIdentifier`, and `displayScale` in the `inspector` block. + +## Connection model + +The runtime opens a WebSocket from the simulator app to the SimDeck server: + +```text +ws://127.0.0.1:4310/api/inspector/connect +``` + +After the WebSocket is up, the server sends `Inspector.getInfo` and the runtime responds with the protocol version, `processIdentifier`, bundle metadata, and the available hierarchy sources. The server registers the runtime under that PID and prefers it over any TCP-based inspector for the same process. + +If the WebSocket transport is not available, the runtime falls back to long-polling: + +- `GET /api/inspector/poll?processIdentifier=` — waits for the next request from the server. +- `POST /api/inspector/response` — sends the response back. + +Both transports speak the same envelope, so the same JS code handles them. + +## Source locations from Angular templates + +The NativeScript runtime can attach `sourceLocation` metadata to individual nodes when Angular dev-mode template source locations are enabled. Wire up your NativeScript webpack config: + +```js +const webpack = require("@nativescript/webpack"); + +class AngularTemplateSourceLocationsPlugin { + apply(compiler) { + const enable = async () => { + const angularCompiler = await import("@angular/compiler"); + angularCompiler.setEnableTemplateSourceLocations?.(true); + }; + + compiler.hooks.beforeRun.tapPromise( + "AngularTemplateSourceLocationsPlugin", + enable, + ); + compiler.hooks.watchRun.tapPromise( + "AngularTemplateSourceLocationsPlugin", + enable, + ); + } +} + +module.exports = (env) => { + webpack.init(env); + webpack.chainWebpack((config) => { + config + .plugin("angular-template-source-locations") + .use(AngularTemplateSourceLocationsPlugin); + }); + + return webpack.resolveConfig(); +}; +``` + +With that in place, hierarchy nodes carry entries like: + +```json +{ + "type": "Label", + "sourceLocation": { + "file": "src/app/home.component.html", + "line": 12, + "column": 5, + "offset": 238 + } +} +``` + +The SimDeck browser client renders the file path inline so you can jump straight to source. + +## Stopping the inspector + +```ts +import { stopXcodeCanvasInspector } from "@nativescript/xcode-canvas-inspector"; + +stopXcodeCanvasInspector(); +``` + +This closes the WebSocket and stops responding to inspector requests. Subsequent calls to `startXcodeCanvasInspector(...)` will spin a fresh runtime. + +## Coexistence with the Swift agent + +If both the Swift in-app inspector agent and the NativeScript runtime are present in the same app, the SimDeck server prefers the NativeScript runtime because it can publish the framework-level hierarchy. Direct TCP clients of the Swift agent are unaffected. diff --git a/docs/inspector/swift.md b/docs/inspector/swift.md new file mode 100644 index 00000000..39cdaa13 --- /dev/null +++ b/docs/inspector/swift.md @@ -0,0 +1,152 @@ +# Swift In-App Inspector Agent + +`XcodeCanvasInspectorAgent` is a debug-only iOS Swift package that exposes a UIKit hierarchy and live property edits through the [`XCWI/0.1`](/api/inspector-protocol) protocol. Apps that link it for `Debug` builds can be inspected from the SimDeck browser client without going through the system accessibility stack. + +The package source lives at `packages/inspector-agent/` in this repo. + +## Install + +Add the local Swift package to your app: + +```text +packages/inspector-agent +``` + +Then link the `XcodeCanvasInspectorAgent` product into your app target for `Debug` configurations only. + +## Start the agent + +Call the initializer early in app startup, behind a `#if DEBUG` guard. + +### SwiftUI + +```swift +#if DEBUG +import XcodeCanvasInspectorAgent +#endif + +@main +struct DemoApp: App { + init() { + #if DEBUG + try? XcodeCanvasInspectorAgent.shared.start() + #endif + } + + var body: some Scene { + WindowGroup { + ContentView() + } + } +} +``` + +### UIKit + +```swift +func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? +) -> Bool { + #if DEBUG + try? XcodeCanvasInspectorAgent.shared.start() + #endif + return true +} +``` + +## How discovery works + +The agent listens on TCP `127.0.0.1:47370` by default. If that port is already in use it tries the next 32 ports and listens on the first free one. It also advertises Bonjour service type `_xcwinspector._tcp` so other tools can find it without probing. + +The SimDeck server discovers the agent for a given simulator by: + +1. Probing TCP `47370–47402` on `127.0.0.1`. +2. Calling `Inspector.getInfo` to read the agent's `processIdentifier`. +3. Running `ps -p -o command=` and matching the simulator's UDID against the process command line. + +This lets multiple inspector-enabled apps run side by side without colliding. + +## Talking to the agent directly + +The protocol is newline-delimited JSON over TCP — convenient enough for `nc`: + +```sh +printf '{"id":1,"method":"Inspector.getInfo"}\n' | nc 127.0.0.1 47370 +printf '{"id":2,"method":"View.getHierarchy","params":{"maxDepth":4}}\n' | nc 127.0.0.1 47370 +``` + +For the full envelope shape and method list, see the [Inspector Protocol](/api/inspector-protocol). + +## SwiftUI tagging + +The agent automatically reports SwiftUI hosting and bridge `UIView`s, but SwiftUI's value tree is not publicly enumerable at runtime. To make specific SwiftUI elements addressable, tag them in source: + +```swift +Text("Continue") + .xcwInspectorTag("continue-label", id: "onboarding.continue.label") +``` + +Tagged SwiftUI views appear as lightweight, non-interactive probe `UIView`s in the hierarchy with `swiftUI.isProbe = true`. The browser client surfaces them in the inspector pane. + +## Publishing a framework hierarchy + +Frameworks with their own logical tree can publish that tree into the agent. When a published snapshot exists, `View.getHierarchy` returns it by default; pass `"source": "uikit"` to force the raw UIKit tree. + +```swift +try? XcodeCanvasInspectorAgent.shared.publishHierarchySnapshot( + source: "nativescript", + snapshotJSON: #"{"source":"nativescript","roots":[]}"# +) +``` + +This is exactly how the [NativeScript runtime inspector](/inspector/nativescript) exposes its logical tree. + +Framework snapshots can attach source locations to individual nodes: + +```json +{ + "type": "Label", + "title": "Continue", + "sourceLocation": { + "file": "src/app/home.component.html", + "line": 12, + "column": 5, + "offset": 238 + } +} +``` + +The browser client uses these to jump from the hierarchy back to source. + +## Auth token + +For shared-network simulator sessions, start the agent with a token and require every request to include a top-level `token`: + +```swift +try? XcodeCanvasInspectorAgent.shared.start( + configuration: .init( + port: 47370, + bindToLocalhostOnly: false, + authToken: "debug-secret" + ) +) +``` + +When a token is set, requests without a matching `token` field are rejected with `error.code = -32401`. + +## Recommended configuration + +For most apps the defaults are correct: + +- Bind to `127.0.0.1` only. +- Listen on `47370` (with auto-fallback if busy). +- No auth token. + +For multi-user development setups (shared simulator hosts on a LAN, CI rigs), set `bindToLocalhostOnly: false` plus an auth token and forward the inspector port through your VPN or SSH tunnel of choice. + +## Compatibility with the NativeScript inspector + +The Swift agent and the NativeScript runtime inspector implement the same protocol on different transports. Anything that can talk `XCWI/0.1` over TCP can also talk it over the SimDeck WebSocket hub, and vice versa. + +The SimDeck server prefers connected NativeScript inspectors over Swift TCP agents when both are present for the same process. Direct TCP clients can pick whichever transport they prefer. diff --git a/docs/public/icons/README.md b/docs/public/icons/README.md new file mode 100644 index 00000000..bfb42e86 --- /dev/null +++ b/docs/public/icons/README.md @@ -0,0 +1,54 @@ +# Home page icons + +These SVGs are used by the feature grid in [`docs/index.md`](../../index.md). They are vendored locally so the site has no runtime CDN dependency. + +## Source + +- **Library**: [Lucide](https://lucide.dev) — distributed via the [`lucide-static`](https://www.npmjs.com/package/lucide-static) npm package. +- **License**: ISC. The license comment at the top of each SVG (``) must stay intact. + +## Manual edits applied to each icon + +After downloading, two small tweaks are applied: + +1. `stroke="currentColor"` → `stroke="#0a84ff"` so the icon picks up the SimDeck accent color in both light and dark themes (VitePress renders feature icons via ``, which doesn't inherit the page color). +2. `stroke-width="2"` → `stroke-width="1.75"` for a slightly lighter feel at small sizes. + +## Adding a new icon + +1. Pick the icon at [lucide.dev](https://lucide.dev) and note its kebab-case name (e.g., `rocket`). +2. From the repo root, fetch and patch it: + + ```sh + ICON=rocket + curl -sSfL "https://unpkg.com/lucide-static@latest/icons/${ICON}.svg" \ + -o "docs/public/icons/${ICON}.svg" + sed -i '' \ + -e 's/stroke="currentColor"/stroke="#0a84ff"/' \ + -e 's/stroke-width="2"/stroke-width="1.75"/' \ + "docs/public/icons/${ICON}.svg" + ``` + + (On Linux, drop the empty `''` after `sed -i`.) + +3. Reference it from `docs/index.md`: + + ```yaml + - icon: + src: /icons/rocket.svg + width: 28 + height: 28 + title: ... + details: ... + ``` + +4. `npm run docs:dev` to preview, `npm run docs:build` to verify the production build. + +## Replacing the icon set + +If you ever swap Lucide for another library (Phosphor, Tabler, Heroicons), keep the same constraints: + +- Inline SVG, not a sprite or icon font. +- `stroke` (or `fill`) set to a fixed color that works on both backgrounds — `` won't inherit `currentColor`. +- Roughly 24×24 viewBox so the existing `width: 28 / height: 28` in `index.md` keeps the visual rhythm. +- License attribution preserved in a comment at the top of each file. diff --git a/docs/public/icons/monitor-smartphone.svg b/docs/public/icons/monitor-smartphone.svg new file mode 100644 index 00000000..7847b671 --- /dev/null +++ b/docs/public/icons/monitor-smartphone.svg @@ -0,0 +1,18 @@ + + + + + + + diff --git a/docs/public/icons/network.svg b/docs/public/icons/network.svg new file mode 100644 index 00000000..00bb211a --- /dev/null +++ b/docs/public/icons/network.svg @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/docs/public/icons/puzzle.svg b/docs/public/icons/puzzle.svg new file mode 100644 index 00000000..0b1f3da5 --- /dev/null +++ b/docs/public/icons/puzzle.svg @@ -0,0 +1,15 @@ + + + + diff --git a/docs/public/icons/scan-search.svg b/docs/public/icons/scan-search.svg new file mode 100644 index 00000000..cfbe29d0 --- /dev/null +++ b/docs/public/icons/scan-search.svg @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/docs/public/icons/shield-check.svg b/docs/public/icons/shield-check.svg new file mode 100644 index 00000000..85a5d4d9 --- /dev/null +++ b/docs/public/icons/shield-check.svg @@ -0,0 +1,16 @@ + + + + + diff --git a/docs/public/icons/zap.svg b/docs/public/icons/zap.svg new file mode 100644 index 00000000..b9b3ddd3 --- /dev/null +++ b/docs/public/icons/zap.svg @@ -0,0 +1,15 @@ + + + + diff --git a/package-lock.json b/package-lock.json index fa6f7ed4..21e77eb8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,12 +17,271 @@ }, "devDependencies": { "@vscode/vsce": "^3.6.2", - "prettier": "^3.6.2" + "prettier": "^3.6.2", + "vitepress": "^1.6.4" }, "engines": { "node": ">=18" } }, + "node_modules/@algolia/abtesting": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.17.0.tgz", + "integrity": "sha512-nuhHZdTiCtRzJEe9VSNzyqE9cOQMt01UWBzymFnjbgwrxxZpbGHQde6Oa/y9zyspTCjbUtb7Q5HQek1CLiLyeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/autocomplete-core": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz", + "integrity": "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", + "@algolia/autocomplete-shared": "1.17.7" + } + }, + "node_modules/@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz", + "integrity": "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.17.7" + }, + "peerDependencies": { + "search-insights": ">= 1 < 3" + } + }, + "node_modules/@algolia/autocomplete-preset-algolia": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz", + "integrity": "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.17.7" + }, + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/autocomplete-shared": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", + "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/client-abtesting": { + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.51.0.tgz", + "integrity": "sha512-PKrKlIla1U2J7mFcIQn6N3pWP4oySmkxShnbbDsj/H7818gKbET5KsUwsVoNjWIxHKTJMCTcQ7ekAJ8Ea23NMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.51.0.tgz", + "integrity": "sha512-U+HCY1K16Km91pIRL1kN6bW6BbGFAF/WhkRSCx4wyl1aFpbrlhSFQs/dAwWbmyBiHWwVWhl7stWHQ1pum5EfMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-common": { + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.51.0.tgz", + "integrity": "sha512-YPJ3dEuZLCRp846Az94t6Z2gwSNRazP+SmBco7p6SCa4fYrtIE820PDXYZshbNrj2Z8Qfbmv7BQ1Lecl5L3G/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-insights": { + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.51.0.tgz", + "integrity": "sha512-/gEwLlR7fQ7YjOW+ADRZ0NxLDtpTC61FSzlZ01Gdl1kTJfU0Rq3Y/TYqwxGxlQGcUiXtGzrpjxXWh3Y0TZD6NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.51.0.tgz", + "integrity": "sha512-nRwUN1Y2cKyOAFZyIBagkEfZSIhP05nWhT4Rjwl84lcjECssYggftrAODrZ4leakXxSGjhxs/AdaAFEIBqwVFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-query-suggestions": { + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.51.0.tgz", + "integrity": "sha512-pybzYCG7VoQKppo+z5iZOKpW8XqtFxhsAIRgEaNboCnfypKukiBHJAwB+pmr7vMZXBsOHwklGYWwCG82e8qshA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-search": { + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.51.0.tgz", + "integrity": "sha512-DWVIlj6RqcvdhwP0gBU9OpOQPnHdcAk9jlT+z8rsNb2+liWv4eUlfQZ7saGBraFsnygEHD3PtdppIHvqwBAb5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/ingestion": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.51.0.tgz", + "integrity": "sha512-bA25s12iUDJi/X8M7tWlPRT8GeOhls/yDbdoUqinz27lNqsOlcM1UrAxIKdIZ6lm3sXit+ewPIz1pS2x6rXu8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/monitoring": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.51.0.tgz", + "integrity": "sha512-zj+RcE5e0NE0/ew6oEOTgOplPHry+w2oi7h0Y87lhdq4E0d7xLS31KVB8kKfCGkrG7AYtZvrcyvLOKS5d0no4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/recommend": { + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.51.0.tgz", + "integrity": "sha512-/HDgccfye1Rq3bPxaSCsvSEBHzSMmtpM9ZRGRtAuC62Cv+ql/76IWnxjGTDXtqIJ+/j7ZlFYAzq9fkp95wF2SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.51.0.tgz", + "integrity": "sha512-nJdW+WBwGlXWMJbxxB7/AJPvNq0lLJSudXmIQCJbmH8jsOXQhRpAtoCD4ceLyJKv3ze9JbQu4Gqu5JDLckuFcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.51.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-fetch": { + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.51.0.tgz", + "integrity": "sha512-bsBgRI/1h1mjS3eCyfGau78yGZVmiDLmT1aU6dMnk75/T0SgKqnSKNpQ53xKoDYVChGDcNnpHXWpoUSo8MH1+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.51.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-node-http": { + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.51.0.tgz", + "integrity": "sha512-zPrIDVPpmKWgrjmWOqpqrhqAhNjvVkjoj+mqw2NBPxEOuKNzP0H+Qz5NJLLTOepBVj1UFedFaF3AUgxLsB9ukQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.51.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@azu/format-text": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@azu/format-text/-/format-text-1.0.2.tgz", @@ -224,6 +483,16 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-validator-identifier": { "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", @@ -234,53 +503,938 @@ "node": ">=6.9.0" } }, - "node_modules/@isaacs/cliui": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", - "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, "engines": { - "node": ">=18" + "node": ">=6.0.0" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "dev": true, "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { - "node": ">= 8" + "node": ">=6.9.0" + } + }, + "node_modules/@docsearch/css": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.8.2.tgz", + "integrity": "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@docsearch/js": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.8.2.tgz", + "integrity": "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@docsearch/react": "3.8.2", + "preact": "^10.0.0" + } + }, + "node_modules/@docsearch/react": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.8.2.tgz", + "integrity": "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-core": "1.17.7", + "@algolia/autocomplete-preset-algolia": "1.17.7", + "@docsearch/css": "3.8.2", + "algoliasearch": "^5.14.2" + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 19.0.0", + "react": ">= 16.8.0 < 19.0.0", + "react-dom": ">= 16.8.0 < 19.0.0", + "search-insights": ">= 1 < 3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "search-insights": { + "optional": true + } + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@iconify-json/simple-icons": { + "version": "1.2.79", + "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.79.tgz", + "integrity": "sha512-aNyO7Fd1qej9oQfIyohYFRv0lhQLaZ+6UkK1c1qwax0MDPUOZOdq65MlU500kow97pD/W+b2u1And3e25eE24Q==", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz", + "integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz", + "integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz", + "integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz", + "integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz", + "integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz", + "integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz", + "integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz", + "integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz", + "integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz", + "integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz", + "integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz", + "integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz", + "integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz", + "integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz", + "integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz", + "integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz", + "integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz", + "integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz", + "integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz", + "integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz", + "integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz", + "integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz", + "integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz", + "integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">= 8" - } + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz", + "integrity": "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } + "optional": true, + "os": [ + "win32" + ] }, "node_modules/@secretlint/config-creator": { "version": "10.2.2", @@ -456,6 +1610,93 @@ "node": ">=20.0.0" } }, + "node_modules/@shikijs/core": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-2.5.0.tgz", + "integrity": "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/engine-javascript": "2.5.0", + "@shikijs/engine-oniguruma": "2.5.0", + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.4" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz", + "integrity": "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^3.1.0" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz", + "integrity": "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@shikijs/langs": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-2.5.0.tgz", + "integrity": "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/themes": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-2.5.0.tgz", + "integrity": "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/transformers": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-2.5.0.tgz", + "integrity": "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "2.5.0", + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/types": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.5.0.tgz", + "integrity": "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "dev": true, + "license": "MIT" + }, "node_modules/@sindresorhus/merge-streams": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", @@ -553,6 +1794,58 @@ "@textlint/ast-node-types": "15.5.4" } }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/normalize-package-data": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", @@ -567,6 +1860,20 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "dev": true, + "license": "MIT" + }, "node_modules/@typespec/ts-http-runtime": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.5.tgz", @@ -582,6 +1889,27 @@ "node": ">=20.0.0" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, "node_modules/@vscode/vsce": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.9.1.tgz", @@ -746,33 +2074,297 @@ "linux" ] }, - "node_modules/@vscode/vsce-sign-win32-arm64": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-arm64/-/vsce-sign-win32-arm64-2.0.6.tgz", - "integrity": "sha512-ivM/MiGIY0PJNZBoGtlRBM/xDpwbdlCWomUWuLmIxbi1Cxe/1nooYrEQoaHD8ojVRgzdQEUzMsRbyF5cJJgYOg==", - "cpu": [ - "arm64" - ], + "node_modules/@vscode/vsce-sign-win32-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-arm64/-/vsce-sign-win32-arm64-2.0.6.tgz", + "integrity": "sha512-ivM/MiGIY0PJNZBoGtlRBM/xDpwbdlCWomUWuLmIxbi1Cxe/1nooYrEQoaHD8ojVRgzdQEUzMsRbyF5cJJgYOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@vscode/vsce-sign-win32-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-x64/-/vsce-sign-win32-x64-2.0.6.tgz", + "integrity": "sha512-mgth9Kvze+u8CruYMmhHw6Zgy3GRX2S+Ed5oSokDEK5vPEwGGKnmuXua9tmFhomeAnhgJnL4DCna3TiNuGrBTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.33.tgz", + "integrity": "sha512-3PZLQwFw4Za3TC8t0FvTy3wI16Kt+pmwcgNZca4Pj9iWL2E72a/gZlpBtAJvEdDMdCxdG/qq0C7PN0bsJuv0Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.2", + "@vue/shared": "3.5.33", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-core/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.33.tgz", + "integrity": "sha512-PXq0yrfCLzzL07rbXO4awtXY1Z06LG2eu6Adg3RJFa/j3Cii217XxxLXG22N330gw7GmALCY0Z8RgXEviwgpjA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.33", + "@vue/shared": "3.5.33" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.33.tgz", + "integrity": "sha512-UTUvRO9cY+rROrx/pvN9P5Z7FgA6QGfokUCfhQE4EnmUj3rVnK+CHI0LsEO1pg+I7//iRYMUfcNcCPe7tg0CoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.2", + "@vue/compiler-core": "3.5.33", + "@vue/compiler-dom": "3.5.33", + "@vue/compiler-ssr": "3.5.33", + "@vue/shared": "3.5.33", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.10", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.33.tgz", + "integrity": "sha512-IErjYdnj1qIupG5xxiVIYiiRvDhGWV4zuh/RCrwfYpuL+HWQzeU6lCk/nF9r7olWMnjKxCAkOctT2qFWFkzb1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.33", + "@vue/shared": "3.5.33" + } + }, + "node_modules/@vue/devtools-api": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz", + "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.9" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz", + "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.9", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz", + "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.33", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.33.tgz", + "integrity": "sha512-p8UfIqyIhb0rYGlSgSBV+lPhF2iUSBcRy7enhTmPqKWadHy9kcOFYF1AejYBP9P+avnd3OBbD49DU4pLWX/94A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.33" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.33", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.33.tgz", + "integrity": "sha512-UpFF45RI9//a7rvq7RdOQblb4tup7hHG9QsmIrxkFQLzQ7R8/iNQ5LE15NhLZ1/WcHMU2b47u6P33CPUelHyIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.33", + "@vue/shared": "3.5.33" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.33", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.33.tgz", + "integrity": "sha512-IOxMsAOwquhfITgmOgaPYl7/j8gKUxUFoflRc+u4LxyD3+783xne8vNta1PONVCvCV9A0w7hkyEepINDqfO0tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.33", + "@vue/runtime-core": "3.5.33", + "@vue/shared": "3.5.33", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.33", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.33.tgz", + "integrity": "sha512-0xylq/8/h44lVG0pZFknv1XIdEgymq2E9n59uTWJBG+dIgiT0TMCSsxrN7nO16Z0MU0MPjFcguBbZV8Itk52Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.33", + "@vue/shared": "3.5.33" + }, + "peerDependencies": { + "vue": "3.5.33" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.33", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.33.tgz", + "integrity": "sha512-5vR2QIlmaLG77Ygd4pMP6+SGQ5yox9VhtnbDWTy9DzMzdmeLxZ1QqxrywEZ9sa1AVubfIJyaCG3ytyWU81ufcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vueuse/core": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz", + "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/integrations": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-12.8.2.tgz", + "integrity": "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vueuse/core": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "async-validator": "^4", + "axios": "^1", + "change-case": "^5", + "drauu": "^0.4", + "focus-trap": "^7", + "fuse.js": "^7", + "idb-keyval": "^6", + "jwt-decode": "^4", + "nprogress": "^0.2", + "qrcode": "^1.5", + "sortablejs": "^1", + "universal-cookie": "^7" + }, + "peerDependenciesMeta": { + "async-validator": { + "optional": true + }, + "axios": { + "optional": true + }, + "change-case": { + "optional": true + }, + "drauu": { + "optional": true + }, + "focus-trap": { + "optional": true + }, + "fuse.js": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "jwt-decode": { + "optional": true + }, + "nprogress": { + "optional": true + }, + "qrcode": { + "optional": true + }, + "sortablejs": { + "optional": true + }, + "universal-cookie": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz", + "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==", "dev": true, - "license": "SEE LICENSE IN LICENSE.txt", - "optional": true, - "os": [ - "win32" - ] + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } }, - "node_modules/@vscode/vsce-sign-win32-x64": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-x64/-/vsce-sign-win32-x64-2.0.6.tgz", - "integrity": "sha512-mgth9Kvze+u8CruYMmhHw6Zgy3GRX2S+Ed5oSokDEK5vPEwGGKnmuXua9tmFhomeAnhgJnL4DCna3TiNuGrBTQ==", - "cpu": [ - "x64" - ], + "node_modules/@vueuse/shared": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz", + "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==", "dev": true, - "license": "SEE LICENSE IN LICENSE.txt", - "optional": true, - "os": [ - "win32" - ] + "license": "MIT", + "dependencies": { + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } }, "node_modules/agent-base": { "version": "7.1.4", @@ -801,6 +2393,32 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/algoliasearch": { + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.51.0.tgz", + "integrity": "sha512-u3XS8HaTzt5YN90KPsOXMRjYJUMVD1dtr6yi4NXQluMbZ5IjQNBu1MEabdAxFhYtEuexqomPMSmRIhQJUd3QSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/abtesting": "1.17.0", + "@algolia/client-abtesting": "5.51.0", + "@algolia/client-analytics": "5.51.0", + "@algolia/client-common": "5.51.0", + "@algolia/client-insights": "5.51.0", + "@algolia/client-personalization": "5.51.0", + "@algolia/client-query-suggestions": "5.51.0", + "@algolia/client-search": "5.51.0", + "@algolia/ingestion": "1.51.0", + "@algolia/monitoring": "1.51.0", + "@algolia/recommend": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/ansi-escapes": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", @@ -926,6 +2544,16 @@ "url": "https://bevry.me/fund" } }, + "node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -1067,6 +2695,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1084,6 +2723,28 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/cheerio": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz", @@ -1179,6 +2840,17 @@ "node": ">= 0.8" } }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "12.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", @@ -1196,6 +2868,22 @@ "dev": true, "license": "MIT" }, + "node_modules/copy-anything": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz", + "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-what": "^5.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1241,6 +2929,13 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -1340,6 +3035,16 @@ "node": ">=0.4.0" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -1351,6 +3056,20 @@ "node": ">=8" } }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -1459,6 +3178,13 @@ "dev": true, "license": "MIT" }, + "node_modules/emoji-regex-xs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", + "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", + "dev": true, + "license": "MIT" + }, "node_modules/encoding-sniffer": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", @@ -1559,6 +3285,52 @@ "node": ">= 0.4" } }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -1634,6 +3406,16 @@ "node": ">=8" } }, + "node_modules/focus-trap": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.8.0.tgz", + "integrity": "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tabbable": "^6.4.0" + } + }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", @@ -1691,6 +3473,21 @@ "node": ">=14.14" } }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -1918,6 +3715,51 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true, + "license": "MIT" + }, "node_modules/hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -1931,6 +3773,17 @@ "node": ">=10" } }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/htmlparser2": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", @@ -2144,6 +3997,19 @@ "node": ">=0.12.0" } }, + "node_modules/is-what": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz", + "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/is-wsl": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", @@ -2416,6 +4282,23 @@ "node": ">=10" } }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/markdown-it": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", @@ -2444,6 +4327,28 @@ "node": ">= 0.4" } }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", @@ -2461,6 +4366,100 @@ "node": ">= 8" } }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -2554,10 +4553,24 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minisearch": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.2.0.tgz", + "integrity": "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==", + "dev": true, + "license": "MIT" + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" }, "node_modules/mkdirp-classic": { "version": "0.5.3", @@ -2581,6 +4594,25 @@ "dev": true, "license": "ISC" }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/napi-build-utils": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", @@ -2697,6 +4729,18 @@ "wrappy": "1" } }, + "node_modules/oniguruma-to-es": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz", + "integrity": "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex-xs": "^1.0.0", + "regex": "^6.0.1", + "regex-recursion": "^6.0.2" + } + }, "node_modules/open": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", @@ -2884,6 +4928,13 @@ "dev": true, "license": "MIT" }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -2914,6 +4965,46 @@ "node": ">=4" } }, + "node_modules/postcss": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/preact": { + "version": "10.29.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.1.tgz", + "integrity": "sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/prebuild-install": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", @@ -2959,6 +5050,17 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/pump": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", @@ -3110,6 +5212,33 @@ "node": ">= 6" } }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "dev": true, + "license": "MIT" + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -3131,6 +5260,58 @@ "node": ">=0.10.0" } }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.2.tgz", + "integrity": "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.2", + "@rollup/rollup-android-arm64": "4.60.2", + "@rollup/rollup-darwin-arm64": "4.60.2", + "@rollup/rollup-darwin-x64": "4.60.2", + "@rollup/rollup-freebsd-arm64": "4.60.2", + "@rollup/rollup-freebsd-x64": "4.60.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.2", + "@rollup/rollup-linux-arm-musleabihf": "4.60.2", + "@rollup/rollup-linux-arm64-gnu": "4.60.2", + "@rollup/rollup-linux-arm64-musl": "4.60.2", + "@rollup/rollup-linux-loong64-gnu": "4.60.2", + "@rollup/rollup-linux-loong64-musl": "4.60.2", + "@rollup/rollup-linux-ppc64-gnu": "4.60.2", + "@rollup/rollup-linux-ppc64-musl": "4.60.2", + "@rollup/rollup-linux-riscv64-gnu": "4.60.2", + "@rollup/rollup-linux-riscv64-musl": "4.60.2", + "@rollup/rollup-linux-s390x-gnu": "4.60.2", + "@rollup/rollup-linux-x64-gnu": "4.60.2", + "@rollup/rollup-linux-x64-musl": "4.60.2", + "@rollup/rollup-openbsd-x64": "4.60.2", + "@rollup/rollup-openharmony-arm64": "4.60.2", + "@rollup/rollup-win32-arm64-msvc": "4.60.2", + "@rollup/rollup-win32-ia32-msvc": "4.60.2", + "@rollup/rollup-win32-x64-gnu": "4.60.2", + "@rollup/rollup-win32-x64-msvc": "4.60.2", + "fsevents": "~2.3.2" + } + }, "node_modules/run-applescript": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", @@ -3206,6 +5387,14 @@ "node": ">=11.0.0" } }, + "node_modules/search-insights": { + "version": "2.17.3", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", + "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/secretlint": { "version": "10.2.2", "resolved": "https://registry.npmjs.org/secretlint/-/secretlint-10.2.2.tgz", @@ -3264,6 +5453,23 @@ "node": ">=8" } }, + "node_modules/shiki": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-2.5.0.tgz", + "integrity": "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "2.5.0", + "@shikijs/engine-javascript": "2.5.0", + "@shikijs/engine-oniguruma": "2.5.0", + "@shikijs/langs": "2.5.0", + "@shikijs/themes": "2.5.0", + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -3433,6 +5639,27 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -3469,6 +5696,16 @@ "dev": true, "license": "CC0-1.0" }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -3518,6 +5755,21 @@ "node": ">=8" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/strip-ansi": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", @@ -3555,6 +5807,19 @@ "boundary": "^2.0.0" } }, + "node_modules/superjson": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "copy-anything": "^4" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -3585,6 +5850,13 @@ "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" } }, + "node_modules/tabbable": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", + "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", + "dev": true, + "license": "MIT" + }, "node_modules/table": { "version": "6.9.0", "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", @@ -3720,6 +5992,17 @@ "node": ">=8.0" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -3813,6 +6096,79 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -3872,6 +6228,160 @@ "url": "https://bevry.me/fund" } }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitepress": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.6.4.tgz", + "integrity": "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@docsearch/css": "3.8.2", + "@docsearch/js": "3.8.2", + "@iconify-json/simple-icons": "^1.2.21", + "@shikijs/core": "^2.1.0", + "@shikijs/transformers": "^2.1.0", + "@shikijs/types": "^2.1.0", + "@types/markdown-it": "^14.1.2", + "@vitejs/plugin-vue": "^5.2.1", + "@vue/devtools-api": "^7.7.0", + "@vue/shared": "^3.5.13", + "@vueuse/core": "^12.4.0", + "@vueuse/integrations": "^12.4.0", + "focus-trap": "^7.6.4", + "mark.js": "8.11.1", + "minisearch": "^7.1.1", + "shiki": "^2.1.0", + "vite": "^5.4.14", + "vue": "^3.5.13" + }, + "bin": { + "vitepress": "bin/vitepress.js" + }, + "peerDependencies": { + "markdown-it-mathjax3": "^4", + "postcss": "^8" + }, + "peerDependenciesMeta": { + "markdown-it-mathjax3": { + "optional": true + }, + "postcss": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.33", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.33.tgz", + "integrity": "sha512-1AgChhx5w3ALgT4oK3acm2Es/7jyZhWSVUfs3rOBlGQC0rjEDkS7G4lWlJJGGNQD+BV3reCwbQrOe1mPNwKHBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.33", + "@vue/compiler-sfc": "3.5.33", + "@vue/runtime-dom": "3.5.33", + "@vue/server-renderer": "3.5.33", + "@vue/shared": "3.5.33" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/whatwg-encoding": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", @@ -3990,6 +6500,17 @@ "dependencies": { "buffer-crc32": "~0.2.3" } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/package.json b/package.json index 04108c95..c57c8dec 100644 --- a/package.json +++ b/package.json @@ -42,11 +42,15 @@ "test": "cargo test --manifest-path server/Cargo.toml && npm run --prefix client test", "ci": "npm run lint && npm run build && npm run test && npm run package:vscode-extension", "dev": "npm run build:cli && node scripts/dev.mjs", + "docs:dev": "vitepress dev docs", + "docs:build": "vitepress build docs", + "docs:preview": "vitepress preview docs", "postinstall": "node scripts/npm-postinstall.mjs", "prepack": "npm run build:client" }, "devDependencies": { "@vscode/vsce": "^3.6.2", - "prettier": "^3.6.2" + "prettier": "^3.6.2", + "vitepress": "^1.6.4" } }