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
@@ -18,5 +18,5 @@
18
18
  "verbatimModuleSyntax": true,
19
19
  "useDefineForClassFields": true
20
20
  },
21
- "include": ["src"]
21
+ "include": ["src", "settings", "extension"]
22
22
  }
@@ -1,14 +1,16 @@
1
1
  import tailwindcss from '@tailwindcss/vite';
2
2
  import react from '@vitejs/plugin-react';
3
3
  import { defineConfig } from 'vite';
4
+ import { bridgething, daemonProxy } from './scripts/bridgething';
4
5
 
5
- export default defineConfig({
6
- plugins: [react(), tailwindcss()],
6
+ export default defineConfig(async () => ({
7
+ plugins: [react(), tailwindcss(), bridgething()],
7
8
  build: {
8
9
  target: 'es2022',
9
10
  sourcemap: true,
10
11
  },
11
12
  server: {
12
13
  host: true,
14
+ proxy: await daemonProxy(),
13
15
  },
14
- });
16
+ }));
@@ -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
  };
@@ -0,0 +1,23 @@
1
+ ## This project ships a native extension
2
+
3
+ `manifest.json` declares an `extension` block, so this bundle has two halves.
4
+
5
+ - `src/` is the webapp, running in the device's chromium kiosk.
6
+ - `extension/` is a Deno process on the desktop. The bridgething desktop app
7
+ spawns it while the app is installed and enabled, with or without a Car Thing
8
+ connected.
9
+
10
+ The two halves talk over the daemon's forward surface: `client.forward` in the
11
+ webapp, `device.send` and `ctx.on('message', ...)` in the extension. The daemon
12
+ delivers a forward while this webapp is the active one on that device.
13
+
14
+ Host access belongs in the extension, an ordinary Deno program with `npm:`,
15
+ `jsr:`, and `node:` available. Keep `src/` a view over what it sends. The webapp
16
+ also runs with nothing attached, so check `capabilities.available.forward` and
17
+ give the user something useful when it is false.
18
+
19
+ `manifest.json` starts with `"permissions": ["all"]`. Narrow it to the Deno
20
+ permissions you use before you publish.
21
+
22
+ The `ctx` contract, the permission grammar, the dev loop, and the gotchas:
23
+ `.claude/skills/bridgething/reference/extension.md`.
@@ -0,0 +1,20 @@
1
+ import { asJson, defineExtension, json } from '@bridgething/extension';
2
+
3
+ defineExtension({
4
+ start(ctx) {
5
+ ctx.log.info(`__PROJECT_NAME__ extension up, data in ${ctx.dataDir}`);
6
+
7
+ ctx.on('device', event => {
8
+ if (event.type !== 'connected') return;
9
+ const greeting = ctx.config(event.device).greeting ?? 'hello';
10
+ ctx.log.info(`${event.device.name} connected; greeting is "${greeting}"`);
11
+ event.device.send(json({ type: 'greeting', text: greeting }));
12
+ });
13
+
14
+ ctx.on('message', (device, message) => {
15
+ const payload = asJson<Record<string, unknown>>(message);
16
+ if (!payload) return;
17
+ device.send(json({ type: 'echo', payload }));
18
+ });
19
+ },
20
+ });
@@ -1,40 +1,24 @@
1
-
2
1
  ## This project is a launcher
3
2
 
4
- `manifest.json` declares `"role": "launcher"`, which means two things:
5
-
6
- - The daemon hides this bundle from `client.webapp.list`, so a launcher never
7
- lists itself in its own grid.
8
- - The bundle is eligible for the device's **launcher slot**. Holding that slot
9
- makes this the home screen: the boot default, the target of the Mode-button
10
- gesture, and what the back button returns to.
3
+ `manifest.json` declares `"role": "launcher"`. The daemon keeps this bundle out
4
+ of `client.webapp.list`, so the grid you draw excludes itself. Holding the
5
+ device's launcher slot makes it the home screen: the boot default, the target of
6
+ the Mode gesture, and where the Back button returns to.
11
7
 
12
- `bun run push` claims the slot and switches to it, so a push puts you straight
13
- on the home screen. `bun run push --release` hands it back, and the companion
14
- phone app can do the same. The built-in hub stays installed and can never be
15
- uninstalled, so releasing the slot always gets the stock home screen back. That
16
- is the recovery path if a launcher you are building wedges the device.
8
+ `bun run push` claims the slot and switches to it. `bun run push --release` hands
9
+ it back to the built-in hub, the recovery path when a build wedges the device.
17
10
 
18
- ### What a home screen is expected to do
11
+ ### What a home screen covers
19
12
 
20
- Nothing is enforced. The daemon does not require a launcher to implement any
21
- particular surface; if yours does less than the built-in hub, that is a choice,
22
- not an error, and the user can switch back. The built-in hub covers:
13
+ A launcher implements as much or as little as you want. The built-in hub covers:
23
14
 
24
- - the app grid (`client.webapp.list` / `.icon` / `.activate` / `.current`, plus
25
- the `onWebappInstalled` / `onWebappUninstalled` events so the grid stays live)
26
- - bluetooth settings: bonds, adapter alias, discoverable
15
+ - the app grid (`client.webapp.list`, `.icon`, `.activate`, `.current`, plus
16
+ `onWebappInstalled` and `onWebappUninstalled`)
17
+ - bluetooth bonds, adapter alias, discoverable
27
18
  - display brightness
28
19
  - system info and health
29
20
  - power: restart, shut down, factory reset
30
- - OTA progress (`client.system.onOtaProgress` / `.onOtaError`)
31
-
32
- The starter here implements only the grid. Read the built-in hub's source if you
33
- want a reference for the rest; everything it uses is on the public client SDK.
34
-
35
- ### Onboarding is not your problem
21
+ - OTA progress (`client.system.onOtaProgress` and `.onOtaError`)
36
22
 
37
- The first-boot pairing wizard lives in the built-in hub. A phone must already be
38
- paired before anyone can install your launcher, so by the time this runs, the
39
- device is set up. A factory reset clears the launcher slot along with everything
40
- else, which puts the built-in hub and its wizard back.
23
+ The starter here draws the grid. Everything the hub uses is on the public client
24
+ SDK.
@@ -1,9 +1,6 @@
1
1
  import { BridgethingClient, type WebappInfo } from '@bridgething/client';
2
2
  import { useEffect, useMemo, useState } from 'react';
3
-
4
- const wsUrl =
5
- import.meta.env.VITE_BRIDGETHING_URL ??
6
- (typeof window !== 'undefined' ? `ws://${window.location.host}/` : 'ws://127.0.0.1:8891/');
3
+ import { daemonUrl } from './daemon';
7
4
 
8
5
  type Entry = {
9
6
  info: WebappInfo;
@@ -11,7 +8,7 @@ type Entry = {
11
8
  };
12
9
 
13
10
  export default function App() {
14
- const client = useMemo(() => new BridgethingClient({ url: wsUrl }), []);
11
+ const client = useMemo(() => new BridgethingClient({ url: daemonUrl() }), []);
15
12
  const [entries, setEntries] = useState<Entry[] | null>(null);
16
13
  const [error, setError] = useState<string | null>(null);
17
14
  const [activating, setActivating] = useState<string | null>(null);
@@ -1,19 +1,13 @@
1
-
2
1
  ## This project is a system overlay
3
2
 
4
- `manifest.json` declares `"overlay": "overlay.js"`. When this bundle holds the
5
- device's **overlay slot**, the daemon injects that file into every webapp's
6
- document as it loads. The page in `src/` is incidental: it only renders if
7
- someone launches this bundle from the hub.
8
-
9
- `bun run push` claims the slot for you and reloads the kiosk, so the overlay
10
- appears over whatever app is showing. It deliberately does **not** switch to
11
- this bundle's own page - switching away from the app you are testing against is
12
- the opposite of what you want.
3
+ `manifest.json` declares `"overlay": "overlay.js"`. While this bundle holds the
4
+ device's overlay slot, the daemon injects that file into every webapp's document
5
+ as it loads. The page in `src/` renders when someone launches this bundle from
6
+ the hub.
13
7
 
14
- `bun run push --release` hands the slot back to the daemon's built-in overlay.
15
- That is the recovery path when a build of yours misbehaves; the companion phone
16
- app can do the same thing.
8
+ `bun run push` claims the overlay slot and reloads the kiosk, so the overlay
9
+ appears over the webapp that is showing. `bun run push --release` hands the slot
10
+ back to the built-in overlay, the recovery path when a build misbehaves.
17
11
 
18
12
  ### The contract
19
13
 
@@ -23,42 +17,34 @@ The daemon prepends one global before your bundle:
23
17
  window.__bridgethingOverlay = { origin: 'http://127.0.0.1:8891', surfaces: {...} };
24
18
  ```
25
19
 
26
- - **`surfaces`** is the active webapp's declared profile: `notifications`,
27
- `call`, `pairing`, `connection`, `volume`. An app that draws its own volume
28
- indicator declares `volume: false`. **Honor these.** The daemon cannot enforce
29
- it, and ignoring them means the user sees two of everything on apps that draw
30
- their own. If every surface is off, the daemon injects nothing at all.
31
- - **`origin`** is the kiosk origin. Check `location.origin` against it before
32
- mounting so the bundle never runs in a page the daemon did not serve.
20
+ `surfaces` is the active webapp's declared profile, a boolean each for
21
+ `notifications`, `call`, `pairing`, `connection`, `volume`, and `voice`. Render
22
+ only the surfaces set to true; an app that draws its own volume indicator
23
+ declares `volume: false`. When every surface is false the daemon injects nothing.
33
24
 
34
- Everything else comes from `@bridgething/client` over the local websocket, the
35
- same SDK a normal webapp uses.
25
+ `origin` is the kiosk origin. Compare `location.origin` against it before
26
+ mounting. Everything else comes from `@bridgething/client` over the local
27
+ websocket.
36
28
 
37
- ### Constraints that are not style
29
+ ### Output constraints
38
30
 
39
- `overlay.js` must be **one self-contained file** under 512 KiB. It is injected as
40
- a script string into another app's document, so it cannot import a module graph,
41
- fetch assets, or reach a bundler at runtime. `vite.overlay.config.ts` builds it
42
- as a single inlined iife; keep it that way.
31
+ `overlay.js` must be one self-contained file under 512 KiB. The daemon injects it
32
+ as a script string into another app's document, so it carries its own code and
33
+ styles and reaches nothing at runtime. `vite.overlay.config.ts` builds a single
34
+ inlined iife; keep that shape.
43
35
 
44
- That is a constraint on the *output*, not on how you write it. Style
45
- `overlay/main.tsx` with tailwind classes exactly like the rest of the project.
46
- `overlay/style.css` is a real stylesheet; it is imported with vite's `?inline`
47
- so the compiled css lands in the bundle as a string and gets mounted into the
48
- shadow root. Add `@theme` tokens or plain rules there. Because the shadow root
49
- is closed, tailwind is told not to scan the project (`source(none)`) and only
50
- generates what `overlay/main.tsx` uses - if you split the overlay across more
51
- files, add each one with `@source`.
36
+ Style `overlay/main.tsx` with tailwind classes like the rest of the project.
37
+ `overlay/style.css` is imported with vite's `?inline`, so the compiled css mounts
38
+ into the shadow root as a string. Tailwind scans only `overlay/main.tsx`
39
+ (`source(none)`), so add an `@source` line for every file you split across.
52
40
 
53
- The starter encodes four things you should not drop:
41
+ ### Keep these four from the starter
54
42
 
55
- 1. **Origin guard** before mounting.
56
- 2. **`__bridgethingOverlayMounted`** guard, so a double injection is a no-op.
57
- 3. **Closed shadow root**, so your styles and the host app's cannot reach each
58
- other.
59
- 4. **Escape-only, capture-phase key handling**, and only while something is
60
- showing. Your overlay sits on top of every app on the device; swallowing keys
61
- broadly breaks all of them.
43
+ 1. The origin guard before mounting.
44
+ 2. The `__bridgethingOverlayMounted` guard, so a second injection is a no-op.
45
+ 3. The closed shadow root, which keeps your styles and the host app's apart.
46
+ 4. Escape-only key handling on the capture phase, active only while something is
47
+ showing.
62
48
 
63
- You are running inside someone else's page. A crash, a fullscreen paint, or a
64
- greedy key handler takes down every webapp, not just this one.
49
+ You run inside every webapp's page. A crash, a fullscreen paint, or a broad key
50
+ handler takes down all of them.
@@ -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
+ }