Skip to content
busy-appPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Clock

Clock

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

Getting started

pnpm install
cp .env.example .env   # set VITE_BUSY_ADDR to your device's address
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, dot, dash and underscore. 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.

my-app/
├── src/                          the app
│   ├── main.ts                   the entry point: export default function run()
│   ├── appmeta/
│   │   ├── manifest.json         id, name, version
│   │   ├── settings.json         the settings screen — optional
│   │   ├── icon_front_8x8.png    front icon, 8×8 colour — optional
│   │   └── icon_back_11x11.png   back icon, 11×11 greyscale — 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. Files in src/images, src/animations and src/sounds keep their folder; everything else is sorted by extension into the same three, and whatever has no match lands in resources/.

Extension Goes to
.png .jpg .jpeg .gif .bmp .webp .svg .image .ico images/
.anim .animation animations/
.wav .snd .sng .mp3 .ogg .rtttl sounds/
anything else resources/

A file whose extension disagrees with the folder it sits in is still placed by the folder, with a warning.

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. The build itself comes from @busy-app/cli.

Commands

Command What it does
pnpm build builds the package into dist/
pnpm build --out <path> builds into <path> instead
pnpm build --tgz also packs the package into a .tgz beside it
pnpm build --tgz-only packs the .tgz and leaves nothing else
pnpm build --no-minify readable output, for debugging
pnpm build --bundle inlines every module into one main.js
pnpm typecheck type-checks the app

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. The release workflow packs its own archive, named with the version as well.

Manifest

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

Field
format_version manifest format, 1 — required
id up to 32 characters — required
name display name — required
version semver, e.g. 1.0.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.

Vite version

vite is pinned rather than ranged. Vite 8 builds with rolldown, whose native binding is a separate per-platform package; a release with a missing binding breaks pnpm build. Pinning keeps the build reproducible — raise the version deliberately.

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