# picosky - common development tasks. Source lives in src/ (src/*.lua modules + # src/cart.p8, which #includes them and holds the gfx/label/sfx/music data). The # root picosky.p8 / picosky*.js are BUILD OUTPUT, generated by `make build`. # `make` or `make help` lists targets. Override any variable on the command line, # e.g. `make build PICO8=/usr/bin/pico8` or `make serve PORT=9000`. # --- config ------------------------------------------------------------------ PICO8 ?= $(HOME)/.local/pico-8/pico8 P8HOME ?= /tmp/p8home SHRINKO8 ?= python3 tools/shrinko8/shrinko8.py # Tokens are picosky's binding budget (~90%); chars (~30%) and compressed (~55%) # have headroom. --focus-tokens trades a little compressed size for ~140 fewer # tokens per cart, so the .p8/.js outputs (and `count`) use it. The .p8.png # targets stay on the default (compressed-focused) minify - their limit is the # compressed ROM size, not tokens. MINIFY ?= --minify --focus-tokens # CART = source cart (#includes src/*.lua + holds the gfx/sfx/music data). # OUT = built standalone demo cart (BUILD OUTPUT). Inline `#` comments are kept # verbatim in Make values, so these are on their own lines (CART/OUT are file # prerequisites below - trailing whitespace would break the rules). CART ?= src/cart.p8 SRC := $(wildcard src/*.lua) OUT ?= picosky.p8 EXPORT ?= picosky.js DEMO_EXPORT ?= picosky-demo.js ZIP ?= /tmp/pico8.zip PORT ?= 8080 HEADLESS_ENV = SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy .DEFAULT_GOAL := help # --- help -------------------------------------------------------------------- .PHONY: help help: ## Show this help @grep -E '^[a-zA-Z0-9_-]+:.*?## ' $(MAKEFILE_LIST) \ | awk 'BEGIN{FS=":.*?## "}{printf " \033[36m%-14s\033[0m %s\n",$$1,$$2}' # --- dev loop ---------------------------------------------------------------- .PHONY: serve serve: ## Vite dev server on 127.0.0.1:8080 (LAN=1 to expose on network, HOST= to pick interface) PORT=$(PORT) pnpm exec vite $(if $(LAN),--host $(or $(HOST),0.0.0.0),$(if $(HOST),--host $(HOST),)) .PHONY: preview preview: ## Serve the built dist/ (with the real service worker) on 127.0.0.1:4173 pnpm exec vite preview # --- cart build -------------------------------------------------------------- # Source is src/cart.p8 (#includes src/*.lua + the gfx/sfx/music data). Two web # carts come from it: shrinko8 minifies and sets the per-cart ONLINE flag (online # = real client; demo = self-contained lua mock feed, no js data bridge), then # headless PICO-8 exports each to JS. The root picosky.p8 is BUILD OUTPUT too - # the minified standalone demo cart (online can't run without the js bridge). $(OUT): $(CART) $(SRC) $(SHRINKO8) $(CART) $(OUT) $(MINIFY) --const ONLINE false @echo "built $(OUT) (standalone demo cart)" $(EXPORT): $(CART) $(SRC) @mkdir -p build $(P8HOME) $(SHRINKO8) $(CART) build/online.p8 $(MINIFY) --const ONLINE true cd build && $(HEADLESS_ENV) $(PICO8) -home $(P8HOME) online.p8 -export "picosky.html" cp build/$(EXPORT) $(EXPORT) @echo "built $(EXPORT) (online)" $(DEMO_EXPORT): $(CART) $(SRC) @mkdir -p build $(P8HOME) $(SHRINKO8) $(CART) build/demo.p8 $(MINIFY) --const ONLINE false cd build && $(HEADLESS_ENV) $(PICO8) -home $(P8HOME) demo.p8 -export "picosky-demo.html" cp build/$(DEMO_EXPORT) $(DEMO_EXPORT) @echo "built $(DEMO_EXPORT) (demo)" .PHONY: build build-online build-demo cart cart: $(OUT) ## Build the standalone demo cart -> picosky.p8 build-online: $(EXPORT) ## Export the online cart -> picosky.js build-demo: $(DEMO_EXPORT) ## Export the demo cart -> picosky-demo.js build: $(OUT) build-online build-demo ## Build root cart + export both web carts # --- asset generation -------------------------------------------------------- .PHONY: avatars avatars: ## Re-fetch + bake real Bluesky avatars into __gfx__ node tools/fetch-avatars.js python3 tools/bake-avatars.py .PHONY: images images: ## Convert fox + npmx source images to PICO-8 hex strings (paste output into actions.lua) @echo "fox_hex:"; .venv/bin/python3 tools/gen-image.py tools/fox-reference.png @echo "npmx_hex:"; .venv/bin/python3 tools/gen-image.py tools/npmx-logo.png .PHONY: icons icons: ## Inject stats (reply/repost/heart 8-10) + footer (compose/refresh/quote 12-14) icons into __gfx__ python3 tools/gen_icons.py $(CART) .PHONY: label label: ## Regenerate the cart __label__ from its butterfly sprite python3 tools/gen_label.py $(CART) .PHONY: web-icons web-icons: ## Regenerate favicons, app icons + Open Graph card from the page logo python3 tools/gen_web_icons.py # --- .p8.png exports (BBS / Splore) ------------------------------------------ .PHONY: png png: ## Build picosky.p8.png (demo+online, compressed ≤ 100%) $(SHRINKO8) $(CART) picosky.p8.png -f png --minify --title "A BLUESKY CLIENT FOR PICO-8" --title "made by vinnymac ♥" .PHONY: png-online png-online: ## Build picosky-online.p8.png (ONLINE=true, strips demo block) $(SHRINKO8) $(CART) picosky-online.p8.png -f png --minify --const ONLINE true # --- code checks (shrinko8) -------------------------------------------------- # Lint runs on the readable source (the only place its warnings are meaningful); # count runs on the minified per-cart builds - the budgets that actually ship. .PHONY: lint lint: ## Lint the cart source $(SHRINKO8) $(CART) --lint .PHONY: count count: ## Token / char / compressed budgets of the shipped carts (online + demo) @mkdir -p build @echo "online:"; $(SHRINKO8) $(CART) build/online-count.p8 $(MINIFY) --const ONLINE true --count @echo "demo:"; $(SHRINKO8) $(CART) build/demo-count.p8 $(MINIFY) --const ONLINE false --count .PHONY: validate validate: lint count ## Lint source + budget-check the shipped carts (the gate) # --- deploy ------------------------------------------------------------------ DIST ?= dist .PHONY: dist dist: build bundle ## Rebuild the carts, then bundle the web shell into dist/ for Cloudflare Pages .PHONY: bundle bundle: ## Bundle the web shell into dist/ with Vite (index.html modules + verbatim carts, generated sw.js) pnpm exec vite build --outDir $(DIST) .PHONY: deploy deploy: dist ## Build + deploy to Cloudflare Pages (requires wrangler auth) npx wrangler pages deploy $(DIST) --project-name picosky --commit-dirty=true .PHONY: ci-dist ci-dist: ## Bundle dist/ from committed carts (no pico8/python3 required; needs pnpm) # --ignore-scripts: the bundle needs no native addons (picosky's canvas is # only for the avatar tool) and the CI image has no python/compiler. pnpm install --frozen-lockfile --ignore-scripts $(MAKE) bundle .PHONY: ci-deploy ci-deploy: ci-dist ## Deploy committed artifacts to Cloudflare Pages (CI-safe, no pico8 needed) CLOUDFLARE_ACCOUNT_ID=ebbd354a5e5728e183b95c92fbcfb770 npx wrangler pages deploy $(DIST) --project-name picosky --branch=main --commit-dirty=true # --- setup / housekeeping ---------------------------------------------------- .PHONY: install-pico8 install-pico8: ## Install PICO-8 from /tmp/pico8.zip (override ZIP=) @test -f $(ZIP) || { echo "missing $(ZIP) (set ZIP=path)"; exit 1; } mkdir -p $(HOME)/.local/pico-8 $(HOME)/.local/bin /tmp/pico-8-extract unzip -o -q $(ZIP) -d /tmp/pico-8-extract cp -f /tmp/pico-8-extract/pico-8/pico8 /tmp/pico-8-extract/pico-8/pico8.dat $(HOME)/.local/pico-8/ chmod +x $(HOME)/.local/pico-8/pico8 ln -sf $(HOME)/.local/pico-8/pico8 $(HOME)/.local/bin/pico8 @echo "installed: $(PICO8)" .PHONY: setup setup: ## Clone shrinko8 into tools/ (needed for lint/count/validate/build) git clone --depth=1 https://github.com/thisismypassport/shrinko8.git tools/shrinko8 .PHONY: clean clean: ## Remove generated exports + build/ intermediates (keeps the built picosky.p8) rm -rf build picosky.js picosky.html picosky-demo.js picosky-demo.html picosky.p8.png picosky-online.p8.png