Skip to content
busy-appPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

32 Commits

Folders and files

Repository files navigation

Clock

Clock

A JavaScript app for the BUSY Bar, generated from the BUSY JS app template.

Getting started

pnpm install
pnpm build

The app's id is app.busy.clock, set in src/appmeta/manifest.json. An id is <namespace>.<app>, at most 32 characters, made of letters, digits, dots, and underscores. If yours still says app.example, change it to your own namespace before publishing.

Layout

src/ is the app — everything in it is yours to edit.

The entry point is src/main.ts (or src/main.js). The name is fixed: the build turns it into scripts/main.js, the file the BUSY Bar starts. Any imported modules are emitted as separate files alongside it in scripts/, unless pnpm build --bundle inlines them.

src/main.ts is run from top to bottom, not imported: it starts the app by calling main() at the end and must not export anything. Imported modules can use standard exports.

my-app/
├── src/                          the app
│   ├── main.ts                   the entry point: function main(), called at the end
│   ├── appmeta/
│   │   ├── manifest.json         id, name, version
│   │   ├── settings.json         the settings screen — optional
│   │   ├── icon_front_8x8.png    front icon, 8×8 color — optional
│   │   └── icon_back_11x11.png   back icon, 11×11 grayscale — optional
│   ├── images/
│   ├── animations/
│   └── sounds/
├── shared/                       app-level helpers, imported as @shared/*

Both icons are optional; without them a default is used.

Resources

Anything in src/ that isn't a source file is copied into the package, sorted by what the BUSY Bar can read:

Extension Destination
.png .image images/
.anim animations/
.snd sounds/
anything else resources/

A file in src/images, src/animations or src/sounds keeps its folder as long as the BUSY Bar reads its format.

images/, animations/ and sounds/ hold only those formats. An image, animation or sound in another format — .jpg, .gif, .svg, .wav, .mp3 and the like — goes to resources/, and the build warns that the device cannot read it.

Everything the app draws is placed in src/frame.ts and sent with displayDraw() from @shared/device. Text widths come from src/font.ts — it only has the characters this clock prints.

Input

listen("input", handler) delivers the device keys: ok, start and back with a press / release action, and encoder with a delta of 1 or -1. It returns an unbind function.

Commands

Command Description
pnpm build empties dist/ and builds the package into it
pnpm build --out <path> builds into <path> instead, replacing only this app's package there
pnpm build --tgz also packs the package into a .tgz alongside it
pnpm build --tgz-only packs the .tgz and leaves nothing else
pnpm build --tgz --tgz-name <name> names the .tgz, e.g. "{id}-{version}.tgz"
pnpm build --no-minify readable output, for debugging
pnpm build --bundle inlines every module into one main.js
pnpm build --addr <host> sets the BUSY Bar address the app connects to, localhost by default
pnpm typecheck type-checks the app
pnpm app:install installs dist/app.busy.clock.tgz on the BUSY Bar; build it first with pnpm build --tgz
pnpm app:uninstall removes the app from the BUSY Bar, keeping its settings
pnpm app:run launches the installed app
pnpm app:stop quits the running app

The four app: commands talk to the BUSY Bar connected over USB. For a device connected over Wi-Fi, pass --addr <host> with its network IP address or hostname, and --password <password> if password protection is enabled:

pnpm app:install --addr <host>
pnpm app:install --addr <host> --password <password>

Build output

The package folder is named after the app id:

dist/
└── app.busy.clock/
    ├── appmeta/manifest.json
    ├── scripts/main.js
    └── images/ animations/ sounds/ resources/

--tgz packs that folder into dist/app.busy.clock.tgz, with the folder itself at the root of the archive. --tgz-name gives the archive another name; {id} and {version} in it are replaced with the manifest values.

Releasing

Pushing a git tag matching the manifest version (e.g. 0.1.0) builds the app and attaches app.busy.clock-<version>.tgz to a GitHub release. The release notes open with the app's name, id, version and API version. The release fails when the tag differs from the manifest version.

Manifest

src/appmeta/manifest.json carries the app's identity and a couple of knobs:

Field Description
format_version manifest format, 1 — required
id up to 32 characters — required
name display name — required
version semver, e.g. 1.0.0 — required
api_version minimum BUSY Bar HTTP API version the app needs, e.g. 27.12.0 — required
description, author free form, default ""
heap_size_kib memory for the app heap, 1–256, default 32
debug when true, shown only in developer mode, default false

Raise heap_size_kib if the app runs out of memory.

Node version

The app builds on Node 24 — the version in .nvmrc and the range engines allows. Node 26 breaks the build and is excluded. With fnm or nvm installed, fnm use / nvm use picks the right one up from .nvmrc.

Settings

To give the app a settings screen, add src/appmeta/settings.json describing the fields, then read the stored values with getAppSettings():

import { getAppSettings } from "@shared/device";

const { version, values } = await getAppSettings(manifest.id);

values is a map of field id to the stored value. If nothing is stored yet, the first read fills in the defaults from the schema. The clock reads settings once, at startup — restart it to pick up a change.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages