mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-09-11 04:34:30 +00:00
docs: amend Constitution Principle II for tsc --noEmit
- Constitution II: permit `tsc --noEmit` with allowJs/checkJs as a CI/dev-time check and encourage JSDoc + `// @ts-check`; emit-mode TypeScript, .ts/.tsx source, JSX, frameworks, and bundlers stay forbidden in core. Bump version 1.0.0 -> 1.1.0. - Constitution IV: add static/slopsmith.d.ts to the stable-contract surface -- breaking changes need a "Migration notes" CHANGELOG entry. - CHANGELOG: note the typecheck step + ambient .d.ts under [Unreleased]. - CLAUDE.md: point the Frontend Conventions section at slopsmith.d.ts as the typed plugin-contract source of truth. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
6f433d9cea
commit
de7ced6429
@@ -41,12 +41,17 @@ is Tailwind CSS, served as a prebuilt static stylesheet
|
|||||||
(`static/tailwind.min.css`, regenerated by `scripts/build-tailwind.sh`)
|
(`static/tailwind.min.css`, regenerated by `scripts/build-tailwind.sh`)
|
||||||
— never the runtime Play CDN, whose on-the-fly JIT rescans the DOM on
|
— never the runtime Play CDN, whose on-the-fly JIT rescans the DOM on
|
||||||
the main thread and caused sustained frame drops with the 3D highway
|
the main thread and caused sustained frame drops with the 3D highway
|
||||||
(slopsmith-desktop#110). No React, Vue, Svelte, bundler, transpiler, or
|
(slopsmith-desktop#110). No React, Vue, Svelte, bundler, or transpiler
|
||||||
TypeScript appears in the core static tree, and no build step runs on
|
appears in the core static tree, and no build step runs on the serve
|
||||||
the serve path: the Tailwind build is a maintainer-only one-shot whose
|
path: the Tailwind build is a maintainer-only one-shot whose output is
|
||||||
output is committed, so Docker / desktop / end users never build. New
|
committed, so Docker / desktop / end users never build. `tsc --noEmit`
|
||||||
features extend `app.js` and the existing globals (`window.playSong`,
|
with `allowJs` / `checkJs` is permitted as a CI / dev-time type check
|
||||||
`window.showScreen`, `window.createHighway`, `window.slopsmith`).
|
— JSDoc type annotations and `// @ts-check` directives in core `.js`
|
||||||
|
files are encouraged. TypeScript stays a dev-only devDependency; emit
|
||||||
|
-mode TypeScript, `.ts` / `.tsx` source, JSX, frameworks, and bundlers
|
||||||
|
remain forbidden in core. New features extend `app.js` and the existing
|
||||||
|
globals (`window.playSong`, `window.showScreen`,
|
||||||
|
`window.createHighway`, `window.slopsmith`).
|
||||||
|
|
||||||
**Non-negotiable rules**
|
**Non-negotiable rules**
|
||||||
|
|
||||||
@@ -63,6 +68,11 @@ features extend `app.js` and the existing globals (`window.playSong`,
|
|||||||
and does not re-apply the base reset core already provides. Plugins
|
and does not re-apply the base reset core already provides. Plugins
|
||||||
MUST NOT load the Tailwind Play CDN (or any runtime CSS JIT) — the
|
MUST NOT load the Tailwind Play CDN (or any runtime CSS JIT) — the
|
||||||
same no-CDN, build-free-at-serve rule that binds core binds plugins.
|
same no-CDN, build-free-at-serve rule that binds core binds plugins.
|
||||||
|
- `tsc --noEmit` with `allowJs` / `checkJs` is permitted as a CI /
|
||||||
|
dev-time type check. JSDoc type annotations and `// @ts-check`
|
||||||
|
directives in core `.js` files are encouraged. TypeScript stays a
|
||||||
|
dev-only devDependency — emit-mode TypeScript, `.ts` / `.tsx` source,
|
||||||
|
JSX, frameworks, and bundlers remain forbidden in core.
|
||||||
- New UI state lives in `localStorage` (or a backend endpoint), not in a
|
- New UI state lives in `localStorage` (or a backend endpoint), not in a
|
||||||
framework store.
|
framework store.
|
||||||
- Naming: camelCase JS, kebab-case CSS, snake_case plugin IDs. Player
|
- Naming: camelCase JS, kebab-case CSS, snake_case plugin IDs. Player
|
||||||
@@ -117,6 +127,12 @@ format. Both must keep playing across releases.
|
|||||||
- Existing arrangement IDs, sloppak manifests, and the highway
|
- Existing arrangement IDs, sloppak manifests, and the highway
|
||||||
WebSocket message shape are stable contracts. Breaking changes
|
WebSocket message shape are stable contracts. Breaking changes
|
||||||
require a CHANGELOG entry under "Migration notes".
|
require a CHANGELOG entry under "Migration notes".
|
||||||
|
- The plugin-facing JS contracts — `window.slopsmith`, `window.highway`,
|
||||||
|
the highway WebSocket message shape, and the `setRenderer`
|
||||||
|
visualization contract — are declared in `static/slopsmith.d.ts` and
|
||||||
|
form part of this stable-contract surface. The `.d.ts` is the typed
|
||||||
|
source of truth; a breaking change to it requires the same CHANGELOG
|
||||||
|
entry under "Migration notes".
|
||||||
|
|
||||||
### V. Pure-Function Core Libraries, Tested
|
### V. Pure-Function Core Libraries, Tested
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- **Perf**: reduce per-frame allocations in the 2D highway chord + lyric render paths. `_ensureChordRenderCache` now also caches `sortedNotes` / `nonZeroNotes` / `nonZeroFrets` / `allMuted` / `hasMultipleNotes` (computed once per chord, invalidated on `src` / `_inverted` / `chordTemplates` change — the third key catches a stale `isOpen`-derived classification when the WS `chord_templates` message lands after the final `chords` chunk), so `drawChords` no longer re-sorts / re-filters / spreads min-max per visible chord per frame. The in-chord unison bend classification is folded inline (no `chordPositions.filter` × 2 per frame). `drawLyrics` memoizes `ctx.measureText` results in a two-level `Map<fontSize, Map<text, width>>` so cache hits don't allocate a composite string key. Lit-sustain shimmer in `drawSustains` swaps the 4 per-note-per-frame `Math.random()` calls for a 64-entry precomputed jitter LUT (xorshift32-seeded — the LUT contents are reload-stable and test-reproducible; rendered shimmer is deterministic per `createHighway()` instance, since the seed includes that instance's `_frameIdx`) indexed by `(frameIdx + n.s + ⌊n.t·60⌋)`, visually indistinguishable and allocation-free.
|
- **Perf**: reduce per-frame allocations in the 2D highway chord + lyric render paths. `_ensureChordRenderCache` now also caches `sortedNotes` / `nonZeroNotes` / `nonZeroFrets` / `allMuted` / `hasMultipleNotes` (computed once per chord, invalidated on `src` / `_inverted` / `chordTemplates` change — the third key catches a stale `isOpen`-derived classification when the WS `chord_templates` message lands after the final `chords` chunk), so `drawChords` no longer re-sorts / re-filters / spreads min-max per visible chord per frame. The in-chord unison bend classification is folded inline (no `chordPositions.filter` × 2 per frame). `drawLyrics` memoizes `ctx.measureText` results in a two-level `Map<fontSize, Map<text, width>>` so cache hits don't allocate a composite string key. Lit-sustain shimmer in `drawSustains` swaps the 4 per-note-per-frame `Math.random()` calls for a 64-entry precomputed jitter LUT (xorshift32-seeded — the LUT contents are reload-stable and test-reproducible; rendered shimmer is deterministic per `createHighway()` instance, since the seed includes that instance's `_frameIdx`) indexed by `(frameIdx + n.s + ⌊n.t·60⌋)`, visually indistinguishable and allocation-free.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- Optional JS type checking via `tsc --noEmit`. Added a `static/slopsmith.d.ts` ambient declaration file — the typed source of truth for the plugin-facing contract (`window.slopsmith`, `window.highway`, the highway WebSocket message shape, the `setRenderer` visualization contract, the keyboard-shortcut API) — plus a `tsconfig.json`, a `typecheck` npm script, and a CI step. TypeScript is a dev-only devDependency: no build/emit step, no runtime dependency, every shipped `.js` file stays byte-identical. Core `.js` files opt in individually with a `// @ts-check` directive (currently `static/app.js` and `static/highway.js`); files without it are unaffected.
|
||||||
- **Player progression: Mastery Rank, instrument-path challenges, daily/weekly quests, Decibels currency, cosmetics shop (spec 010).** Onboarding gains two steps: pick one or more **instrument paths** (Guitar / Bass / Drums — data-driven, more can ship as content) and a **calibration challenge** offer (play the bundled Slopsmith Diagnostic with note detection at 100% accuracy — or skip; either way you reach **Mastery Rank 1**, and a skipped calibration can still be completed later from the Progress screen). Each path levels by completing a content-defined number of **challenges** (any order) from that level's set; Mastery Rank = onboarding rank + the sum of path levels, starting at 0 on a fresh install. The existing unified XP backend is untouched but the frontend renames it to **Decibels (dB)** — a spendable currency earned ONLY by playing (songs, FeedBarcade rounds, quest rewards; no real-money path exists or may be added) — with spend tracked in a separate wallet so lifetime earnings stay monotonic. Rotating **daily/weekly quests** (deterministic per period, lazy instantiation, local-midnight / Monday resets) award dB and feed `quest_completed` challenges. A new **Progress** screen (rank hero, per-path checklists, quest countdowns, add-a-path) and **Shop** screen (themes via CSS-variable swaps under `html[data-fb-theme]`, avatar frames; atomic balance-checked purchases — 402 on insufficient dB, 409 on re-buy) join the v3 nav, and the topbar badge now shows Rank + challenge-set progress + dB balance. All definitions live in `data/progression/` JSON (paths/levels/challenges, quest pools, shop catalog) — adding a rank, challenge, quest, or cosmetic is a content edit + restart, never code; invalid content degrades to logged warnings. New tables (additive + idempotent): `progression_state`, `player_paths`, `challenge_progress`, `quest_state`, `wallet`, `shop_owned`, `shop_equipped`. New endpoints: `GET /api/progression`, `POST /api/progression/paths|onboarding|events` (events whitelists `minigame_run`; `song_completed` stays server-derived inside `POST /api/stats`, which now resolves the instrument server-side and reports an additive `progression` outcome key), `GET /api/shop`, `POST /api/shop/buy|equip`; equipped cosmetics ride along on `GET /api/profile`. A new **`progression` capability domain** (core-owned, kind: command, safety: safe — `inspect`, `record-event`, `list-shop`, `buy-item`/`equip-item` gated on user action) emits `challenge-completed`/`quest-completed`/`path-level-up`/`rank-changed`/`db-changed`/`calibration-completed`/`cosmetic-equipped`, mirrored as `progression:*` window events, with a redaction-safe diagnostics contributor; backend plugins get the symmetric `record_progression_event` context hook (the bundled minigames hub reports runs through it, guarded for standalone). Spec: `specs/010-progression-domain/`. Tests: `tests/test_progression.py`, `tests/test_progression_api.py`. **Migration notes:** existing XP totals carry over as lifetime dB (balance = lifetime − spent); resetting a per-source XP contribution (e.g. a minigames profile reset) after spending can clamp the spendable balance to 0 until new dB is earned; drums-path v1 content uses currently-satisfiable goals (arcade rounds, quests, any-instrument plays) until drums scoring lands.
|
- **Player progression: Mastery Rank, instrument-path challenges, daily/weekly quests, Decibels currency, cosmetics shop (spec 010).** Onboarding gains two steps: pick one or more **instrument paths** (Guitar / Bass / Drums — data-driven, more can ship as content) and a **calibration challenge** offer (play the bundled Slopsmith Diagnostic with note detection at 100% accuracy — or skip; either way you reach **Mastery Rank 1**, and a skipped calibration can still be completed later from the Progress screen). Each path levels by completing a content-defined number of **challenges** (any order) from that level's set; Mastery Rank = onboarding rank + the sum of path levels, starting at 0 on a fresh install. The existing unified XP backend is untouched but the frontend renames it to **Decibels (dB)** — a spendable currency earned ONLY by playing (songs, FeedBarcade rounds, quest rewards; no real-money path exists or may be added) — with spend tracked in a separate wallet so lifetime earnings stay monotonic. Rotating **daily/weekly quests** (deterministic per period, lazy instantiation, local-midnight / Monday resets) award dB and feed `quest_completed` challenges. A new **Progress** screen (rank hero, per-path checklists, quest countdowns, add-a-path) and **Shop** screen (themes via CSS-variable swaps under `html[data-fb-theme]`, avatar frames; atomic balance-checked purchases — 402 on insufficient dB, 409 on re-buy) join the v3 nav, and the topbar badge now shows Rank + challenge-set progress + dB balance. All definitions live in `data/progression/` JSON (paths/levels/challenges, quest pools, shop catalog) — adding a rank, challenge, quest, or cosmetic is a content edit + restart, never code; invalid content degrades to logged warnings. New tables (additive + idempotent): `progression_state`, `player_paths`, `challenge_progress`, `quest_state`, `wallet`, `shop_owned`, `shop_equipped`. New endpoints: `GET /api/progression`, `POST /api/progression/paths|onboarding|events` (events whitelists `minigame_run`; `song_completed` stays server-derived inside `POST /api/stats`, which now resolves the instrument server-side and reports an additive `progression` outcome key), `GET /api/shop`, `POST /api/shop/buy|equip`; equipped cosmetics ride along on `GET /api/profile`. A new **`progression` capability domain** (core-owned, kind: command, safety: safe — `inspect`, `record-event`, `list-shop`, `buy-item`/`equip-item` gated on user action) emits `challenge-completed`/`quest-completed`/`path-level-up`/`rank-changed`/`db-changed`/`calibration-completed`/`cosmetic-equipped`, mirrored as `progression:*` window events, with a redaction-safe diagnostics contributor; backend plugins get the symmetric `record_progression_event` context hook (the bundled minigames hub reports runs through it, guarded for standalone). Spec: `specs/010-progression-domain/`. Tests: `tests/test_progression.py`, `tests/test_progression_api.py`. **Migration notes:** existing XP totals carry over as lifetime dB (balance = lifetime − spent); resetting a per-source XP contribution (e.g. a minigames profile reset) after spending can clamp the spendable balance to 0 until new dB is earned; drums-path v1 content uses currently-satisfiable goals (arcade rounds, quests, any-instrument plays) until drums scoring lands.
|
||||||
- **3D highway score FX (notedetect game-scoring layer).** The bundled `highway_3d` renderer now visualizes the scoring layer shipped in slopsmith-plugin-notedetect ≥1.13: floating **"+N" score pops** above each judged gem (sourced from the note-state provider's new `{ points, mult, popKey }` verdict fields — chord members share the chord-level `popKey`, so a chord pops once, not once per string), plus session-level FX from the new `notedetect:fx` event — a particle burst at the strike line on streak milestones (25/50/every 100), an expanding ring pulse on multiplier tier-ups (×2/×3/×4), and a brief red wash when a ≥10 streak breaks. Colors and the pop font follow the user's notedetect scoring-UI skin (`slopsmith_notedetect_skin`: neon/esports/metal, refreshed live on the `notedetect:skin` bus event). Everything renders on the existing 2D overlay canvas from fixed-size slot pools — no Three.js geometry, no text-sprite cache traffic, near-zero cost when idle — and degrades to a silent no-op with older notedetect builds (the new fields/events simply never arrive). Splitscreen panels scope FX to their own detector instance via the bubbling per-panel `notedetect:fx` dispatch. `plugins/highway_3d` v3.24.1.
|
- **3D highway score FX (notedetect game-scoring layer).** The bundled `highway_3d` renderer now visualizes the scoring layer shipped in slopsmith-plugin-notedetect ≥1.13: floating **"+N" score pops** above each judged gem (sourced from the note-state provider's new `{ points, mult, popKey }` verdict fields — chord members share the chord-level `popKey`, so a chord pops once, not once per string), plus session-level FX from the new `notedetect:fx` event — a particle burst at the strike line on streak milestones (25/50/every 100), an expanding ring pulse on multiplier tier-ups (×2/×3/×4), and a brief red wash when a ≥10 streak breaks. Colors and the pop font follow the user's notedetect scoring-UI skin (`slopsmith_notedetect_skin`: neon/esports/metal, refreshed live on the `notedetect:skin` bus event). Everything renders on the existing 2D overlay canvas from fixed-size slot pools — no Three.js geometry, no text-sprite cache traffic, near-zero cost when idle — and degrades to a silent no-op with older notedetect builds (the new fields/events simply never arrive). Splitscreen panels scope FX to their own detector instance via the bubbling per-panel `notedetect:fx` dispatch. `plugins/highway_3d` v3.24.1.
|
||||||
- **3D highway: slide direction arrows + gem-follow animation.** Slide notes now show a ›/‹ arrow indicating which way the slide goes — on the note/gem itself, as an early preview on the neck before the note arrives, and (optionally) chained further ahead for multi-leg slides — each independently toggleable in Settings → 3D Highway (`slideArrowApproachVisible`, `slideArrowNeckVisible`, `slideArrowChainPreviewVisible`). The note gem also now visually glides from its starting fret to the slide's destination over the note's sustain and holds there through the brief post-sustain linger, instead of snapping back to the starting fret — most noticeable on unpitched "slide to nothing" notes. `plugins/highway_3d` v3.25.2.
|
- **3D highway: slide direction arrows + gem-follow animation.** Slide notes now show a ›/‹ arrow indicating which way the slide goes — on the note/gem itself, as an early preview on the neck before the note arrives, and (optionally) chained further ahead for multi-leg slides — each independently toggleable in Settings → 3D Highway (`slideArrowApproachVisible`, `slideArrowNeckVisible`, `slideArrowChainPreviewVisible`). The note gem also now visually glides from its starting fret to the slide's destination over the note's sustain and holds there through the brief post-sustain linger, instead of snapping back to the starting fret — most noticeable on unpitched "slide to nothing" notes. `plugins/highway_3d` v3.25.2.
|
||||||
|
|||||||
@@ -547,6 +547,7 @@ Sloppak is the preferred format for new features. The [Stems plugin](https://git
|
|||||||
- **Styling** — Tailwind CSS utility classes, dark theme (`bg-dark-600`, `text-gray-300`, accent `#4080e0`, gold `#e8c040`). Tailwind is served as a **prebuilt** stylesheet (`static/tailwind.min.css`, regenerated by `bash scripts/build-tailwind.sh`), **never** the runtime Play CDN — the CDN's on-the-fly JIT rescanned the DOM on the main thread and dropped ~26% of frames with the 3D highway (slopsmith-desktop#110). The committed CSS only contains classes the build scanner saw, so CI (`tailwind-fresh`) rebuilds and diffs it; run the build script and commit when you add new classes. A plugin that uses classes not guaranteed in core (notably arbitrary values like `w-[37px]`) MUST ship its own compiled stylesheet via the `styles` manifest key, built with `corePlugins.preflight = false` (utilities only — core ships the one base reset). Plugins MUST NOT load the Tailwind Play CDN or any runtime CSS JIT. See constitution Principle II.
|
- **Styling** — Tailwind CSS utility classes, dark theme (`bg-dark-600`, `text-gray-300`, accent `#4080e0`, gold `#e8c040`). Tailwind is served as a **prebuilt** stylesheet (`static/tailwind.min.css`, regenerated by `bash scripts/build-tailwind.sh`), **never** the runtime Play CDN — the CDN's on-the-fly JIT rescanned the DOM on the main thread and dropped ~26% of frames with the 3D highway (slopsmith-desktop#110). The committed CSS only contains classes the build scanner saw, so CI (`tailwind-fresh`) rebuilds and diffs it; run the build script and commit when you add new classes. A plugin that uses classes not guaranteed in core (notably arbitrary values like `w-[37px]`) MUST ship its own compiled stylesheet via the `styles` manifest key, built with `corePlugins.preflight = false` (utilities only — core ships the one base reset). Plugins MUST NOT load the Tailwind Play CDN or any runtime CSS JIT. See constitution Principle II.
|
||||||
- **Naming** — camelCase for JS functions, kebab-case for CSS classes, snake_case for plugin IDs
|
- **Naming** — camelCase for JS functions, kebab-case for CSS classes, snake_case for plugin IDs
|
||||||
- **Player layout** — `#player` is `display:flex; flex-direction:column; position:fixed; inset:0`. `#highway` is `flex:1`. `#player-controls` sits at the bottom. Hiding the highway collapses the layout — use `margin-top: auto` on controls if you need to hide it.
|
- **Player layout** — `#player` is `display:flex; flex-direction:column; position:fixed; inset:0`. `#highway` is `flex:1`. `#player-controls` sits at the bottom. Hiding the highway collapses the layout — use `margin-top: auto` on controls if you need to hide it.
|
||||||
|
- **Plugin contract types** — the plugin-facing JS surface (`window.slopsmith`, `window.highway`, the highway WebSocket message shape, the `setRenderer` contract, the keyboard-shortcut API) is declared in `static/slopsmith.d.ts`. That file is the typed source of truth — consult it for exact signatures, and update it when you change a contract. Core `.js` files opt into checking with a `// @ts-check` directive on line 1; `npm run typecheck` (`tsc --noEmit`, dev/CI-only) verifies them. No build or emit step — the shipped files are plain JS.
|
||||||
|
|
||||||
## Backend Conventions
|
## Backend Conventions
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user