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.
- package/README.md +16 -12
- package/dist/index.mjs +476 -142
- package/package.json +20 -5
- package/template/_claude/skills/bridgething/SKILL.md +12 -17
- package/template/_claude/skills/bridgething/reference/develop.md +86 -66
- package/template/_claude/skills/bridgething/reference/extension.md +183 -0
- package/template/_claude/skills/bridgething/reference/sdk.md +86 -105
- package/template/_claude/skills/bridgething/reference/ship.md +19 -38
- package/template/_gitignore +1 -0
- package/template/package.json +6 -5
- package/template/scripts/bridgething.ts +1086 -0
- package/template/scripts/push.ts +212 -193
- package/template/settings/main.tsx +14 -13
- package/template/src/App.tsx +3 -6
- package/template/src/daemon.ts +14 -0
- package/template/tsconfig.json +1 -1
- package/template/vite.config.ts +5 -3
- package/template/vite.settings.config.ts +3 -3
- package/template-extension/_claude_append.md +23 -0
- package/template-extension/extension/main.ts +20 -0
- package/template-launcher/_claude_append.md +14 -30
- package/template-launcher/src/App.tsx +2 -5
- package/template-overlay/_claude_append.md +31 -45
- package/template-overlay/vite.overlay.config.ts +1 -1
- package/template-source/CLAUDE.md +104 -0
- package/template-source/README.md +57 -0
- package/template-source/_bunversion +1 -0
- package/template-source/_github/workflows/ci.yml +28 -0
- package/template-source/_github/workflows/publish.yml +33 -0
- package/template-source/_gitignore +7 -0
- package/template-source/_prettierrc.js +10 -0
- package/template-source/apps/.gitkeep +0 -0
- package/template-source/package.json +27 -0
- package/template-source/source.json +8 -0
- package/template-versions.json +6 -0
- package/template/CLAUDE.md +0 -95
- 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
|
-
|
|
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`
|
|
21
|
-
`client.<surface>` method and event
|
|
22
|
-
- `node_modules/@bridgething/lib/dist/bindings
|
|
23
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
const client = new BridgethingClient({ url:
|
|
22
|
+
import { daemonUrl } from './daemon';
|
|
23
|
+
|
|
24
|
+
const client = new BridgethingClient({ url: daemonUrl() });
|
|
44
25
|
```
|
|
45
26
|
|
|
46
|
-
Construct once and reuse
|
|
47
|
-
|
|
48
|
-
`
|
|
49
|
-
|
|
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
|
-
|
|
55
|
-
|
|
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
|
-
|
|
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
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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
|
|
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();
|
|
96
|
-
client.player.
|
|
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' });
|
|
77
|
+
client.player.setRepeat({ mode: 'all' }); // 'off' | 'all' | 'one'
|
|
100
78
|
```
|
|
101
79
|
|
|
102
|
-
|
|
103
|
-
seek
|
|
104
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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' });
|
|
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
|
-
|
|
128
|
-
|
|
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
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
-
|
|
157
|
-
`favoritesList
|
|
158
|
-
|
|
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
|
-
##
|
|
140
|
+
## Every surface
|
|
161
141
|
|
|
162
|
-
|
|
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
|
|
168
|
-
| `asset` | fetch blobs by opaque id | get, preload, onReady
|
|
169
|
-
| `store` | per-webapp key
|
|
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
|
|
172
|
-
| `library` | Spotify browse
|
|
173
|
-
| `audio` | volume, mute, TTS, earcons | volumeUp
|
|
174
|
-
| `notifications` | phone notification actions | invokePositive
|
|
175
|
-
| `phone` | call control
|
|
176
|
-
| `geo` | location watches
|
|
177
|
-
| `net` | HTTP
|
|
178
|
-
| `hardware` | backlight
|
|
179
|
-
| `bluetooth` | adapter alias
|
|
180
|
-
| `system` | version, logs, power, diagnostics | versionRequest, logsTail
|
|
181
|
-
| `time` | wall clock
|
|
182
|
-
| `voice` | mic
|
|
183
|
-
| `webapp` |
|
|
184
|
-
| `forward` |
|
|
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
|
-
-
|
|
189
|
-
protocol error
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
-
|
|
194
|
-
-
|
|
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
|
|
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,
|
|
14
|
-
|
|
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`
|
|
20
|
-
activating.
|
|
21
|
-
the
|
|
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
|
-
|
|
25
|
-
the matching
|
|
26
|
-
|
|
27
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
37
|
+
bun run update # updates the connected Car Thing's daemon and image
|
|
52
38
|
```
|
|
53
39
|
|
|
54
|
-
|
|
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.
|
package/template/_gitignore
CHANGED
package/template/package.json
CHANGED
|
@@ -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.
|
|
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": "^
|
|
27
|
+
"@vitejs/plugin-react": "^6.1.1",
|
|
27
28
|
"fflate": "^0.8.2",
|
|
28
|
-
"tailwindcss": "^4.
|
|
29
|
+
"tailwindcss": "^4.3.3",
|
|
29
30
|
"typescript": "^6.0.3",
|
|
30
|
-
"vite": "^
|
|
31
|
+
"vite": "^8.2.2",
|
|
31
32
|
"vite-plugin-singlefile": "^2.3.3"
|
|
32
33
|
}
|
|
33
34
|
}
|