SOURCE / PINNED RELEASE
Made of little things.
super-platformer
- Release
- b61593abc2c6…
- Author-recorded commit
- bbd35ca510f6…
- License
- LICENSE
- Author’s source reference
- nostr://npub16uawymqtmdg4j6zcl6snt5apzfp4ek2g8lpasnqa75tspdpglcqsgpatvc/wss%3A%2F%2Fgit.napplet.soy%2F/n-01288b1ab38
Archive hash verified: d2529eb9ad1961f2…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
## napplet soyLI workspace
Read [docs/napplet-space.md](docs/napplet-space.md) first for this project's CLI commands, installed skills and host capabilities. Use the upstream guidance below with those tooling mappings.
# super-platformer
A small TypeScript platformer napplet built with the published `@napplet`
packages.
Napplets are sandboxed iframe apps. The shell/runtime injects `window.napplet`
before app code runs; the app uses `@napplet/sdk` to call available NAP domains.
## The game
A tile-based jump-and-run with a single simple character (a rounded body with
eyes). Six varied levels — Meadow, Cavern, Spires, Foundry, Ruins, and Gauntlet —
packed with pits, spikes, spinning saw blades, moving platforms, and four
cartoon enemy types.
- **Lives and checkpoints:** you start each level with three hearts (shown in the
HUD). Getting hurt costs one heart but you keep playing — coins, enemies,
crates and saws stay as they are, and you respawn at the last **checkpoint**
you touched (or where you last stood safely) with a short moment of
invulnerability so you can recover. Only when all three hearts are gone does
the level restart from the beginning; choose Try again to retry it.
- Enemies: **walker** (patrols), **jumper** (bounces), **flyer** (hovers),
**brute** (takes two stomps), and a **boss** that guards the last two levels
(five hits, shown as pips above its head). Jump on an enemy from above to
defeat it and collect a coin; touch one from the side or below and you are
out. Each type has an angry cartoon face.
- Hazards: spike tiles and rotating saw blades, including ground saws you must
jump over. Moving platforms appear as bonus routes.
- Sound: short synthesized sound effects for jumping, coins, stomps, power-ups,
fire, checkpoints, the boss and level completion. Toggle them with the ♪
button in the HUD or the `sound` setting.
- Power-up crates (`?`): touch a crate to break it and gain a temporary power-up.
- **Coin magnet** — pulls nearby coins toward you.
- **Fire shot** — press the fire key/button to launch fireballs that defeat
enemies.
- **Shield** — makes you invulnerable and destroys enemies on contact.
- **Double jump** — one extra jump in mid-air.
- **Speed** — a short burst of extra running speed.
The active power-up and its remaining time are shown in the HUD.
- Controls: `←`/`→` or `A`/`D` move, `Space`/`W`/`↑` jump, `X`/`J` fire,
`P`/`Esc` pause, `R` restart. On touch devices, on-screen buttons appear at
the bottom.
- Optional settings: `touchControls` forces the on-screen buttons; `textSize`
and `controlSize` adjust the HUD and overlay controls.
- Best times per level and for a full run are persisted through `storage` when
that optional domain is available; otherwise they stay in memory for the
session. The runtime `theme` repaints the whole surface and the canvas palette;
without it the stylesheet fallback palette is used.
- The game declares no hard NAP requirements. It works with no injected domains
at all, so every shell feature is an optional enhancement.
## Start
```bash
pnpm install
pnpm dev
```
Build and verify the production artifact:
```bash
pnpm verify
```
`pnpm build` uses `@napplet/vite-plugin` to produce one inlined `index.html` and,
when `VITE_DEV_PRIVKEY_HEX` is set, write a local napplet manifest JSON file for
hash workflow testing. That file uses NIP-5D kinds with the NIP-5A tag schema.
## Conformance Testing
Verify the napplet conforms to the NAP protocol before publishing. Two variants,
mirroring `vitest` vs `vitest --ui`:
```bash
pnpm test:conformance # headless: build + check; non-zero exit on failure (CI)
pnpm test:conformance:ui # live web runtime; re-runs on every source change
```
It loads the build into a real `sandbox="allow-scripts"` iframe and drives the
protocol with a reference shell. Boot, forbidden-global, and degradation checks
run for a local directory; manifest, wire, and lifecycle checks report `SKIP`
unless the invocation supplies the evidence they require.
Exacting requirements for a passing build:
- Build to a **single self-contained `index.html`** (`@napplet/vite-plugin`
`artifactMode: 'single-file'` in `vite.config.ts`). NIP-5D loads a napplet via
`iframe.srcdoc` with
`sandbox="allow-scripts"` and no `allow-same-origin` (an opaque origin) — there
is no served origin from which to fetch an external `<script src>`, so the JS
must be inlined into the one file. External-asset builds do not boot.
- Do not import `@napplet/shim` from app code. The runtime injects
`window.napplet`; conformance verifies boot and emitted envelopes through the
reference runtime.
- Emit only well-formed envelopes via `@napplet/sdk`. Declare only hard NAP
requirements in `vite.config.ts`; guard optional domains with
`window.napplet?.domain` checks and a graceful fallback.
- Do not reference `window.nostr` or use direct `fetch`/`WebSocket`/`localStorage`.
The executable `index.html` does **not** carry its own aggregate hash. Before
execution, the runtime verifies manifest path blobs and recomputes the NIP-5A
aggregate carried by the manifest `x` tag.
This napplet declares no hard domains. Every shell-backed feature (best-time
storage, runtime theming, user settings) is optional and degrades gracefully when
its injected domain is absent. Add `requires` only if a future core task genuinely
cannot run without a domain.
## Included
- Vanilla Vite + TypeScript napplet app.
- Typed `@napplet/sdk` helpers over the runtime-injected `window.napplet`
namespace.
- Build-time `@napplet/vite-plugin` wiring for the single-file artifact and
optional local manifest generation.
- Default app-chrome text selection disabled in `src/styles.css`, with
opt-in controls for copyable or editable regions.
- Context documents for NIP-5D, shell boundaries, package surfaces, and authoring
patterns.
- Guidance for handling missing NAP interfaces or numbered wire formats without
submitting unnecessary protocol PRs.
- An applet-shaped starter layout: no title header (the runtime shows the
name), compact density, container-query tiers from a tiny widget to a
full-screen pane, and whole-surface runtime theming.
- A pointer to the current `napplet-*` agent skills (installed with the
skills.sh CLI) instead of forked local skill bodies.
## Authoring Context
Read these before changing protocol-facing behavior:
- `docs/nip-5d.md`
- `docs/boundaries.md`
- `docs/design-patterns.md`
- `docs/package-surfaces.md`
- `docs/new-nap-proposals.md`
- `docs/authoring-checklist.md`
The living NIP-5D source is referenced from `docs/nip-5d.md`; this template does
not treat its local notes as normative protocol text.
For agent-driven work, install the official `napplet-*` skills with the open
skills CLI; it detects your coding agents and places the skills where each one
reads them:
```bash
npx skills add napplet/napplet
```
Then ask the agent to use `napplet-make` (end to end) or `napplet-ui` (layout
only). The skills are non-normative guidance; living NIP-5D and NAP documents
remain protocol truth.
## Text Selection
The starter disables accidental text selection by default. To change the whole
napplet, set `--napplet-text-selection: text` in `src/styles.css`. To opt in one
region, add `data-napplet-select="text"` or `data-napplet-select="all"`.
## Package Scripts
```bash
pnpm dev # local Vite dev server
pnpm type-check # TypeScript strict-mode check
pnpm build # Vite production build
pnpm preview # preview dist/
pnpm test:guidance # stale-guidance and optional-domain regression checks
pnpm verify # guidance test + type-check + build
pnpm test:conformance # headless NAP conformance (build + check, CI exit code)
pnpm test:conformance:ui # live conformance web runtime, re-runs on change
```
## Plug-in controllers
Plug in a controller, click the game, then press and release a controller button
so the browser can detect it. Standard browser-mapped Xbox / PlayStation-style
controllers use the left stick or D-pad to move, A / Cross to jump or confirm,
X / Square to fire, and Start / Options to pause or resume. Y / Triangle restarts
from a menu. Release controls after connecting, returning focus, or confirming a
menu. The stick has a 25% dead zone. Click once to enable browser audio.
Gamepad input is an optional browser input surface, subject to the host's
Permissions Policy and browser support. No new NAP domain, hard requirement,
network access, or host implementation is added. Unsupported mappings and denied
access show a hint; keyboard and touch remain available. Disconnect and focus
loss release controller input; independent input sources do not cancel each other.
