create-bridgething 0.10.0 → 0.12.0
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 +86 -21
- package/package.json +9 -3
- package/template/CLAUDE.md +60 -84
- package/template/README.md +31 -44
- package/template/_claude/skills/bridgething/SKILL.md +12 -17
- package/template/_claude/skills/bridgething/reference/develop.md +84 -65
- package/template/_claude/skills/bridgething/reference/extension.md +183 -0
- package/template/_claude/skills/bridgething/reference/sdk.md +74 -106
- 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/src/App.tsx +2 -5
- package/template/src/daemon.ts +14 -0
- package/template/vite.config.ts +5 -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/README.md
CHANGED
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
# create-bridgething
|
|
2
2
|
|
|
3
|
-
Scaffold a
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
[`@bridgething/client`](https://www.npmjs.com/package/@bridgething/client)
|
|
7
|
-
preinstalled.
|
|
3
|
+
Scaffold a [bridgething](https://github.com/JoeyEamigh/bridgething) webapp for
|
|
4
|
+
the Spotify Car Thing: React, Vite, Tailwind v4, and
|
|
5
|
+
[`@bridgething/client`](https://www.npmjs.com/package/@bridgething/client).
|
|
8
6
|
|
|
9
7
|
```sh
|
|
10
8
|
bun create bridgething my-app
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
| flag | what you get |
|
|
12
|
+
| -------------- | --------------------------------------------- |
|
|
13
|
+
| `--launcher` | A replacement home screen |
|
|
14
|
+
| `--overlay` | System UI drawn over every webapp |
|
|
15
|
+
| `--extension` | A desktop-side Deno process beside the webapp |
|
|
16
|
+
| `--no-install` | Skip `bun install` |
|
|
17
|
+
| `--no-git` | Skip `git init` |
|
|
18
|
+
|
|
19
|
+
`--extension` combines with `--launcher`, `--overlay`, or a plain webapp.
|
|
16
20
|
|
|
17
|
-
The
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
The project ships `bun run dev`, `build`, `push` (install on a connected Car
|
|
22
|
+
Thing), `share` (zip for the store), and `update` (bring the device to the
|
|
23
|
+
latest release).
|
|
20
24
|
|
|
21
|
-
-
|
|
25
|
+
- Docs: <https://bridgething.com/docs>
|
|
22
26
|
- Source: <https://github.com/JoeyEamigh/bridgething>
|
package/dist/index.mjs
CHANGED
|
@@ -7,6 +7,29 @@ import { v7 } from "uuid";
|
|
|
7
7
|
//#region src/index.ts
|
|
8
8
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
9
|
const TEMPLATE_DIR = resolve(__dirname, "..", "template");
|
|
10
|
+
const EXTENSION = {
|
|
11
|
+
dir: "template-extension",
|
|
12
|
+
manifest: {
|
|
13
|
+
extension: {
|
|
14
|
+
entry: "extension/desktop.mjs",
|
|
15
|
+
permissions: ["all"],
|
|
16
|
+
api: 1
|
|
17
|
+
},
|
|
18
|
+
config: [{
|
|
19
|
+
type: "string",
|
|
20
|
+
data: {
|
|
21
|
+
key: "greeting",
|
|
22
|
+
label: "Greeting the extension sends on connect"
|
|
23
|
+
}
|
|
24
|
+
}]
|
|
25
|
+
},
|
|
26
|
+
dependencies: { "@bridgething/extension": `^${JSON.parse(readFileSync(resolve(__dirname, "..", "package.json"), "utf8")).version}` },
|
|
27
|
+
devDependencies: {
|
|
28
|
+
deno: "2.9.6",
|
|
29
|
+
esbuild: "^0.28.2"
|
|
30
|
+
},
|
|
31
|
+
summary: "native extension (a desktop-side Deno process)"
|
|
32
|
+
};
|
|
10
33
|
const VARIANTS = {
|
|
11
34
|
app: { summary: "webapp" },
|
|
12
35
|
launcher: {
|
|
@@ -32,10 +55,12 @@ function parseArgs(argv) {
|
|
|
32
55
|
let install = true;
|
|
33
56
|
let git = true;
|
|
34
57
|
let variant = "app";
|
|
58
|
+
let extension = false;
|
|
35
59
|
for (const arg of argv) if (arg === "--no-install") install = false;
|
|
36
60
|
else if (arg === "--no-git") git = false;
|
|
37
61
|
else if (arg === "--launcher") variant = "launcher";
|
|
38
62
|
else if (arg === "--overlay") variant = "overlay";
|
|
63
|
+
else if (arg === "--extension") extension = true;
|
|
39
64
|
else if (arg === "--help" || arg === "-h") {
|
|
40
65
|
printHelp();
|
|
41
66
|
process.exit(0);
|
|
@@ -52,11 +77,12 @@ function parseArgs(argv) {
|
|
|
52
77
|
target: positional[0],
|
|
53
78
|
install,
|
|
54
79
|
git,
|
|
55
|
-
variant
|
|
80
|
+
variant,
|
|
81
|
+
extension
|
|
56
82
|
};
|
|
57
83
|
}
|
|
58
84
|
function printHelp() {
|
|
59
|
-
console.log(`Usage: create-bridgething <target-dir> [--launcher | --overlay] [--no-install] [--no-git]
|
|
85
|
+
console.log(`Usage: create-bridgething <target-dir> [--launcher | --overlay] [--extension] [--no-install] [--no-git]
|
|
60
86
|
|
|
61
87
|
Scaffold a new bridgething webapp at <target-dir>.
|
|
62
88
|
|
|
@@ -67,6 +93,10 @@ Variants:
|
|
|
67
93
|
--overlay A system overlay. Ships an 'overlay.js' the daemon injects
|
|
68
94
|
into every webapp, replacing the built-in one.
|
|
69
95
|
|
|
96
|
+
Add-ons (combine with any variant):
|
|
97
|
+
--extension A native extension: a Deno process the desktop app runs
|
|
98
|
+
alongside the webapp, with host access the manifest declares.
|
|
99
|
+
|
|
70
100
|
Options:
|
|
71
101
|
--no-install Skip 'bun install' after copying.
|
|
72
102
|
--no-git Skip 'git init' after copying.
|
|
@@ -79,23 +109,48 @@ function patchJson(path, patch) {
|
|
|
79
109
|
...patch
|
|
80
110
|
}, null, 2)}\n`);
|
|
81
111
|
}
|
|
112
|
+
function patchPackage(target, patch) {
|
|
113
|
+
const path = join(target, "package.json");
|
|
114
|
+
const pkg = JSON.parse(readFileSync(path, "utf8"));
|
|
115
|
+
patchJson(path, {
|
|
116
|
+
scripts: {
|
|
117
|
+
...pkg.scripts,
|
|
118
|
+
...patch.scripts
|
|
119
|
+
},
|
|
120
|
+
dependencies: sortKeys({
|
|
121
|
+
...pkg.dependencies,
|
|
122
|
+
...patch.dependencies
|
|
123
|
+
}),
|
|
124
|
+
devDependencies: sortKeys({
|
|
125
|
+
...pkg.devDependencies,
|
|
126
|
+
...patch.devDependencies
|
|
127
|
+
})
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
function sortKeys(record) {
|
|
131
|
+
return Object.fromEntries(Object.entries(record).sort(([a], [b]) => a.localeCompare(b)));
|
|
132
|
+
}
|
|
133
|
+
function overlayTemplate(target, dir, subs) {
|
|
134
|
+
copyTemplate(resolve(__dirname, "..", dir), target, subs);
|
|
135
|
+
const appendPath = join(target, "_claude_append.md");
|
|
136
|
+
if (!existsSync(appendPath)) return;
|
|
137
|
+
const claudePath = join(target, "CLAUDE.md");
|
|
138
|
+
writeFileSync(claudePath, `${readFileSync(claudePath, "utf8").trimEnd()}\n\n${readFileSync(appendPath, "utf8")}`);
|
|
139
|
+
rmSync(appendPath);
|
|
140
|
+
}
|
|
82
141
|
function applyVariant(target, variant, subs) {
|
|
83
142
|
const spec = VARIANTS[variant];
|
|
84
|
-
if (spec.dir)
|
|
85
|
-
const appendPath = join(target, "_claude_append.md");
|
|
86
|
-
if (existsSync(appendPath)) {
|
|
87
|
-
const claudePath = join(target, "CLAUDE.md");
|
|
88
|
-
writeFileSync(claudePath, readFileSync(claudePath, "utf8") + readFileSync(appendPath, "utf8"));
|
|
89
|
-
rmSync(appendPath);
|
|
90
|
-
}
|
|
143
|
+
if (spec.dir) overlayTemplate(target, spec.dir, subs);
|
|
91
144
|
if (spec.manifest) patchJson(join(target, "public", "manifest.json"), spec.manifest);
|
|
92
|
-
if (spec.scripts) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
145
|
+
if (spec.scripts) patchPackage(target, { scripts: spec.scripts });
|
|
146
|
+
}
|
|
147
|
+
function applyExtension(target, subs) {
|
|
148
|
+
overlayTemplate(target, EXTENSION.dir, subs);
|
|
149
|
+
patchJson(join(target, "public", "manifest.json"), EXTENSION.manifest);
|
|
150
|
+
patchPackage(target, {
|
|
151
|
+
dependencies: EXTENSION.dependencies,
|
|
152
|
+
devDependencies: EXTENSION.devDependencies
|
|
153
|
+
});
|
|
99
154
|
}
|
|
100
155
|
const BINARY_EXT = new Set([
|
|
101
156
|
".ttf",
|
|
@@ -168,7 +223,9 @@ function main() {
|
|
|
168
223
|
console.log(`scaffolding ${projectName} (${webappUuid}) in ${target}`);
|
|
169
224
|
copyTemplate(TEMPLATE_DIR, target, subs);
|
|
170
225
|
applyVariant(target, args.variant, subs);
|
|
171
|
-
|
|
226
|
+
if (args.extension) applyExtension(target, subs);
|
|
227
|
+
const shape = args.extension ? `${VARIANTS[args.variant].summary} + ${EXTENSION.summary}` : VARIANTS[args.variant].summary;
|
|
228
|
+
console.log(` ✓ template copied (${shape})`);
|
|
172
229
|
linkAgentAliases(target);
|
|
173
230
|
console.log(" ✓ agent guides linked (CLAUDE.md, AGENTS.md, /bridgething skill)");
|
|
174
231
|
if (args.git) if (run("git", ["init", "--quiet"], target)) console.log(" ✓ git initialized");
|
|
@@ -182,7 +239,8 @@ function main() {
|
|
|
182
239
|
Done! Next steps:
|
|
183
240
|
|
|
184
241
|
cd ${args.target}
|
|
185
|
-
${args.install ? "" : " bun install\n"} bun run dev # local dev server (http://localhost:5173/)
|
|
242
|
+
${args.install ? "" : " bun install\n"} bun run dev # local dev server (http://localhost:5173/) against the connected Car Thing
|
|
243
|
+
bun run dev:device # the same server on the Car Thing's own screen, hot reload included
|
|
186
244
|
|
|
187
245
|
Open this folder with your coding agent (Claude Code, Codex, opencode, ...).
|
|
188
246
|
It reads CLAUDE.md / AGENTS.md and the /bridgething skill in .claude/skills
|
|
@@ -193,14 +251,21 @@ driving the app, and installing and sharing it.
|
|
|
193
251
|
bun run push <addr> # build + install onto a connected Car Thing (default bridgething.local)
|
|
194
252
|
bun run share # build first, then zip dist/ to hand to friends
|
|
195
253
|
bun run update # bring the connected Car Thing to the latest bridgething release
|
|
196
|
-
${args.
|
|
254
|
+
${args.extension ? `
|
|
255
|
+
'extension/main.ts' is the desktop-side half: a Deno process the bridgething
|
|
256
|
+
desktop app runs alongside this webapp, with the host access 'manifest.json'
|
|
257
|
+
declares. 'bun run dev' and 'bun run dev:device' also run it, rebuilt on every
|
|
258
|
+
save and wired to the connected Car Thing, so both halves iterate together.
|
|
259
|
+
'bun run build' bundles it to dist/extension/desktop.mjs.
|
|
260
|
+
` : ""}${args.variant === "app" ? "" : `
|
|
197
261
|
'bun run push' also claims the device's ${args.variant} slot, so this is live the
|
|
198
262
|
moment it lands. 'bun run push --release' hands the slot back to the built-in
|
|
199
263
|
${args.variant === "launcher" ? "hub" : "overlay"}, which is how you recover if a build of this wedges the screen.
|
|
200
264
|
The companion phone app can do the same thing.
|
|
201
265
|
`}
|
|
202
|
-
The starter App connects to
|
|
203
|
-
|
|
266
|
+
The starter App connects to the daemon through daemonUrl() in src/daemon.ts: the
|
|
267
|
+
local daemon on the device, and the dev server's proxy to the Car Thing over USB
|
|
268
|
+
under bun run dev (SUPERBIRD_HOST picks another device).
|
|
204
269
|
`);
|
|
205
270
|
}
|
|
206
271
|
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-bridgething",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Scaffold a new bridgething webapp (React + Vite + Tailwind v4)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,18 +25,24 @@
|
|
|
25
25
|
"files": [
|
|
26
26
|
"dist",
|
|
27
27
|
"template",
|
|
28
|
+
"template-extension",
|
|
28
29
|
"template-launcher",
|
|
29
30
|
"template-overlay"
|
|
30
31
|
],
|
|
31
32
|
"scripts": {
|
|
32
|
-
"prebuild": "bun scripts/sync-
|
|
33
|
+
"prebuild": "bun scripts/sync-shared.ts",
|
|
33
34
|
"build": "tsdown",
|
|
34
35
|
"dev": "tsdown --watch",
|
|
35
|
-
"typecheck": "tsc --noEmit"
|
|
36
|
+
"typecheck": "tsc --noEmit",
|
|
37
|
+
"test": "bun test"
|
|
36
38
|
},
|
|
37
39
|
"devDependencies": {
|
|
40
|
+
"@bridgething/extension": "0.12.0",
|
|
41
|
+
"@msgpack/msgpack": "^3.1.3",
|
|
42
|
+
"@types/bun": "^1.4.0",
|
|
38
43
|
"@types/node": "^26.1.1",
|
|
39
44
|
"@types/uuid": "^11.0.0",
|
|
45
|
+
"esbuild": "^0.28.2",
|
|
40
46
|
"tsdown": "^0.21.10"
|
|
41
47
|
},
|
|
42
48
|
"peerDependencies": {
|
package/template/CLAUDE.md
CHANGED
|
@@ -1,95 +1,71 @@
|
|
|
1
1
|
# Building this bridgething webapp
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
Spotify Car Thing. It
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
- **No keyboard, no mouse.** Those five controls plus touch are the whole input
|
|
27
|
-
surface. There is no text entry unless you build an on-screen one.
|
|
28
|
-
|
|
29
|
-
## The SDK: `@bridgething/client`
|
|
3
|
+
A bridgething webapp is one page running full-screen in the chromium kiosk on a
|
|
4
|
+
Spotify Car Thing. It reaches the on-device daemon through `@bridgething/client`.
|
|
5
|
+
|
|
6
|
+
## The device
|
|
7
|
+
|
|
8
|
+
The screen is 800x480, landscape, and never resizes. The kiosk shows one webapp,
|
|
9
|
+
so build in-app views rather than tabs or windows. Add an on-screen keyboard if
|
|
10
|
+
the app needs text entry.
|
|
11
|
+
|
|
12
|
+
Listen with a `keydown` handler and a `wheel` handler on `window`.
|
|
13
|
+
|
|
14
|
+
| Control | Event |
|
|
15
|
+
| ------------ | ------------------------------------- |
|
|
16
|
+
| Preset 1-4 | `keydown` key `"1"` `"2"` `"3"` `"4"` |
|
|
17
|
+
| Mode | `keydown` key `"m"` |
|
|
18
|
+
| Back | `keydown` key `"Escape"` |
|
|
19
|
+
| Rotary wheel | `wheel` with horizontal `deltaX` |
|
|
20
|
+
| Touch | pointer and touch events |
|
|
21
|
+
|
|
22
|
+
Make horizontal wheel scroll move through the main list. Five fast presses of
|
|
23
|
+
Mode returns to the launcher.
|
|
24
|
+
|
|
25
|
+
## The client
|
|
30
26
|
|
|
31
27
|
```ts
|
|
32
28
|
import { BridgethingClient } from '@bridgething/client';
|
|
33
|
-
|
|
29
|
+
import { useMemo } from 'react';
|
|
30
|
+
|
|
31
|
+
import { daemonUrl } from './daemon';
|
|
32
|
+
|
|
33
|
+
const client = useMemo(() => new BridgethingClient({ url: daemonUrl() }), []);
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
Construct it once and reuse it. It
|
|
37
|
-
|
|
38
|
-
already does this). Do not hardcode a WebSocket URL.
|
|
39
|
-
|
|
40
|
-
It is fully typed with a doc comment on every surface, method, and field. Read
|
|
41
|
-
those declarations directly - you have no editor to hover in:
|
|
42
|
-
|
|
43
|
-
- `node_modules/@bridgething/client/dist/dispatch.generated.d.ts` - every
|
|
44
|
-
`client.<surface>.<method>` and event, with docs.
|
|
45
|
-
- `node_modules/@bridgething/lib/dist/bindings/*.d.ts` - the payload and reply
|
|
46
|
-
types (`PlayerState`, `MediaItem`, ...), with per-field docs.
|
|
47
|
-
|
|
48
|
-
18 surfaces: `player asset config store capabilities library audio notifications
|
|
49
|
-
phone geo net hardware bluetooth system time voice webapp forward`. Each method
|
|
50
|
-
is one of three shapes:
|
|
51
|
-
|
|
52
|
-
- **events**: `client.<s>.onXxx(handler)` returns an unsubscribe fn; or
|
|
53
|
-
`client.<s>.subscribe({ onXxx, ... })`.
|
|
54
|
-
- **requests**: `await client.<s>.xxx(req)` returns `{ ok: true, response }` or
|
|
55
|
-
`{ ok: false, kind, error }` - always check `.ok`.
|
|
56
|
-
- **commands**: `await client.<s>.xxx(payload)` returns `void` (fire-and-forget).
|
|
57
|
-
|
|
58
|
-
Now-playing and library data come from the **connected phone's** Spotify. Assume
|
|
59
|
-
a phone may not be connected: handle the empty/disconnected state gracefully (the
|
|
60
|
-
starter shows a "connect a phone" placeholder). Fetch artwork by opaque asset id
|
|
61
|
-
via `client.asset.get`; never build image URLs by hand.
|
|
62
|
-
|
|
63
|
-
Run `/bridgething` for the cookbook, the dev loop, and shipping; grep the `.d.ts`
|
|
64
|
-
files for a specific method or type.
|
|
65
|
-
|
|
66
|
-
## `manifest.json` (in `public/`)
|
|
67
|
-
|
|
68
|
-
- `id` - baked in at scaffold, unique to this project. **Never change it.**
|
|
69
|
-
- `name`, `version`, `description`, `icon` - cosmetic / store metadata.
|
|
70
|
-
- `config` - user-tunable settings (each becomes a `client.config` value).
|
|
71
|
-
- `permissions` - capabilities you need (e.g. `net.fetch`, `net.proxy`, `geo`).
|
|
72
|
-
- `art: { heroPx, thumbPx }` - the pixel sizes you want artwork delivered at.
|
|
73
|
-
- `settings` - one self-contained HTML file (built from `settings/`) the
|
|
74
|
-
companion phone app renders as this webapp's settings page. It talks to the
|
|
75
|
-
companion over `@bridgething/client/settings`, not the on-device SDK. Capped
|
|
76
|
-
at 1 MiB at install. It loads from a `file://` origin: websocket APIs work
|
|
77
|
-
from it, but plain HTTP fetch is CORS-gated (`Origin: null`) - see the
|
|
78
|
-
network caveat in the `/bridgething` skill's sdk reference.
|
|
36
|
+
Construct it once and reuse it. It connects and reconnects on its own. Call
|
|
37
|
+
`daemonUrl()` instead of a literal `ws://` address.
|
|
79
38
|
|
|
80
|
-
|
|
39
|
+
Now-playing and library data come from the phone's Spotify, so render a
|
|
40
|
+
placeholder when no phone is connected. Fetch artwork with `client.asset.get`
|
|
41
|
+
using the opaque id on the track.
|
|
42
|
+
|
|
43
|
+
Every surface: `player asset config store doc capabilities library audio
|
|
44
|
+
notifications phone peer geo net hardware bluetooth system time voice lyrics
|
|
45
|
+
webapp forward`. Each method is an event, a request, or a command.
|
|
46
|
+
|
|
47
|
+
Methods, types, and examples: `.claude/skills/bridgething/reference/sdk.md`.
|
|
81
48
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
- `
|
|
85
|
-
- `
|
|
86
|
-
- `
|
|
87
|
-
|
|
49
|
+
## `public/manifest.json`
|
|
50
|
+
|
|
51
|
+
- `id` identifies this webapp on the device. Keep it.
|
|
52
|
+
- `version` tells builds apart. Raise it before sharing an update.
|
|
53
|
+
- `config` declares the settings the companion app edits and `client.config`
|
|
54
|
+
reads.
|
|
55
|
+
- `permissions` grants `geo` and `net.proxy`.
|
|
56
|
+
- `art.heroPx` and `art.thumbPx` are the sizes artwork arrives at.
|
|
57
|
+
- `settings` names the page built from `settings/`, capped at 1 MiB. It talks to
|
|
58
|
+
the companion app through `@bridgething/client/settings`.
|
|
59
|
+
|
|
60
|
+
## Workflow
|
|
88
61
|
|
|
89
|
-
|
|
62
|
+
- `bun run dev` serves at `http://localhost:5173/` against the connected Car
|
|
63
|
+
Thing.
|
|
64
|
+
- `bun run dev:device` shows that server on the device's own screen.
|
|
65
|
+
- `bun run build` writes `dist/`.
|
|
66
|
+
- `bun run push` builds and installs onto the connected device.
|
|
67
|
+
- `bun run share` zips `dist/`.
|
|
68
|
+
- `bun run update` brings the device to the latest bridgething release.
|
|
90
69
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
- Assume a phone is always connected.
|
|
94
|
-
- Build image URLs by hand; fetch by opaque asset id.
|
|
95
|
-
- Rely on rapid repeated `m` (system launcher gesture).
|
|
70
|
+
Running and driving the app: `.claude/skills/bridgething/reference/develop.md`.
|
|
71
|
+
Push, zip, update: `.claude/skills/bridgething/reference/ship.md`.
|
package/template/README.md
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
# **PROJECT_NAME**
|
|
2
2
|
|
|
3
|
-
A bridgething webapp scaffolded with `create-bridgething`.
|
|
3
|
+
A bridgething webapp scaffolded with `create-bridgething`. It runs full-screen in
|
|
4
|
+
the chromium kiosk on a Spotify Car Thing and talks to the on-device daemon
|
|
5
|
+
through [`@bridgething/client`](https://github.com/JoeyEamigh/bridgething).
|
|
4
6
|
|
|
5
|
-
Stack: React 19
|
|
6
|
-
[`@bridgething/client`](https://github.com/JoeyEamigh/bridgething) preinstalled.
|
|
7
|
+
Stack: React 19, Vite, Tailwind v4, TypeScript strict.
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
Open this folder with your coding agent (Claude Code, Codex, opencode, ...). It
|
|
11
|
-
reads `CLAUDE.md` / `AGENTS.md` and the `/bridgething` skill.
|
|
9
|
+
Open this folder with your coding agent. It reads `CLAUDE.md` or `AGENTS.md` and
|
|
10
|
+
the `/bridgething` skill.
|
|
12
11
|
|
|
13
12
|
## Develop
|
|
14
13
|
|
|
@@ -17,54 +16,42 @@ bun install
|
|
|
17
16
|
bun run dev
|
|
18
17
|
```
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
Vite serves at http://localhost:5173/ with hot reload, against the daemon on the
|
|
20
|
+
Car Thing plugged in over USB. Set `SUPERBIRD_HOST` to target another device, or
|
|
21
|
+
`BRIDGETHING_DAEMON_URL` to reach a daemon elsewhere.
|
|
23
22
|
|
|
24
23
|
```bash
|
|
25
|
-
|
|
24
|
+
bun run dev:device
|
|
26
25
|
```
|
|
27
26
|
|
|
28
|
-
|
|
27
|
+
The same server, shown on the Car Thing's own screen, hot reload included.
|
|
28
|
+
Ctrl-C hands the screen back to the installed build.
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
When the project ships an extension (`extension/main.ts`), both commands bundle
|
|
31
|
+
it on every save and run it under Deno with the permissions the manifest
|
|
32
|
+
declares.
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
on the Car Thing, and switches the kiosk to your app. Pass an IP address to target
|
|
36
|
-
something other than `bridgething.local` over USB.
|
|
37
|
-
|
|
38
|
-
## Share
|
|
34
|
+
## Push, share, update
|
|
39
35
|
|
|
40
36
|
```bash
|
|
37
|
+
bun run push # build, copy to the Car Thing, switch the kiosk to it
|
|
41
38
|
bun run build
|
|
42
|
-
bun run share
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
`share` writes `<name>-<version>.zip` from `dist/`. Anyone with a bridgething Car
|
|
46
|
-
Thing installs it from the app.
|
|
47
|
-
|
|
48
|
-
## Update the device
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
bun run update
|
|
39
|
+
bun run share # write <name>-<version>.zip from dist/
|
|
40
|
+
bun run update # bring the device to the latest bridgething release
|
|
52
41
|
```
|
|
53
42
|
|
|
54
|
-
`
|
|
55
|
-
|
|
43
|
+
`push` targets `bridgething.local` over USB; pass a host or IP address for
|
|
44
|
+
another device. Anyone with a bridgething Car Thing installs the zip from the
|
|
45
|
+
companion app. For one device out of several,
|
|
46
|
+
`bun run update -- --host ws://bridgething-<serial>.local:8892/`.
|
|
56
47
|
|
|
57
48
|
## Layout
|
|
58
49
|
|
|
59
|
-
- `src/App.tsx`
|
|
60
|
-
|
|
61
|
-
- `src/
|
|
62
|
-
- `
|
|
63
|
-
|
|
64
|
-
- `settings/`
|
|
65
|
-
`
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
`bun run build` runs both configs: the main app into `dist/`, then the settings
|
|
69
|
-
page as `dist/settings.html`. `manifest.json`'s `settings` field points the
|
|
70
|
-
companion at it.
|
|
50
|
+
- `src/App.tsx` subscribes to `client.player.onSnapshot`, fetches artwork through
|
|
51
|
+
`client.asset.get`, and draws transport controls.
|
|
52
|
+
- `src/daemon.ts` returns the daemon URL.
|
|
53
|
+
- `public/manifest.json` carries the webapp id, version, config schema, and
|
|
54
|
+
permissions.
|
|
55
|
+
- `settings/` is the settings page the companion app renders, built to
|
|
56
|
+
`dist/settings.html`.
|
|
57
|
+
- `index.html` and `vite.config.ts` fix the 800x480 viewport and the build target.
|
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: bridgething
|
|
3
|
-
description: Build, run, and ship a bridgething webapp for the Spotify Car Thing.
|
|
3
|
+
description: Build, run, and ship a bridgething webapp for the Spotify Car Thing. Use for @bridgething/client calls (now-playing, transport, artwork, storage, user settings, networking, every surface), running the dev server and driving the 800x480 screen and its physical controls, pushing the built app to a device or packaging a shareable zip, and writing the desktop-side Deno extension a webapp can ship.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Building a bridgething webapp
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Three jobs. Read the reference file for the one in front of you:
|
|
14
|
-
|
|
15
|
-
- **Call the daemon** - now-playing, transport, artwork, storage, settings,
|
|
16
|
-
networking, and the full 18-surface index: [reference/sdk.md](reference/sdk.md)
|
|
17
|
-
- **Run and drive the app** - dev server, screenshot the 800x480 screen, press
|
|
18
|
-
the physical controls (locally, then over CDP on the device):
|
|
8
|
+
- Calling the daemon, from now-playing to networking, plus every surface the SDK
|
|
9
|
+
ships: [reference/sdk.md](reference/sdk.md)
|
|
10
|
+
- Running the app, screenshotting the 800x480 screen, and pressing the physical
|
|
11
|
+
controls locally and over CDP on the device:
|
|
19
12
|
[reference/develop.md](reference/develop.md)
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
|
|
13
|
+
- Installing onto a connected Car Thing, packaging a zip, and updating the
|
|
14
|
+
device: [reference/ship.md](reference/ship.md)
|
|
15
|
+
- The desktop-side Deno extension, its `ctx` contract, and its permission model,
|
|
16
|
+
when the project has an `extension/` directory:
|
|
17
|
+
[reference/extension.md](reference/extension.md)
|
|
23
18
|
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
CLAUDE.md holds the device shape, the control-to-event mapping, and the manifest
|
|
20
|
+
fields.
|