index ↓

settings pages

typing a url or picking from many entities on the car screen is painful. a settings page moves that to the phone. it is one self-contained html file in your bundle. the companion app renders it in a webview.

s1 - declare it

point manifest.json at the built file. it must be a single self-contained html file, all js and css inlined. the cap is 1 MiB at install. keep it lean.

{
  "id": "…",
  "name": "My App",
  "version": "0.1.0",
  "settings": "settings.html"
}

bun create bridgething scaffolds all of this: a preact page under settings/, a single-file vite build with size warnings at 200 KiB, and the manifest entry.

// package.json: the scaffold ships this wiring
"build": "vite build && vite build -c vite.settings.config.ts"

s2 - talk to the companion

inside the page, import @bridgething/client/settings. it is a tiny bridge sdk. it reads and writes your app's declared config and writes the doc namespace: shared per-app key/value state.

import { settings } from '@bridgething/client/settings';

const { webappId, name } = await settings.context();

// the user's saved config values (and the manifest schema, for rendering a form)
const fields = await settings.config.fields();
const values = await settings.config.list();
await settings.config.set('base_url', url);

// the shared doc namespace: what your phone-side page authors, your
// on-device webapp reads
await settings.doc.set('selected_entities', ids.join(','));
const off = settings.onDocChanged((key, value) => sync(key, value));

settings.done(); // closes the sheet

on the device side, your webapp reads the same doc namespace over the normal client:

// on-device webapp: adopt what the settings page authored, live
const saved = await client.doc.get({ key: 'selected_entities' });
client.doc.onChanged(c => applySelection(c.value));

s3 - network: use websocket apis or permissive origins

the page runs on the phone, so it has real internet. but it loads from a file:// origin, and the webview enforces cors on fetch/xhr from that origin. in practice:

  • websocket apis work. the ws handshake is not cors-gated.
  • plain http apis work only if the server sends permissive cors headers (your requests arrive with Origin: null or *). most public apis are fine.

rule of thumb: if the service has a websocket api, use it from the settings page. if it's http-only and cors-strict, do the fetching in your on-device webapp via client.net, which tunnels through the phone and is not origin-restricted.

s4 - a worked example

the home assistant example app ships a settings page. it dials your ha server over its websocket api. it lists every entity, grouped and searchable. it saves your selection as a doc the device app applies live. the entity picker never renders on the car screen.