create-bridgething 0.11.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +16 -12
  2. package/dist/index.mjs +476 -142
  3. package/package.json +20 -5
  4. package/template/_claude/skills/bridgething/SKILL.md +12 -17
  5. package/template/_claude/skills/bridgething/reference/develop.md +86 -66
  6. package/template/_claude/skills/bridgething/reference/extension.md +183 -0
  7. package/template/_claude/skills/bridgething/reference/sdk.md +86 -105
  8. package/template/_claude/skills/bridgething/reference/ship.md +19 -38
  9. package/template/_gitignore +1 -0
  10. package/template/package.json +6 -5
  11. package/template/scripts/bridgething.ts +1086 -0
  12. package/template/scripts/push.ts +212 -193
  13. package/template/settings/main.tsx +14 -13
  14. package/template/src/App.tsx +3 -6
  15. package/template/src/daemon.ts +14 -0
  16. package/template/tsconfig.json +1 -1
  17. package/template/vite.config.ts +5 -3
  18. package/template/vite.settings.config.ts +3 -3
  19. package/template-extension/_claude_append.md +23 -0
  20. package/template-extension/extension/main.ts +20 -0
  21. package/template-launcher/_claude_append.md +14 -30
  22. package/template-launcher/src/App.tsx +2 -5
  23. package/template-overlay/_claude_append.md +31 -45
  24. package/template-overlay/vite.overlay.config.ts +1 -1
  25. package/template-source/CLAUDE.md +104 -0
  26. package/template-source/README.md +57 -0
  27. package/template-source/_bunversion +1 -0
  28. package/template-source/_github/workflows/ci.yml +28 -0
  29. package/template-source/_github/workflows/publish.yml +33 -0
  30. package/template-source/_gitignore +7 -0
  31. package/template-source/_prettierrc.js +10 -0
  32. package/template-source/apps/.gitkeep +0 -0
  33. package/template-source/package.json +27 -0
  34. package/template-source/source.json +8 -0
  35. package/template-versions.json +6 -0
  36. package/template/CLAUDE.md +0 -95
  37. package/template/README.md +0 -70
@@ -1,61 +1,38 @@
1
1
  # The webapp SDK (`@bridgething/client`)
2
2
 
3
- A webapp reaches the daemon only through this one client: a typed facade over a
4
- local WebSocket. You never open sockets or build wire messages yourself.
5
-
6
- ## Contents
7
-
8
- - Read the types (the `.d.ts` files are the source of truth)
9
- - Connect
10
- - The three call shapes (events / requests / commands)
11
- - Cookbook (player, asset, store, config, net, library)
12
- - Full surface index (all 18)
13
- - Gotchas
14
-
15
3
  ## Read the types
16
4
 
17
- You have no editor to hover in, so read the shipped declarations directly. Every
18
- method, event, and field carries a doc comment.
5
+ The shipped declarations carry a doc comment on every method, event, and field.
19
6
 
20
- - `node_modules/@bridgething/client/dist/dispatch.generated.d.ts` - every
21
- `client.<surface>` method and event, with argument and return types.
22
- - `node_modules/@bridgething/lib/dist/bindings/*.d.ts` - the payload and reply
23
- types (`PlayerState`, `MediaItem`, `NowPlayingUpdate`, `AssetGot`, ...) with
24
- per-field docs. `client.d.ts` is the webapp surface; `shared.ts` holds the
25
- shared data types.
26
-
27
- Useful greps:
7
+ - `node_modules/@bridgething/client/dist/dispatch.generated.d.ts` has every
8
+ `client.<surface>` method and event with its argument and return types.
9
+ - `node_modules/@bridgething/lib/dist/bindings/client.d.ts` has the request and
10
+ reply payloads. `shared.d.ts` has the data types.
28
11
 
29
12
  ```bash
30
13
  grep -n 'class PlayerSurface' -A40 node_modules/@bridgething/client/dist/dispatch.generated.d.ts
31
- grep -n 'export type PlayerState ' -A12 node_modules/@bridgething/lib/dist/bindings/shared.ts
14
+ grep -n 'export type PlayerState ' -A12 node_modules/@bridgething/lib/dist/bindings/shared.d.ts
32
15
  ```
33
16
 
34
- The `.d.ts` is the source of truth; this file is the map.
35
-
36
17
  ## Connect
37
18
 
38
19
  ```ts
39
20
  import { BridgethingClient } from '@bridgething/client';
40
21
 
41
- const client = new BridgethingClient(); // on device: talks to the local daemon
42
- // dev against a real device from your laptop:
43
- const client = new BridgethingClient({ url: import.meta.env.VITE_BRIDGETHING_URL });
22
+ import { daemonUrl } from './daemon';
23
+
24
+ const client = new BridgethingClient({ url: daemonUrl() });
44
25
  ```
45
26
 
46
- Construct once and reuse (a `useMemo` in React). It auto-connects and
47
- auto-reconnects. Watch the link with `client.on(e => ...)` (`e.type` is
48
- `open` / `close` / `connecting` / `message`) and read `client.connectionState`
49
- (`'connecting' | 'open' | 'closing' | 'closed'`). The daemon is always the sole
50
- peer; you never address anything.
27
+ Construct once and reuse, a `useMemo` in React. Watch the link with
28
+ `client.on(e => ...)`, where `e.type` is `open`, `close`, `connecting`, or
29
+ `message`, and read `client.connectionState`, one of
30
+ `'connecting' | 'open' | 'closing' | 'closed'`.
51
31
 
52
32
  ## The three call shapes
53
33
 
54
- Every surface method is one of three shapes. Learn these and the whole SDK reads
55
- the same way.
56
-
57
- **1. Events** - the daemon pushes; you subscribe. `onXxx` returns an unsubscribe
58
- function (call it in cleanup). Or `subscribe({...})` for several at once.
34
+ Events. `onXxx` returns an unsubscribe function to call in cleanup.
35
+ `subscribe({...})` takes several at once.
59
36
 
60
37
  ```ts
61
38
  const off = client.player.onSnapshot(reply => setState(reply.state));
@@ -63,8 +40,7 @@ const off = client.player.onSnapshot(reply => setState(reply.state));
63
40
  off();
64
41
  ```
65
42
 
66
- **2. Requests** - you ask, the daemon answers. Returns a tagged result; always
67
- check `.ok`.
43
+ Requests. Check `.ok` on every result.
68
44
 
69
45
  ```ts
70
46
  const res = await client.player.stateGet();
@@ -72,8 +48,8 @@ if (res.ok) console.log(res.response.state);
72
48
  else console.warn(res.kind, res.error); // kind: 'domain' | 'protocol'
73
49
  ```
74
50
 
75
- **3. Commands** - fire-and-forget. Returns `Promise<void>`; it resolves when the
76
- daemon has taken the message, not when the phone finished acting.
51
+ Commands. The returned `Promise<void>` resolves when the daemon has taken the
52
+ message, not when the phone finished acting.
77
53
 
78
54
  ```ts
79
55
  await client.player.skipNext();
@@ -81,31 +57,31 @@ await client.player.skipNext();
81
57
 
82
58
  ## Cookbook
83
59
 
84
- **Now playing + transport (`client.player`)**
60
+ `client.player`:
85
61
 
86
62
  ```ts
87
63
  client.player.onSnapshot(r => setState(r.state)); // full PlayerState on every material change
88
64
  client.player.stateGet().then(r => r.ok && setState(r.response.state)); // prime on mount
89
65
 
90
- // PlayerState: { track?: MediaItem, playback: Playback, queue, options, context? }
66
+ // PlayerState: { track: MediaItem | null, playback: Playback, queue, options, context }
91
67
  // track.title / track.artist / track.album / track.artworkId / track.durationMs
92
68
  // playback.state: 'stopped' | 'paused' | 'playing'; playback.positionMs; playback.shuffle
93
69
 
94
70
  client.player.play({ uri: 'spotify:track:...' });
95
- client.player.pause(); client.player.resume(); client.player.skipNext();
96
- client.player.skipPrev({ allowSeeking: true }); // true = restart if progressed
71
+ client.player.pause();
72
+ client.player.resume();
73
+ client.player.skipNext();
74
+ client.player.skipPrev({ allowSeeking: true }); // true restarts the track if it is progressed
97
75
  client.player.seekTo({ positionMs: 30_000 });
98
76
  client.player.setShuffle({ on: true });
99
- client.player.setRepeat({ mode: 'all' }); // 'off' | 'all' | 'one'
77
+ client.player.setRepeat({ mode: 'all' }); // 'off' | 'all' | 'one'
100
78
  ```
101
79
 
102
- Smooth progress: `onSnapshot` fires on material changes (track change, play/pause,
103
- seek), NOT every second. Extrapolate the playhead locally from `playback.state`
104
- and `positionMs` between snapshots.
80
+ `onSnapshot` fires on material changes such as a track change, play, pause, and
81
+ seek. For a smooth progress bar, extrapolate the playhead between snapshots from
82
+ `playback.state` and `playback.positionMs`.
105
83
 
106
- **Artwork and images (`client.asset`)**
107
-
108
- Player state gives opaque asset ids, never URLs. Fetch bytes and wrap a blob:
84
+ `client.asset`. Player state carries opaque asset ids:
109
85
 
110
86
  ```ts
111
87
  const res = await client.asset.get({ id: track.artworkId, requestId: crypto.randomUUID() });
@@ -116,16 +92,16 @@ if (res.ok) {
116
92
  }
117
93
  ```
118
94
 
119
- **Persistence (`client.store`)** - per-webapp key/value that survives restarts:
95
+ `client.store`, per-webapp key and value that survives restarts:
120
96
 
121
97
  ```ts
122
98
  await client.store.put({ key: 'theme', value: 'dark' });
123
- const r = await client.store.get({ key: 'theme' }); // r.ok && r.response.value
99
+ const r = await client.store.get({ key: 'theme' }); // r.ok && r.response.value
124
100
  await client.store.delete({ key: 'theme' });
125
101
  ```
126
102
 
127
- **User settings (`client.config`)** - values you declare in `manifest.json`; the
128
- user edits them in the companion app, your side is read-only:
103
+ `client.config`, the values declared in `manifest.json`. The companion app writes
104
+ them, the webapp reads them:
129
105
 
130
106
  ```ts
131
107
  client.config.onChanged(c => applySetting(c.key, c.value));
@@ -133,63 +109,68 @@ const r = await client.config.get({ key: 'units' });
133
109
  const all = await client.config.list();
134
110
  ```
135
111
 
136
- The user edits these in the companion phone app. The scaffold ships a
137
- companion-side settings page under `settings/` that reads and writes them via a
138
- separate SDK, `@bridgething/client/settings` (a `postMessage` bridge to the
139
- companion host, not this WebSocket). `manifest.json`'s `settings` field points
140
- the companion at the built `dist/settings.html`.
112
+ The settings page under `settings/` reads and writes them through
113
+ `@bridgething/client/settings`, a `postMessage` bridge. It runs on the phone with
114
+ real internet, but loads from a `file://` origin and the webview enforces CORS on
115
+ fetch and XHR. WebSocket APIs work. Plain HTTP APIs work when the server sends
116
+ permissive CORS headers, since requests arrive with `Origin: null`. For a
117
+ CORS-strict HTTP-only service, fetch through `client.net` instead.
118
+
119
+ Write it in react like the rest of the app. `react` and `react-dom/client`
120
+ imports, `className`, `htmlFor`, react's event types. The build runs it on
121
+ preact through `@preact/preset-vite`, which is most of what keeps the page
122
+ small, so keep the react-flavoured source and leave the preset alone. One
123
+ tsconfig covers `src`, `settings` and `extension`, so `bun run typecheck`
124
+ checks all three.
141
125
 
142
- NETWORK CAVEAT for the settings page: it runs on the phone with real internet,
143
- but it loads from a `file://` origin and the webview enforces CORS on
144
- fetch/XHR. WebSocket APIs work (the WS handshake is not CORS-gated); plain HTTP
145
- APIs work only when the server sends permissive CORS headers (requests arrive
146
- with `Origin: null`). Prefer the service's websocket API from the settings
147
- page; for a CORS-strict HTTP-only service, fetch from the device webapp via
148
- `client.net` (phone-tunneled, not origin-restricted) and keep the settings page
149
- for typing and choosing.
126
+ Keep the page small. It builds to one self-contained HTML file, and the device
127
+ refuses to install a bundle whose declared settings page is over 1 MiB, failing
128
+ the whole install with an invalid-manifest error. The build warns past 200 KiB
129
+ and again past 500 KiB. A heavy dependency in `settings/` is the usual way
130
+ there.
150
131
 
151
- **Internet through the phone (`client.net`)** - the device has no direct network;
152
- `net.fetch` / websockets / a SOCKS proxy all tunnel through the phone. Needs the
153
- matching `permissions` entry in `manifest.json` (`net.fetch`, `net.ws`,
154
- `net.proxy`). Read the `NetSurface` block in the `.d.ts` for the request shapes.
132
+ `client.net` tunnels HTTP, websockets, and a SOCKS proxy through the phone. The
133
+ SOCKS proxy needs `"net.proxy"` in the manifest's `permissions`. Read the
134
+ `NetSurface` block in the `.d.ts` for the request shapes.
155
135
 
156
- **Library + search (`client.library`)** - `browse`, `search`, `recommendations`,
157
- `favoritesList`/`favoritesContains` (requests), `favoritesToggle`/`favoritesSet`
158
- (commands), `onFavoriteChanged` (event). All Spotify-backed.
136
+ `client.library` is Spotify-backed: `browse`, `search`, `recommendations`,
137
+ `favoritesList`, and `favoritesContains` are requests; `favoritesToggle` and
138
+ `favoritesSet` are commands; `onFavoriteChanged` is an event.
159
139
 
160
- ## Full surface index
140
+ ## Every surface
161
141
 
162
- 18 surfaces on `client.<name>`. Read each surface's class in
163
- `dispatch.generated.d.ts` for its exact methods and types.
142
+ Read a surface's class in `dispatch.generated.d.ts` for its exact methods.
164
143
 
165
144
  | Surface | What it does | Notable methods |
166
145
  | --- | --- | --- |
167
- | `player` | now-playing + transport | onSnapshot, stateGet, play, pause, resume, skipNext, skipPrev, seekTo, setShuffle, setRepeat, queueGet |
168
- | `asset` | fetch blobs by opaque id | get, preload, onReady/onCleared |
169
- | `store` | per-webapp key/value | get, put, delete |
146
+ | `player` | now-playing and transport | onSnapshot, stateGet, play, pause, resume, skipNext, skipPrev, seekTo, setShuffle, setRepeat, queueGet |
147
+ | `asset` | fetch blobs by opaque id | get, preload, onReady, onCleared |
148
+ | `store` | per-webapp key and value | get, put, delete |
170
149
  | `config` | user settings from the manifest | get, list, onChanged |
171
- | `capabilities` | which gateway features are live | get, onSnapshot |
172
- | `library` | Spotify browse/search/favorites | browse, search, recommendations, favoritesList/Contains/Toggle/Set, onFavoriteChanged |
173
- | `audio` | volume, mute, TTS, earcons | volumeUp/Down, setVolume, muteToggle, tts, earcon |
174
- | `notifications` | phone notification actions | invokePositive/Negative, onPosted/Removed/Updated |
175
- | `phone` | call control (Android; iOS uses iAP2) | stateGet, accept, end, initiate, mute, dtmf, onCallStarted/Updated/Ended |
176
- | `geo` | location watches (phone-sourced) | watch, unwatch, getOnce, onPosition |
177
- | `net` | HTTP / WS / SOCKS via the phone | fetch, wsOpen/wsSend/wsClose, streamOpen/streamCancel |
178
- | `hardware` | backlight + ambient light sensor | displaySetMode, displaySetLevel, stateGet, onAmbientLightUpdate, onBrightnessChanged |
179
- | `bluetooth` | adapter alias/bonds/discoverable | list, connect, forget, setAlias, enableDiscoverable |
180
- | `system` | version, logs, power, diagnostics | versionRequest, logsTail/Subscribe, reboot, powerOff, factoryReset, onVersion, onLogEntry, onOta* |
181
- | `time` | wall clock (device has no RTC) | get, onSnapshot, onChanged |
182
- | `voice` | mic / push-to-talk (capability-gated) | pushToTalk, cancel, muteMic, stateGet |
183
- | `webapp` | launcher: list/activate installed apps | list, current, activate, icon |
184
- | `forward` | arbitrary passthrough to the companion | text, json, binary, onText/onJson/onBinary |
150
+ | `capabilities` | which companion features are live | get, onSnapshot |
151
+ | `library` | Spotify browse, search, favorites | browse, search, recommendations, favoritesList, favoritesContains, favoritesToggle, favoritesSet, onFavoriteChanged |
152
+ | `audio` | volume, mute, TTS, earcons | volumeUp, volumeDown, setVolume, muteToggle, tts, earcon |
153
+ | `notifications` | phone notification actions | invokePositive, invokeNegative, onPosted, onRemoved, onUpdated |
154
+ | `phone` | call control | stateGet, accept, end, initiate, mute, dtmf, onCallStarted, onCallUpdated, onCallEnded |
155
+ | `geo` | location watches, sourced from the phone | watch, unwatch, getOnce, onPosition |
156
+ | `net` | HTTP, WS, and SOCKS through the phone | fetch, wsOpen, wsSend, wsClose, streamOpen, streamCancel |
157
+ | `hardware` | backlight and ambient light sensor | displaySetMode, displaySetLevel, stateGet, onAmbientLightUpdate, onBrightnessChanged |
158
+ | `bluetooth` | adapter alias, bonds, discoverable | list, connect, forget, setAlias, enableDiscoverable |
159
+ | `system` | version, logs, power, diagnostics | versionRequest, logsTail, logsSubscribe, reboot, powerOff, factoryReset, onVersion, onLogEntry, onOtaProgress |
160
+ | `time` | wall clock | get, onSnapshot, onChanged |
161
+ | `voice` | mic and push-to-talk, capability-gated | pushToTalk, cancel, muteMic, stateGet |
162
+ | `webapp` | list and activate installed webapps | list, current, activate, icon |
163
+ | `forward` | passthrough to the desktop-side extension | text, json, binary, onText, onJson, onBinary |
164
+ | `doc` | per-webapp documents the companion also writes | get, list, set, delete, onChanged |
165
+ | `lyrics` | timed lyrics for the current track | get |
166
+ | `peer` | every known peer and its link state | onSnapshot |
185
167
 
186
168
  ## Gotchas
187
169
 
188
- - Always check `.ok` on request results; the daemon can answer with a domain or
189
- protocol error (e.g. `player.play` with a uri no gateway claims).
190
- - Assume no phone: player, library, net all depend on the connected companion.
191
- Render an empty/placeholder state, do not throw.
192
- - `config` is read-only from the webapp; only the companion writes it.
193
- - Asset ids are opaque - never parse or construct them, never build image URLs.
194
- - Some surfaces are capability-gated or platform-specific (e.g. `voice`, and
195
- `phone` on iOS). Check `client.capabilities` and handle absence.
170
+ - Check `.ok` on every request result. The daemon answers with a domain or
171
+ protocol error when it cannot serve the call, such as `player.play` with a uri
172
+ no companion claims.
173
+ - Write the app so it works with no phone connected. `player`, `library`, and
174
+ `net` all depend on the companion app.
175
+ - Treat asset ids as opaque. Pass them to `client.asset.get` as they arrive.
176
+ - Read `client.capabilities` before using `voice` and `phone`.
@@ -1,65 +1,46 @@
1
1
  # Installing and sharing the app
2
2
 
3
- Two outputs: install onto your own connected device, and a zip anyone can install
4
- through the bridgething companion app.
5
-
6
3
  ## Install onto a connected Car Thing
7
4
 
8
5
  ```bash
9
- bun run push # targets bridgething.local (device over USB)
10
- bun run push <address> # or an explicit host / IP
6
+ bun run push # targets bridgething.local (the device over USB)
7
+ bun run push <address> # or an explicit host or IP
11
8
  ```
12
9
 
13
- `push` builds `dist/` itself, rsyncs it to the device, then tells the daemon to
14
- switch the kiosk to your app - one command, no separate build step. The device
15
- must be reachable: connected over USB it answers to `bridgething.local`;
16
- otherwise pass its address. If multiple devices are on the network, set
10
+ `push` builds `dist/` itself, copies it to the device, then switches the kiosk to
11
+ your app. With several devices on the network, set
17
12
  `SUPERBIRD_HOST=bridgething-<serial>.local`.
18
13
 
19
- Flags: `--skip-build` reuses the existing `dist/`; `--no-switch` copies without
20
- activating. Iterating on hardware, rerun `bun run push` after each change. For
21
- the fast edit loop, drive a local browser instead (see the develop reference) and
22
- save the device for final checks.
14
+ Flags: `--skip-build` copies whatever is already in `dist/`, and `--no-switch`
15
+ copies without activating. To iterate on hardware, use `bun run dev:device` from
16
+ the develop reference instead.
23
17
 
24
- If `manifest.json` declares `role: launcher` or an `overlay`, `push` also claims
25
- the matching device slot, so the bundle is live rather than merely installed. A
26
- launcher is switched to; an overlay is not, since switching away from the app it
27
- draws over defeats the point. `--no-slot` pushes without claiming, and
28
- `--release` gives the slots back to the built-in hub and overlay - the recovery
29
- path when a build wedges the screen.
18
+ When `manifest.json` declares `role: launcher` or an `overlay`, `push` also
19
+ claims the matching slot. A launcher is switched to; an overlay is not, so pass
20
+ `--switch` to open its own page. `--no-slot` pushes without claiming a slot.
21
+ `--release` gives the slots back to the built-in hub and overlay.
30
22
 
31
23
  ## Make a shareable zip
32
24
 
33
25
  ```bash
34
- bun run build # share does NOT build; produce dist/ first
26
+ bun run build # share reads dist/, so produce it first
35
27
  bun run share # writes <name>-<version>.zip from dist/
36
28
  ```
37
29
 
38
- Send that zip to anyone with a bridgething-flashed Car Thing. They install it
39
- from the bridgething companion app (no rebuild, no dev tools). The zip is just
40
- your built bundle with `manifest.json` at its root. `bun run build` also emits
41
- `dist/settings.html` (the companion-side settings page); it ships in the same
42
- bundle when `manifest.json` declares `settings`.
30
+ Anyone with a bridgething Car Thing installs that zip from the companion app.
43
31
 
44
- Bump `version` in `public/manifest.json` before sharing an update, so people can
45
- tell builds apart. Never touch `manifest.json`'s `id` - it is the webapp's
46
- identity and the install path on the device.
32
+ Raise `version` in `public/manifest.json` before you share an update. Keep `id`.
47
33
 
48
34
  ## Keep the device on the latest bridgething
49
35
 
50
36
  ```bash
51
- bun run update # updates the connected Car Thing's daemon + image
37
+ bun run update # updates the connected Car Thing's daemon and image
52
38
  ```
53
39
 
54
- `update` runs `@bridgething/updater`: it connects over the same gateway `push`
55
- uses, reads the release manifest, and brings the device to the latest `stable`
56
- release.
57
-
58
- Multiple devices? Target one explicitly:
40
+ Flags go after `--`:
59
41
 
60
42
  ```bash
61
- bun run update -- --host ws://bridgething-<serial>.local:8892/
43
+ bun run update -- --host ws://bridgething-<serial>.local:8892/ # pick one of several devices
44
+ bun run update -- --channel <name> # track another channel
45
+ bun run update -- --version <composite-version> # install a specific release
62
46
  ```
63
-
64
- Other flags: `-- --channel <name>` to track a non-stable channel,
65
- `-- --daemon-only` to skip the image OTA.
@@ -1,5 +1,6 @@
1
1
  node_modules
2
2
  dist
3
+ .dev-extension
3
4
  .DS_Store
4
5
  *.log
5
6
  .env*
@@ -5,14 +5,15 @@
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "vite",
8
+ "dev:device": "vite --mode device",
8
9
  "build": "vite build && vite build -c vite.settings.config.ts",
10
+ "typecheck": "tsc --noEmit",
9
11
  "preview": "vite preview",
10
12
  "push": "bun scripts/push.ts",
11
13
  "share": "bun scripts/share.ts",
12
14
  "update": "bunx @bridgething/updater"
13
15
  },
14
16
  "dependencies": {
15
- "@bridgething/client": "^0.11.0",
16
17
  "preact": "^10.29.7",
17
18
  "react": "^19.2.0",
18
19
  "react-dom": "^19.2.0"
@@ -20,14 +21,14 @@
20
21
  "devDependencies": {
21
22
  "@msgpack/msgpack": "^3.1.3",
22
23
  "@preact/preset-vite": "^2.10.5",
23
- "@tailwindcss/vite": "^4.1.16",
24
+ "@tailwindcss/vite": "^4.3.3",
24
25
  "@types/react": "^19.2.10",
25
26
  "@types/react-dom": "^19.2.3",
26
- "@vitejs/plugin-react": "^5.1.1",
27
+ "@vitejs/plugin-react": "^6.1.1",
27
28
  "fflate": "^0.8.2",
28
- "tailwindcss": "^4.1.16",
29
+ "tailwindcss": "^4.3.3",
29
30
  "typescript": "^6.0.3",
30
- "vite": "^7.1.13",
31
+ "vite": "^8.2.2",
31
32
  "vite-plugin-singlefile": "^2.3.3"
32
33
  }
33
34
  }