From 8f4128ee34022f8ce16993c2fe8967ea714a2525 Mon Sep 17 00:00:00 2001 From: Axel Meyer Date: Sat, 16 May 2026 14:17:44 +0200 Subject: [PATCH] docs: migrate README to API-Doc-Convention (S2 Phase 3) Restructured per spec section 3.2: Abstract + Badges-Table + Topology-Block + 7 H3-API-Subsections + Conventions + Consumer pattern + Migration + CHANGELOG + References. Topology-Block auto-populated via Sporel.exe --lint --fix. Pre-commit hook installed via --install-hooks. Co-Authored-By: Claude Opus 4.7 (1M context) --- .githooks/pre-commit | 13 ++++ README.md | 147 ++++++++++++++++++++++++++++----------- scripts/install-hooks.sh | 3 + 3 files changed, 124 insertions(+), 39 deletions(-) create mode 100644 .githooks/pre-commit create mode 100644 scripts/install-hooks.sh diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100644 index 0000000..1ebdad0 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,13 @@ +#!/bin/sh +# Sporel API-Doc-Convention pre-commit hook. +SPOREL_EXE="${SPOREL_EXE:-$(command -v Sporel.exe 2>/dev/null || command -v sporel 2>/dev/null)}" +if [ -z "$SPOREL_EXE" ]; then + echo "INFO: Sporel.exe not on PATH. Skipping lint." >&2 + exit 0 +fi +"$SPOREL_EXE" --lint="$(pwd)" --fix +RC=$? +if git diff --cached --name-only | grep -qx 'README.md'; then + git add README.md +fi +exit $RC diff --git a/README.md b/README.md index 929476a..be5a50b 100644 --- a/README.md +++ b/README.md @@ -2,44 +2,107 @@ P.0 action-mapping lib. Decouples physical keys from logical actions. Polling-based (`is_action_down`) + edge-based (`was_action_pressed`, `was_action_released`). -Direction-vector helper composes 4 actions into `{x, y}` ∈ {-1, 0, +1}². +Direction-vector helper composes 4 actions into `{x, y}` in {-1, 0, +1}^2. -- Lib-ID: `lib-core.input` -- Version: `0.4.0` -- Spec v0.4.0 (P.3.10): `meta/docs/superpowers/specs/2026-05-15-p3-housekeeping-design.md` -- Spec v0.3.0 (P.3.3): `meta/docs/superpowers/specs/2026-05-12-p3-3-mouse-input-design.md` -- Spec v0.2.0 (P.2.6): `meta/docs/superpowers/specs/2026-05-11-engine-input-symbolic-keys-design.md` -- Spec v0.1.0 (P.0): `meta/docs/superpowers/specs/2026-05-09-p0-lib-input-design.md` +| Field | Value | +|---|---| +| Version | 0.4.0 | +| Lib-ID | lib-core.input | +| Requires | (none) | +| Tags | input, action-mapping | -Forward-compat stubs (DEPRECATED-MVP) for mouse-button actions, gamepad -bindings, action-context-stack, key-rebinding-config, modifier-combos, -analog-axis, right-modifier-keys, function-keys, -special-keys (delete/insert/home/end/pageup/pagedown). + +```mermaid +graph LR + this["lib-core.input"] + engine["engine.*"] + this --> engine +``` + ## API -- `input.bind(action_name, {"keyname1", "keyname2", ...})` — bind multi-key array (string-keys only, see §Key-Names) -- `input.unbind(action_name)` — remove binding -- `input.is_action_down(action) → bool` — any bound key currently down -- `input.was_action_pressed(action) → bool` — any bound key just-pressed (press-edge; true for exactly one frame per key-down event) -- `input.was_action_released(action) → bool` — any bound key just-released (release-edge; true for exactly one frame per key-up event; triggers once per press-release cycle) -- `input.direction(left, right, up, down) → {x, y}` — vec in {-1,0,+1}², Y-down-positive -- `input.action_count() → number` — bound-actions count +### `input.bind(action_name, keys)` +**Syntax:** `input.bind(action_name: string, keys: string[]) -> void` -## Key-Names (v0.2.0) +**Example:** +```lua +input.bind("move_left", { "a", "left" }) +``` + +**Description:** Bindet einen logischen Action-Namen an ein Array von Key-Names. Atomic: wenn ein Key invalid ist, fehlt der gesamte Bind ohne partial state. Lua `error(...)` für `bind()` misuse (caller-bug fast-fail). + +### `input.unbind(action_name)` +**Syntax:** `input.unbind(action_name: string) -> void` + +**Example:** +```lua +input.unbind("move_left") +``` + +**Description:** Entfernt eine Action-Binding. No-op wenn Action nicht gebunden. + +### `input.is_action_down(action) -> bool` +**Syntax:** `input.is_action_down(action: string) -> bool` + +**Example:** +```lua +if input.is_action_down("move_left") then ... end +``` + +**Description:** Returns true wenn irgendein gebundener Key der Action gerade gedrückt ist. Silent-false für undefined actions. + +### `input.was_action_pressed(action) -> bool` +**Syntax:** `input.was_action_pressed(action: string) -> bool` + +**Example:** +```lua +if input.was_action_pressed("quit") then engine.exit(0) end +``` + +**Description:** Press-Edge — true für genau einen Frame pro Key-Down-Event. Triggers einmal pro Press-Release-Cycle. + +### `input.was_action_released(action) -> bool` +**Syntax:** `input.was_action_released(action: string) -> bool` + +**Example:** +```lua +if input.was_action_released("attack") then finish_swing() end +``` + +**Description:** Release-Edge — true für genau einen Frame pro Key-Up-Event. v0.4.0+. Triggers einmal pro Press-Release-Cycle. + +### `input.direction(left, right, up, down)` +**Syntax:** `input.direction(left: string, right: string, up: string, down: string) -> {x: int, y: int}` + +**Example:** +```lua +local d = input.direction("move_left", "move_right", "move_up", "move_down") +-- d.x, d.y in {-1, 0, +1} +``` + +**Description:** Komponiert 4 Actions in einen Direction-Vector. Y-down-positive per ADR-0031. + +### `input.action_count() -> number` +**Syntax:** `input.action_count() -> number` + +**Description:** Returns Anzahl der gebundenen Actions. Debug-friendly. + +## Key-Names Strict-lowercase. Engine-internal `engine.input.KEY_*` integer-args are rejected. | Category | Names | |---|---| -| Letters | `a`, `b`, …, `z` | -| Numbers | `0`, `1`, …, `9` | +| Letters | `a`, `b`, ..., `z` | +| Numbers | `0`, `1`, ..., `9` | | Arrows | `left`, `right`, `up`, `down` | | Whitespace + control | `space`, `tab`, `enter`, `backspace`, `escape` | | Modifiers (left-side) | `shift` (= `lshift`), `ctrl` (= `lctrl`), `alt` (= `lalt`) | -| Console-toggle | `^` (DE: Zirkumflex / US: backtick — Quake/Source-Tradition) | +| Console-toggle | `^` (DE: Zirkumflex / US: backtick) | +| Mouse | `mouse_left`, `mouse_right`, `mouse_middle` (v0.3.0+) | -Right-modifier-aliases (`rshift`, `rctrl`, `ralt`) + function-keys (`f1`..`f12`) + special-keys (`delete`, `insert`, …) are **forthcoming** — additiv wenn `engine.input` die entsprechenden `KEY_*`-Konstanten exposed. +Right-modifier-aliases (`rshift`, `rctrl`, `ralt`) + function-keys (`f1`..`f12`) + special-keys (`delete`, `insert`, ...) are **forthcoming**. Unknown key-names raise a Lua-error with hint to this README. @@ -47,8 +110,8 @@ Unknown key-names raise a Lua-error with hint to this README. - Y-down-positive per Sporel pixel-convention (ADR-0031). - Multi-key per action (e.g., `{"a", "left"}` for both A and Left-Arrow). -- Silent-false for undefined actions (debug-friendly; lookup-misuse won't drown the game-loop in errors). -- Lua `error(...)` for `bind()` misuse (caller-bug fast-fail). Atomic: invalid key fails the entire bind without partial-state mutation. +- Silent-false for undefined actions (debug-friendly). +- Lua `error(...)` for `bind()` misuse (caller-bug fast-fail). Atomic. ## Consumer pattern @@ -63,32 +126,38 @@ input.bind("move_down", { "s", "down" }) function update(ctx, dt) if input.was_action_pressed("quit") then engine.exit(0) end local d = input.direction("move_left", "move_right", "move_up", "move_down") - -- d.x, d.y ∈ {-1, 0, +1} + -- d.x, d.y in {-1, 0, +1} end ``` -## Migration v0.1.0 → v0.2.0 +## Migration v0.1.0 -> v0.2.0 -`bind()` switched from integer `engine.input.KEY_*` arguments to lowercase string-keys (per P.2.6). Reason: stop leaking engine-internal namespace into modules; align with ADR-0001 „engine knows verbs, libs bring nouns". +`bind()` switched from integer `engine.input.KEY_*` arguments to lowercase string-keys (per P.2.6). Reason: stop leaking engine-internal namespace into modules. | v0.1.0 | v0.2.0 | |---|---| | `bind("quit", { engine.input.KEY_ESCAPE })` | `bind("quit", { "escape" })` | -| `bind("move", { engine.input.KEY_A, engine.input.KEY_LEFT })` | `bind("move", { "a", "left" })` | - -`engine.input.KEY_*` constants remain available for direct engine-input use (e.g. `engine.input.is_key_down(engine.input.KEY_F1)` for edge-cases without bind), but `lib-core.input.bind` strict-rejects them. ## CHANGELOG -## v0.4.0 (P.3.10, 2026-05-15) +### v0.4.0 (P.3.10, 2026-05-15) - Added `was_action_released(action_name)` — release-edge detection per action. - Consumers can now detect key-up edges without per-frame state tracking. -- Engine surface: `engine.input.was_released` + `was_mouse_released`. -## v0.3.0 (P.3.3, 2026-05-12) +### v0.3.0 (P.3.3, 2026-05-12) - `bind()` now accepts unified key+mouse strings via BIND_MAP with kind-tagged entries. -- Mixed bindings supported (e.g. `{"e", "mouse_left"}`). -- Backward-compatible with v0.2.0 callsites. -## v0.2.0 (P.2.6, 2026-05-11) -- `bind()` accepts string-key-names only. Integer `engine.input.KEY_*` args are rejected. +### v0.2.0 (P.2.6, 2026-05-11) +- `bind()` accepts string-key-names only. + +### v0.1.0 (P.0) +- Initial release. + +## References + +- Spec v0.4.0 (P.3.10): `meta/docs/superpowers/specs/2026-05-15-p3-housekeeping-design.md` +- Spec v0.3.0 (P.3.3): `meta/docs/superpowers/specs/2026-05-12-p3-3-mouse-input-design.md` +- Spec v0.2.0 (P.2.6): `meta/docs/superpowers/specs/2026-05-11-engine-input-symbolic-keys-design.md` +- Spec v0.1.0 (P.0): `meta/docs/superpowers/specs/2026-05-09-p0-lib-input-design.md` +- ADR-0001 (engine knows verbs, libs bring nouns) +- ADR-0031 (pixel-convention: Y-down-positive) +- ADR-0038 (API-Doc-Convention) diff --git a/scripts/install-hooks.sh b/scripts/install-hooks.sh new file mode 100644 index 0000000..2ff6e41 --- /dev/null +++ b/scripts/install-hooks.sh @@ -0,0 +1,3 @@ +#!/bin/sh +git config core.hooksPath .githooks +echo "Hooks activated."