create-bridgething 0.12.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/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "create-bridgething",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "type": "module",
5
- "description": "Scaffold a new bridgething webapp (React + Vite + Tailwind v4)",
5
+ "description": "Scaffold a bridgething app source: webapps for the Spotify Car Thing, published as a catalog.v1 on GitHub Pages.",
6
6
  "license": "MIT",
7
7
  "author": "Joey Eamigh <55670930+JoeyEamigh@users.noreply.github.com> (https://github.com/JoeyEamigh)",
8
8
  "homepage": "https://bridgething.com",
@@ -14,7 +14,14 @@
14
14
  "bugs": {
15
15
  "url": "https://github.com/JoeyEamigh/bridgething/issues"
16
16
  },
17
- "keywords": ["bridgething", "spotify", "car-thing", "superbird", "create", "scaffold"],
17
+ "keywords": [
18
+ "bridgething",
19
+ "spotify",
20
+ "car-thing",
21
+ "superbird",
22
+ "create",
23
+ "scaffold"
24
+ ],
18
25
  "publishConfig": {
19
26
  "access": "public",
20
27
  "registry": "https://registry.npmjs.org/"
@@ -24,7 +31,9 @@
24
31
  },
25
32
  "files": [
26
33
  "dist",
34
+ "template-versions.json",
27
35
  "template",
36
+ "template-source",
28
37
  "template-extension",
29
38
  "template-launcher",
30
39
  "template-overlay"
@@ -37,7 +46,7 @@
37
46
  "test": "bun test"
38
47
  },
39
48
  "devDependencies": {
40
- "@bridgething/extension": "0.12.0",
49
+ "@bridgething/extension": "0.12.1",
41
50
  "@msgpack/msgpack": "^3.1.3",
42
51
  "@types/bun": "^1.4.0",
43
52
  "@types/node": "^26.1.1",
@@ -72,15 +72,16 @@ scrolling for the rotary's horizontal wheel.
72
72
  CDP input goes into the page, so the launcher gesture and browser-nav side
73
73
  effects stay out of the way.
74
74
 
75
- Get the app on the kiosk first with `bun run dev:device` or `bun run push`. CDP
76
- is served at `bridgething.local:9222`, reachable over USB:
75
+ Get the app on the kiosk first with `bun run dev:device` or `bun run push`.
76
+ Chromium listens on loopback, but the image proxies it, so CDP answers on
77
+ `bridgething.local:9222` over USB:
77
78
 
78
79
  ```bash
79
80
  curl -s http://bridgething.local:9222/json/version >/dev/null && echo cdp-up
80
81
  ```
81
82
 
82
- Use the `webSocketDebuggerUrl` exactly as returned. It comes back as an IP rather
83
- than the hostname you asked for, and that address is the correct one.
83
+ Use the `webSocketDebuggerUrl` exactly as returned. It comes back as an IP
84
+ rather than the hostname you asked for, and that address is the correct one.
84
85
 
85
86
  Playwright's `connectOverCDP` never finishes the websocket upgrade against the
86
87
  device's embedded chromium, so drive it with a raw CDP websocket. Connect to the
@@ -116,6 +116,19 @@ fetch and XHR. WebSocket APIs work. Plain HTTP APIs work when the server sends
116
116
  permissive CORS headers, since requests arrive with `Origin: null`. For a
117
117
  CORS-strict HTTP-only service, fetch through `client.net` instead.
118
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.
125
+
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.
131
+
119
132
  `client.net` tunnels HTTP, websockets, and a SOCKS proxy through the phone. The
120
133
  SOCKS proxy needs `"net.proxy"` in the manifest's `permissions`. Read the
121
134
  `NetSurface` block in the `.d.ts` for the request shapes.
@@ -7,13 +7,13 @@
7
7
  "dev": "vite",
8
8
  "dev:device": "vite --mode device",
9
9
  "build": "vite build && vite build -c vite.settings.config.ts",
10
+ "typecheck": "tsc --noEmit",
10
11
  "preview": "vite preview",
11
12
  "push": "bun scripts/push.ts",
12
13
  "share": "bun scripts/share.ts",
13
14
  "update": "bunx @bridgething/updater"
14
15
  },
15
16
  "dependencies": {
16
- "@bridgething/client": "^0.12.0",
17
17
  "preact": "^10.29.7",
18
18
  "react": "^19.2.0",
19
19
  "react-dom": "^19.2.0"
@@ -1,6 +1,6 @@
1
1
  import { settings, type ConfigField, type SettingsContext } from '@bridgething/client/settings';
2
- import { render } from 'preact';
3
- import { useEffect, useState } from 'preact/hooks';
2
+ import { useEffect, useState, type FormEvent, type InputEvent } from 'react';
3
+ import { createRoot } from 'react-dom/client';
4
4
  import './style.css';
5
5
 
6
6
  function fieldMeta(field: ConfigField): { key: string; label: string } {
@@ -26,7 +26,7 @@ function Settings() {
26
26
  })();
27
27
  }, []);
28
28
 
29
- async function saveConfig(event: Event) {
29
+ async function saveConfig(event: FormEvent<HTMLFormElement>) {
30
30
  event.preventDefault();
31
31
  setStatus('saving...');
32
32
  try {
@@ -53,17 +53,18 @@ function Settings() {
53
53
  return (
54
54
  <main>
55
55
  <h1>{ctx?.name ?? '__PROJECT_NAME__'} settings</h1>
56
- <p class="hint">{ctx ? `${ctx.webappId} on ${ctx.deviceId}` : 'connecting to the companion host...'}</p>
56
+ <p className="hint">{ctx ? `${ctx.webappId} on ${ctx.deviceId}` : 'connecting to the companion host...'}</p>
57
57
 
58
58
  <form onSubmit={saveConfig}>
59
- {fields.length === 0 && <p class="hint">this webapp declares no config fields yet.</p>}
59
+ {fields.length === 0 && <p className="hint">this webapp declares no config fields yet.</p>}
60
60
  {fields.map(field => {
61
61
  const { key, label } = fieldMeta(field);
62
62
  const value = values[key] ?? '';
63
- const onInput = (e: Event) => setValues({ ...values, [key]: (e.target as HTMLInputElement).value });
63
+ const onInput = (e: InputEvent<HTMLInputElement | HTMLSelectElement>) =>
64
+ setValues({ ...values, [key]: (e.target as HTMLInputElement).value });
64
65
  return (
65
- <div class="field" key={key}>
66
- <label for={key}>{label}</label>
66
+ <div className="field" key={key}>
67
+ <label htmlFor={key}>{label}</label>
67
68
  {field.type === 'enum' ? (
68
69
  <select id={key} value={value} onInput={onInput}>
69
70
  {field.data.choices.map(choice => (
@@ -84,20 +85,20 @@ function Settings() {
84
85
  );
85
86
  })}
86
87
 
87
- <div class="row">
88
+ <div className="row">
88
89
  <button type="submit">Save settings</button>
89
- <button type="button" class="secondary" onClick={saveToDevice}>
90
+ <button type="button" className="secondary" onClick={saveToDevice}>
90
91
  Save to device
91
92
  </button>
92
- <button type="button" class="secondary" onClick={() => settings.done()}>
93
+ <button type="button" className="secondary" onClick={() => settings.done()}>
93
94
  Done
94
95
  </button>
95
96
  </div>
96
97
  </form>
97
98
 
98
- <p class="status">{status}</p>
99
+ <p className="status">{status}</p>
99
100
  </main>
100
101
  );
101
102
  }
102
103
 
103
- render(<Settings />, document.getElementById('root')!);
104
+ createRoot(document.getElementById('root')!).render(<Settings />);
@@ -70,7 +70,7 @@ export default function App() {
70
70
  <div className="mt-6 flex gap-4">
71
71
  <button
72
72
  className="border border-edge px-6 py-3 font-mono text-row text-near transition active:bg-neutral-soft"
73
- onClick={() => client.player.skipPrev()}>
73
+ onClick={() => client.player.skipPrev({ allowSeeking: true })}>
74
74
  ◀◀
75
75
  </button>
76
76
  <button
@@ -18,5 +18,5 @@
18
18
  "verbatimModuleSyntax": true,
19
19
  "useDefineForClassFields": true
20
20
  },
21
- "include": ["src"]
21
+ "include": ["src", "settings", "extension"]
22
22
  }
@@ -26,14 +26,14 @@ function sizeGuard(): Plugin {
26
26
  '',
27
27
  '========================================================================',
28
28
  ` settings.html is ${kib} KiB.`,
29
- ` the companion hard-rejects settings pages over ${HARD_CAP} at install.`,
30
- ' trim dependencies or inline assets before this page stops installing.',
29
+ ` the device refuses to install a bundle whose settings page is over ${HARD_CAP}.`,
30
+ ' trim dependencies or inline assets; the whole install fails, not just this page.',
31
31
  '========================================================================',
32
32
  '',
33
33
  ].join('\n'),
34
34
  );
35
35
  } else if (bytes > WARN_BYTES) {
36
- console.warn(`settings.html is ${kib} KiB; keep it lean (install hard cap is ${HARD_CAP}).`);
36
+ console.warn(`settings.html is ${kib} KiB; keep it lean (install fails over ${HARD_CAP}).`);
37
37
  }
38
38
  },
39
39
  };
@@ -18,7 +18,7 @@ function sizeGuard(): Plugin {
18
18
  return;
19
19
  }
20
20
  if (bytes > WARN_BYTES) {
21
- console.warn(`overlay.js is ${(bytes / 1024).toFixed(1)} KiB; the daemon ignores overlays over ${HARD_CAP}.`);
21
+ console.warn(`overlay.js is ${(bytes / 1024).toFixed(1)} KiB; install fails over ${HARD_CAP}.`);
22
22
  }
23
23
  },
24
24
  };
@@ -0,0 +1,104 @@
1
+ # Building bridgething webapps in this repo
2
+
3
+ This repo is a bun workspace of webapps for the Spotify Car Thing, plus the pipeline that publishes them as a `catalog.v1` source on GitHub Pages. Each `apps/<slug>` is one webapp: a single page running full-screen in the chromium kiosk on the device, reaching the on-device daemon through `@bridgething/client`.
4
+
5
+ ## The device
6
+
7
+ The screen is 800x480, landscape, and never resizes. The kiosk shows one webapp, so build in-app views rather than tabs or windows. Add an on-screen keyboard if the app needs text entry.
8
+
9
+ Listen with a `keydown` handler and a `wheel` handler on `window`.
10
+
11
+ | Control | Event |
12
+ | ------------ | ------------------------------------- |
13
+ | Preset 1-4 | `keydown` key `"1"` `"2"` `"3"` `"4"` |
14
+ | Mode | `keydown` key `"m"` |
15
+ | Back | `keydown` key `"Escape"` |
16
+ | Rotary wheel | `wheel` with horizontal `deltaX` |
17
+ | Touch | pointer and touch events |
18
+
19
+ Make horizontal wheel scroll move through the main list. Five fast presses of Mode returns to the launcher.
20
+
21
+ ## The client
22
+
23
+ ```ts
24
+ import { BridgethingClient } from '@bridgething/client';
25
+ import { useMemo } from 'react';
26
+
27
+ import { daemonUrl } from './daemon';
28
+
29
+ const client = useMemo(() => new BridgethingClient({ url: daemonUrl() }), []);
30
+ ```
31
+
32
+ Construct it once and reuse it. It connects and reconnects on its own. Call `daemonUrl()` instead of a literal `ws://` address.
33
+
34
+ Now-playing and library data come from the phone's Spotify, so render a placeholder when no phone is connected. Fetch artwork with `client.asset.get` using the opaque id on the track.
35
+
36
+ Every surface: `player asset config store doc capabilities library audio notifications phone peer geo net hardware bluetooth system time voice lyrics webapp forward`. Each method is an event, a request, or a command.
37
+
38
+ Methods, types, and examples: `.claude/skills/bridgething/reference/sdk.md`. That skill lives once at the repo root and every app symlinks it, so it is one copy for the whole workspace. `bun run skills` refreshes it from the published `create-bridgething`; `bun run check` says when it is behind.
39
+
40
+ ## Working on one app
41
+
42
+ ```sh
43
+ bun run dev # the only app, against a connected Car Thing
44
+ bun run dev <slug> # name it when there is more than one
45
+ bun run --cwd apps/<slug> dev # always works
46
+ bun run --cwd apps/<slug> dev:device # show that server on the device's own screen
47
+ bun run --cwd apps/<slug> push # build and install onto the connected device
48
+ bun run --cwd apps/<slug> build # writes dist/
49
+ bun run --cwd apps/<slug> typecheck # checks src, settings and extension together
50
+ bun run --cwd apps/<slug> share # zips dist/ to hand to someone directly
51
+ bun run --cwd apps/<slug> update # brings the device to the latest bridgething release
52
+ ```
53
+
54
+ An app with a variant or an extension carries its own `apps/<slug>/CLAUDE.md` with the parts specific to it.
55
+
56
+ Running and driving the app: `.claude/skills/bridgething/reference/develop.md`. Push, zip, update: `.claude/skills/bridgething/reference/ship.md`. The desktop-side Deno extension and its permission model, for an app with an `extension/` directory: `.claude/skills/bridgething/reference/extension.md`.
57
+
58
+ ## The three files that describe an app
59
+
60
+ **`apps/<slug>/public/manifest.json`** is the app's identity as far as the device and the catalog are concerned.
61
+
62
+ - `id` identifies this webapp on the device. It is a uuidv7 generated once at scaffold time. **Never change it**: the device keys upgrade-in-place and the app's key-value namespace on it, so a new uuid orphans everyone's installed copy and its data.
63
+ - `version` is what the store publishes against. Move it with `bun run bump`, never by hand.
64
+ - `description` is the store tagline and must not be empty.
65
+ - `config` declares the settings the companion app edits and `client.config` reads.
66
+ - `permissions` grants `geo` and `net.proxy`.
67
+ - `art.heroPx` and `art.thumbPx` are the sizes artwork arrives at.
68
+ - `settings` names the page built from `settings/`, capped at 1 MiB. It talks to the companion app through `@bridgething/client/settings`.
69
+ - `extension` declares the desktop-side Deno process and the host permissions it gets.
70
+
71
+ **`apps/<slug>/catalog.json`** is the store listing and nothing else: `author`, `homepage`, `source`, an `icon` override, `screenshots`, and `min_libbridgething_version` (the oldest daemon the app works against). Never restate anything the manifest already says. An app that ships an extension must carry its `github.com` repo url in `source`, because the store shows that link next to the host permissions it is asking for.
72
+
73
+ **`apps/<slug>/CHANGELOG.md`** carries a `## <version>` section per release, which becomes that version's changelog in the catalog.
74
+
75
+ ## Adding and shipping
76
+
77
+ ```sh
78
+ bun run new <slug> [--extension | --launcher | --overlay]
79
+ bun run shot <slug> [--replace | --name <label>]
80
+ bun run bump <slug> <major | minor | patch | x.y.z> [-m "note"]
81
+ bun run check
82
+ ```
83
+
84
+ `new` scaffolds into `apps/<slug>`. Never add an app by hand: the scaffold generates the uuid, writes the store listing, wires the dev server to a connected device, and lands the tsconfig and scripts CI expects.
85
+
86
+ `shot` captures the kiosk over CDP into `apps/<slug>/screenshots/`. Chromium's debugging port is 9223 and is bound to the device's loopback, so it goes through an ssh tunnel; the command opens and closes one itself. Up to six, filename order, first is the store card.
87
+
88
+ `bump` writes `public/manifest.json` and `package.json` together and opens the changelog section. Editing one without the other fails the build.
89
+
90
+ `check` is the whole gate and is exactly what CI runs: typecheck, build, bundle, generate the catalog, validate it against `catalog.v1` and the cross-reference invariants. Run it before claiming anything works.
91
+
92
+ Pushing to main publishes. `bun run publish --dry-run` assembles the exact bytes that would be pushed, into `site/`, without pushing.
93
+
94
+ ## Publishing rules that are not negotiable
95
+
96
+ - **A published version is immutable.** `published.json` on the `gh-pages` branch records the sha256 clients verify against. Changing an app means a new version, always. `check` fails a pull request that changed an app without bumping it.
97
+ - **Bundles are reproducible.** Timestamps are flattened before zipping so the same source gives the same digest. Do not add anything nondeterministic to a build.
98
+ - **`site/` is generated and gitignored.** Nothing about a release belongs on main.
99
+ - **CORS is a requirement.** A source must serve its catalog and its downloads with `Access-Control-Allow-Origin: *` or browser clients cannot read it. GitHub Pages does; most self-hosting does not.
100
+ - **`base_url` in `source.json` is where this catalog is served.** If you forked this repo, point it at your own pages site and give every app a fresh uuid, or you publish app ids that belong to someone else. `check` refuses when it disagrees with the origin remote.
101
+
102
+ ## Comments
103
+
104
+ Terse, lowercase, self-contained, non-obvious WHY only, 120 columns. No historical cross-references, no pointers at other files, no emdashes. Most code here needs none.
@@ -0,0 +1,57 @@
1
+ # **SOURCE_NAME**
2
+
3
+ **SOURCE_DESCRIPTION**
4
+
5
+ Webapps for the Spotify Car Thing running [bridgething](https://bridgething.com).
6
+
7
+ ## First run
8
+
9
+ 1. Push this repo to `https://github.com/__REPO_OWNER__/__REPO_NAME__`.
10
+ 2. In **Settings > Pages**, set the source to **Deploy from a branch**, branch `gh-pages`, folder `/ (root)`.
11
+
12
+ The catalog is published to `__BASE_URL__/catalog.v1.json`, which can be submitted to <bridgething.com/apps>
13
+
14
+ ## Develop
15
+
16
+ ```sh
17
+ bun run dev # develop the app against a connected bridgething instance
18
+ bun run dev:device # show the dev server on the car thing screen
19
+ bun run push # build and install to the device
20
+ bun run check # ensure the catalog is valid
21
+ ```
22
+
23
+ With more than one app in `apps/` your commands must specify which one: `bun run dev __APP_SLUG__`.
24
+
25
+ Screenshot for the store listing:
26
+
27
+ ```sh
28
+ bun run shot __APP_SLUG__ # grabs what is on the screen
29
+ bun run shot __APP_SLUG__ --replace # overwrite
30
+ ```
31
+
32
+ ## Add another app
33
+
34
+ ```sh
35
+ bun run new weather # a webapp
36
+ bun run new dashboard --extension # a webapp plus a desktop-side Deno process
37
+ bun run new home --launcher # a replacement home screen
38
+ bun run new hud --overlay # a system overlay drawn over every webapp
39
+ ```
40
+
41
+ ## Ship
42
+
43
+ ```sh
44
+ bun run bump __APP_SLUG__ patch -m "Fix the wind direction arrow"
45
+ git commit -am "__APP_SLUG__: fix the wind direction arrow" && git push
46
+ ```
47
+
48
+ Pushing to main builds the apps and regenerates the catalog.
49
+
50
+ ## Agent skill
51
+
52
+ `.claude/skills/bridgething/` holds the `/bridgething` skill.
53
+
54
+ ```sh
55
+ bun run skills # refresh it from the published create-bridgething
56
+ bun run skills --check # check whether it is behind
57
+ ```
@@ -0,0 +1 @@
1
+ 1.4.0
@@ -0,0 +1,28 @@
1
+ name: ci
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches: [main]
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ check:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ with:
17
+ fetch-depth: 0
18
+
19
+ - uses: oven-sh/setup-bun@v2
20
+ with:
21
+ bun-version-file: .bun-version
22
+
23
+ - run: bun install --frozen-lockfile
24
+
25
+ - name: build, bundle and validate the catalog
26
+ env:
27
+ CHECK_BASE_REF: ${{ github.event_name == 'pull_request' && format('origin/{0}', github.base_ref) || '' }}
28
+ run: bun run check
@@ -0,0 +1,33 @@
1
+ name: publish
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch:
7
+
8
+ concurrency:
9
+ group: publish
10
+ cancel-in-progress: false
11
+
12
+ permissions:
13
+ contents: write
14
+
15
+ jobs:
16
+ publish:
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - uses: oven-sh/setup-bun@v2
22
+ with:
23
+ bun-version-file: .bun-version
24
+
25
+ - run: bun install --frozen-lockfile
26
+
27
+ - name: identify the publisher
28
+ run: |
29
+ git config user.name "github-actions[bot]"
30
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
31
+
32
+ - name: build every unpublished version and push the catalog
33
+ run: bun run publish
@@ -0,0 +1,7 @@
1
+ node_modules
2
+ dist
3
+ site
4
+ .dev-extension
5
+ .DS_Store
6
+ *.log
7
+ .env*
@@ -0,0 +1,10 @@
1
+ /** @type {import("prettier").Config} */
2
+ export default {
3
+ bracketSpacing: true,
4
+ bracketSameLine: true,
5
+ singleQuote: true,
6
+ trailingComma: 'all',
7
+ arrowParens: 'avoid',
8
+ semi: true,
9
+ overrides: [{ files: ['*.ts', '*.js', '*.tsx', '*.jsx', '*.cjs', '*.mjs'], options: { printWidth: 120 } }],
10
+ };
File without changes
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "__REPO_NAME__",
3
+ "private": true,
4
+ "type": "module",
5
+ "workspaces": [
6
+ "apps/*"
7
+ ],
8
+ "scripts": {
9
+ "new": "bunx create-bridgething",
10
+ "dev": "bridgething-source run dev",
11
+ "dev:device": "bridgething-source run dev:device",
12
+ "build": "bridgething-source run build",
13
+ "push": "bridgething-source run push",
14
+ "share": "bridgething-source run share",
15
+ "check": "bridgething-source check",
16
+ "bump": "bridgething-source bump",
17
+ "shot": "bridgething-source shot",
18
+ "catalog": "bridgething-source catalog",
19
+ "publish": "bridgething-source publish",
20
+ "skills": "bridgething-source skills",
21
+ "format": "prettier --write \"*.json\" \"*.js\""
22
+ },
23
+ "devDependencies": {
24
+ "prettier": "^3.9.6",
25
+ "typescript": "^6.0.3"
26
+ }
27
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "__SOURCE_NAME__",
3
+ "description": "__SOURCE_DESCRIPTION__",
4
+ "homepage": "https://github.com/__REPO_OWNER__/__REPO_NAME__",
5
+ "icon": null,
6
+ "base_url": "https://__REPO_OWNER_LOWER__.github.io/__REPO_NAME__",
7
+ "recommended_sources": []
8
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "@bridgething/client": "^0.12.1",
3
+ "@bridgething/extension": "^0.12.1",
4
+ "@bridgething/source": "^0.12.1",
5
+ "libbridgething": "0.12.1"
6
+ }
@@ -1,71 +0,0 @@
1
- # Building this bridgething webapp
2
-
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
26
-
27
- ```ts
28
- import { BridgethingClient } from '@bridgething/client';
29
- import { useMemo } from 'react';
30
-
31
- import { daemonUrl } from './daemon';
32
-
33
- const client = useMemo(() => new BridgethingClient({ url: daemonUrl() }), []);
34
- ```
35
-
36
- Construct it once and reuse it. It connects and reconnects on its own. Call
37
- `daemonUrl()` instead of a literal `ws://` address.
38
-
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`.
48
-
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
61
-
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.
69
-
70
- Running and driving the app: `.claude/skills/bridgething/reference/develop.md`.
71
- Push, zip, update: `.claude/skills/bridgething/reference/ship.md`.
@@ -1,57 +0,0 @@
1
- # **PROJECT_NAME**
2
-
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).
6
-
7
- Stack: React 19, Vite, Tailwind v4, TypeScript strict.
8
-
9
- Open this folder with your coding agent. It reads `CLAUDE.md` or `AGENTS.md` and
10
- the `/bridgething` skill.
11
-
12
- ## Develop
13
-
14
- ```bash
15
- bun install
16
- bun run dev
17
- ```
18
-
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.
22
-
23
- ```bash
24
- bun run dev:device
25
- ```
26
-
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
-
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
-
34
- ## Push, share, update
35
-
36
- ```bash
37
- bun run push # build, copy to the Car Thing, switch the kiosk to it
38
- bun run build
39
- bun run share # write <name>-<version>.zip from dist/
40
- bun run update # bring the device to the latest bridgething release
41
- ```
42
-
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/`.
47
-
48
- ## Layout
49
-
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.