metronome/build.sh
Me Here df213272ae Add "as-built" player variant: mono 128×64 OLED + 16-px WS2812 beat bar
New /player-asbuilt.html showing the PM-1 with parts you'd actually solder
for an RP2040 build, alongside the idealized /player.html:

- 128×64 MONOCHROME OLED (SSD1306 class): rendered as a true 1-bit
  framebuffer — drawn, then thresholded to crisp on/off pixels and scaled
  with image-rendering:pixelated — so the cramped real layout is honest
  (position / big BPM / grouping / scrolling name / bar·beat).
- Fixed 16-px WS2812 ("NeoPixel") RGB beat bar on a strip PCB: lights the
  first beatsPerBar slots (cyan downbeats, amber group-starts, dim others),
  the rest dark — showing the fixed-count hardware honestly.
- EC11 rotary encoder you actually turn (wheel / vertical drag) for tempo,
  tactile buttons, MAX98357A-style speaker grille, USB-C, PWR LED, matte case.

Shares the same firmware via src/engine.js + src/setlists.js (same seed set
lists, same scheduler); only the panel rendering differs. The device is fixed
dark hardware; the page chrome follows light/dark/system. build.sh + deploy.sh
now assemble/serve all three pages; player.html links to it ("As-built ↗").

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 06:57:57 -05:00

36 lines
1.7 KiB
Bash
Executable file

#!/usr/bin/env bash
# Assemble the deployed single-file pages from source + shared partials + assets/.
#
# Both index.html and player.html are sources that share code via markers:
# /*@BUILD:include:src/<file>@*/ inlines a shared partial (engine, seed lists, base CSS)
# @BUILD:favicon@ / @BUILD:logo-*@ / /*@BUILD:samples@*/{} inline base64 assets
# This resolves them so each built page in dist/ is one self-contained file
# (zero deps, works fully offline). deploy.sh runs this first. dist/ is generated —
# don't edit or commit it.
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")"
mkdir -p dist
python3 - <<'PY'
import json, os, pathlib, re
A = pathlib.Path("assets")
SAMPLES = json.dumps(json.load(open(A / "samples.json")))
def build(name):
src = pathlib.Path(name).read_text()
# 1) inline shared partials (function-replacement: no backslash/group interpretation)
src = re.sub(r"/\*@BUILD:include:([^@]+)@\*/",
lambda m: pathlib.Path(m.group(1)).read_text().rstrip("\n"), src)
# 2) inline base64 assets
src = src.replace("@BUILD:favicon@", (A / "favicon.b64").read_text().strip())
src = src.replace("@BUILD:logo-dark@", (A / "logo-dark.b64").read_text().strip())
src = src.replace("@BUILD:logo-light@", (A / "logo-light.b64").read_text().strip())
src = src.replace("/*@BUILD:samples@*/{}", SAMPLES)
assert "@BUILD:" not in src, f"unresolved build marker(s) remain in {name}"
out = pathlib.Path("dist") / name
out.write_text(src)
return out.stat().st_size
i = build("index.html"); p = build("player.html"); a = build("player-asbuilt.html")
print("built dist/index.html (%dKB) + dist/player.html (%dKB) + dist/player-asbuilt.html (%dKB)"
% (i // 1024, p // 1024, a // 1024))
PY