mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-12 19:59:35 +00:00
chore(hotkeys): remove sloppak-convert library hotkey (#594)
* chore(hotkeys): remove sloppak-convert library hotkey
Removes the 'c' keyboard shortcut for converting library entries to
.sloppak. The shortcut was defined in two places:
- The no-op registerShortcut() entry that only existed to show in the
? help panel (the Sloppak Converter plugin handles conversion and
can register its own shortcut via window.registerShortcut).
- The c dispatch in the library-entry keydown handler
({ c: 'button.sloppak-convert-btn', ... }) that triggered the
plugin button.
* test+docs: update tests & CHANGELOG for removed `c` convert hotkey
The previous commit removed the `c` library hotkey but left three
assertions in tests/browser/keyboard-shortcuts.spec.ts that require it,
which fail deterministically (the two registry tests read window._panels
directly, independent of environment):
- should list all registered shortcuts (required {key:'c',scope:'library'})
- should have correct shortcut scopes (expected library::c)
- should show library shortcuts in help modal (Convert library entry / c)
Drop those assertions and record the removal under CHANGELOG
[Unreleased] -> Removed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: byrongamatos <xasiklas@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
byrongamatos
parent
64801d5735
commit
82db8e56b1
@@ -48,6 +48,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- **Perf**: replace runtime Tailwind Play CDN with a prebuilt static stylesheet (`static/tailwind.min.css`). The Play CDN's runtime JIT scanned the DOM ~1.8x/sec on the main thread (~37 ms blocking spans), dropping ~26% of frames in long playback sessions with the 3D highway as default. Theme extensions (dark/accent/gold colors, Inter font) move to `tailwind.config.js`; regen via `bash scripts/build-tailwind.sh`. No runtime build step — the generated CSS is committed. Fixes feedBack-desktop#110.
|
- **Perf**: replace runtime Tailwind Play CDN with a prebuilt static stylesheet (`static/tailwind.min.css`). The Play CDN's runtime JIT scanned the DOM ~1.8x/sec on the main thread (~37 ms blocking spans), dropping ~26% of frames in long playback sessions with the 3D highway as default. Theme extensions (dark/accent/gold colors, Inter font) move to `tailwind.config.js`; regen via `bash scripts/build-tailwind.sh`. No runtime build step — the generated CSS is committed. Fixes feedBack-desktop#110.
|
||||||
- **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.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- **`c` library hotkey ("Convert to .sloppak") removed from core.** Core hardcoded a plugin-specific shortcut: a documentation-only `registerShortcut({ key: 'c', scope: 'library' })` no-op plus a `c → button.sloppak-convert-btn` entry in the library keydown handler that fired the Sloppak Converter plugin's button. Per the plugins-own-their-behavior principle, core no longer ships this hotkey — the convert button still works by click, and the Sloppak Converter plugin can register its own `c` shortcut via `window.registerShortcut()` if keyboard access is wanted. The `f` (favorite) and `e` (edit) library hotkeys, which drive core buttons, are unchanged. Help-modal/registry tests in `tests/browser/keyboard-shortcuts.spec.ts` updated to drop the `c` assertions.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- **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 FeedBack 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 FeedBack 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 feedBack-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 (`feedBack_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 feedBack-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 (`feedBack_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.
|
||||||
|
|||||||
+2
-13
@@ -923,15 +923,13 @@ document.addEventListener('keydown', (e) => {
|
|||||||
// library entry — works on both grid cards and tree rows. Each
|
// library entry — works on both grid cards and tree rows. Each
|
||||||
// dispatches to a button class that the entry markup already
|
// dispatches to a button class that the entry markup already
|
||||||
// exposes, so plugins can keep owning the actual behavior:
|
// exposes, so plugins can keep owning the actual behavior:
|
||||||
// c → .sloppak-convert-btn (Sloppak Converter plugin)
|
|
||||||
// f → .fav-btn (favorite heart toggle)
|
// f → .fav-btn (favorite heart toggle)
|
||||||
// e → .edit-btn (edit metadata modal)
|
// e → .edit-btn (edit metadata modal)
|
||||||
// No-op when no entry is currently focused / selected, when the
|
// No-op when no entry is currently focused / selected, when the
|
||||||
// entry doesn't expose the requested button (e.g. a sloppak
|
// entry doesn't expose the requested button, or when the button is disabled.
|
||||||
// entry has no convert button), or when the button is disabled.
|
|
||||||
// Bails on text input / drawer focus so single-letter typing in
|
// Bails on text input / drawer focus so single-letter typing in
|
||||||
// inputs still works.
|
// inputs still works.
|
||||||
const entryShortcut = { c: 'button.sloppak-convert-btn', f: 'button.fav-btn', e: 'button.edit-btn' }[e.key.toLowerCase()];
|
const entryShortcut = { f: 'button.fav-btn', e: 'button.edit-btn' }[e.key.toLowerCase()];
|
||||||
if (entryShortcut) {
|
if (entryShortcut) {
|
||||||
if (_isInsideInteractiveControl(document.activeElement)) return;
|
if (_isInsideInteractiveControl(document.activeElement)) return;
|
||||||
const ae = document.activeElement;
|
const ae = document.activeElement;
|
||||||
@@ -9435,15 +9433,6 @@ registerShortcut({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
registerShortcut({
|
|
||||||
key: 'c',
|
|
||||||
description: 'Convert library entry to .sloppak',
|
|
||||||
scope: 'library',
|
|
||||||
handler: () => {
|
|
||||||
// Handled by library navigation - this is for documentation only
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
registerShortcut({
|
registerShortcut({
|
||||||
key: 'f',
|
key: 'f',
|
||||||
description: 'Toggle favorite',
|
description: 'Toggle favorite',
|
||||||
|
|||||||
@@ -90,7 +90,6 @@ test.describe('Keyboard Shortcuts', () => {
|
|||||||
const required = [
|
const required = [
|
||||||
{ key: '?', scope: 'global' },
|
{ key: '?', scope: 'global' },
|
||||||
{ key: '/', scope: 'library' },
|
{ key: '/', scope: 'library' },
|
||||||
{ key: 'c', scope: 'library' },
|
|
||||||
{ key: 'f', scope: 'library' },
|
{ key: 'f', scope: 'library' },
|
||||||
{ key: 'e', scope: 'library' },
|
{ key: 'e', scope: 'library' },
|
||||||
{ key: 'Space', scope: 'player' },
|
{ key: 'Space', scope: 'player' },
|
||||||
@@ -125,9 +124,7 @@ test.describe('Keyboard Shortcuts', () => {
|
|||||||
// Library shortcuts should be visible on library screen
|
// Library shortcuts should be visible on library screen
|
||||||
await expect(modal).toContainText('Focus search');
|
await expect(modal).toContainText('Focus search');
|
||||||
await expect(modal).toContainText('/');
|
await expect(modal).toContainText('/');
|
||||||
await expect(modal).toContainText('Convert library entry');
|
|
||||||
await expect(modal).toContainText('c');
|
|
||||||
|
|
||||||
// Player shortcuts should NOT be visible on library screen
|
// Player shortcuts should NOT be visible on library screen
|
||||||
await expect(modal).not.toContainText('Play/Pause');
|
await expect(modal).not.toContainText('Play/Pause');
|
||||||
});
|
});
|
||||||
@@ -149,7 +146,6 @@ test.describe('Keyboard Shortcuts', () => {
|
|||||||
const expectedShortcuts = [
|
const expectedShortcuts = [
|
||||||
{ key: '?', scope: 'global' },
|
{ key: '?', scope: 'global' },
|
||||||
{ key: '/', scope: 'library' },
|
{ key: '/', scope: 'library' },
|
||||||
{ key: 'c', scope: 'library' },
|
|
||||||
{ key: 'f', scope: 'library' },
|
{ key: 'f', scope: 'library' },
|
||||||
{ key: 'e', scope: 'library' },
|
{ key: 'e', scope: 'library' },
|
||||||
{ key: 'Space', scope: 'player' },
|
{ key: 'Space', scope: 'player' },
|
||||||
|
|||||||
Reference in New Issue
Block a user