Commit Graph

5 Commits

Author SHA1 Message Date
K. O. A.
32127bc70b
feat: save as .feedpak; discover and load both .feedpak and .sloppak (#553)
* feat: save songs as .feedpak; discover and load both .feedpak and .sloppak

The open song format was renamed sloppak -> feedpak (public spec lives in
the feedback-feedpak-spec repo), but the server still wrote and recognized
only `.sloppak`. The two are byte-identical on disk.

Read both suffixes everywhere songs are discovered, uploaded, and loaded;
writing the new `.feedpak` suffix is handled in the editor plugin repo. Keep
the internal `format` tag `sloppak` so existing feature gates (stems, drums,
keys) are untouched, matching the "internal rename not landed yet" stance.

- lib/sloppak.py: add FEEDPAK_EXT / SLOPPAK_EXT / SONG_EXTS; is_sloppak()
  now matches either suffix (covers all 7 callers).
- server.py: union scan glob over SONG_EXTS; widen loose-folder exclusion,
  settings DLC count, upload gate (_ALLOWED_SONG_EXTS) and zip-magic check;
  refresh user-facing messages to .feedpak.
- static: library format filter relabeled Sloppak -> Feedpak (value stays
  sloppak, matches both); badge text SLOPPAK -> FEEDPAK in v2 + v3;
  filename-suffix detection and upload drag-drop filter accept both.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: topkoa <topkoa@gmail.com>

* test: cover .feedpak/.sloppak dual-suffix support

Add tests/test_feedpak_extension.py pinning the four paths PR #553
widened so a refactor can't drop .sloppak back-compat or stop
accepting .feedpak:

- is_sloppak / SONG_EXTS suffix detection (file + dir form, case-insensitive)
- _background_scan discovery glob unions over both suffixes
- POST /api/songs/upload accepts both, rejects wrong suffix + non-zip
- save_settings DLC count includes both suffixes

19 tests, all passing; reuses the existing scan_module / TestClient /
isolate_logging fixtures.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Signed-off-by: topkoa <topkoa@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: byrongamatos <xasiklas@gmail.com>
2026-06-22 20:47:56 +02:00
Byron Gamatos
4dc5936712
feat(player): global autoplay & auto-exit option (songs + lessons) (#558)
* fix(v3): pedal click opens the plugin's screen, not its settings

The v3 Pedalboard's settingsTarget() resolved settings-first, so a
plugin that ships both a screen and a settings panel (notably the
bundled Audio Engine) could only ever reach its settings from the
pedalboard — its actual page was unreachable.

Flip to screen-first (stompbox metaphor: step on the pedal, see the
pedal), falling back to settings when there is no screen. Keep a
settings fallback in openPluginSettings() when a declared screen
isn't mounted yet (installing/failed) so settings-bearing plugins are
never stranded on a toast. Drive the pedal aria-label off the same
target so it never promises the wrong surface. Update the unit test
contract to screen > settings > none.

Fixes #555

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(player): global autoplay & auto-exit option (songs + lessons)

Single Settings toggle (autoplayExit, default ON) that auto-starts a song
once it's ready and returns to the launching menu when it ends. Auto-exit
defers while a results/score overlay is on top (heuristic + holdAutoExit()
contract) so a scoring plugin's screen drives the exit. Player origin is now
context-aware (lessons return to the lessons screen via setReturnScreen()),
fixing lesson completion bouncing to the library.

Core-only; songs and lessons share the playSong -> highway path. Adds a
read-only window.slopsmith.autoplayExit getter + holdAutoExit()/setReturnScreen()
for plugins. Unit tests for the pure helpers (_autoplayExitEnabled,
_resolvePlayerOrigin, _resultsOverlayVisible).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 11:04:03 +02:00
Byron Gamatos
fb06e288e1
feat(onboarding): input-device setup step + core-owned midi-input domain (#526)
* feat(capabilities): add core-owned midi-input control-plane domain (#873, #880)

The MIDI analog of audio-input: a core-owned provider-coordinator over MIDI
device discovery, selection, and shared open/close sessions. Separate from
audio-input (whose source/open contract is audio-frame-centric) and not owned
by any feature plugin, so the device-access boundary outlives the input-setup
wizard. `discover` is the Web-MIDI permission boundary; selection persists by
redaction-safe logicalSourceKey; diagnostics redact device labels and never
carry raw MIDI messages.

- static/capabilities/midi-input.js + load-order wiring in both shells
- spec 012 + capability-domains/safety-matrix entries; midi-control narrowed
  to mappings-only (split)
- 9 domain tests against the real runtime

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(input_setup): bundled plugin owning input-calibration + Web-MIDI provider (#872)

Bundled core plugin that supplies the Web-MIDI source provider to the core
midi-input domain, owns the input-calibration workflow domain (run/status/
inspect), and renders the per-instrument wizard (guitar/bass -> audio-input +
note_detect; keys/drums -> midi-input live note/pad test). Idempotent
hydration; redaction-safe. .gitignore allowlists the in-tree plugin.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(onboarding): input-device setup step between paths and calibration (#874)

After instrument-path selection and before the note-detect calibration
challenge, dispatch input-calibration `run` (fire-and-launch) and await the
`calibration-done` event. Fail-soft: a non-handled outcome (plugin/runtime
absent) advances immediately so onboarding can never be stranded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(midi-input): ship a built-in Web-MIDI provider in the core domain

Move the Web-MIDI source provider out of input_setup and into the core
midi-input domain so every consumer (piano, drums, input_setup) gets MIDI
devices from the domain without depending on any one plugin being loaded.
input_setup is now a pure midi-input requester (manifest role updated).
Prepares piano/drums full consumption (#876/#877). +1 domain test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(input_setup): Settings panel to re-run input setup (#878)

Adds a settings.html with a "Set up input devices" button (window
._inputSetupRelaunch) that re-runs the wizard for the player's selected
instrument paths (from /api/progression; falls back to all instruments). Makes
the calibration wizard re-launchable outside first-run onboarding.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(midi-control): formalize the midi-input/midi-control split (#882)

Narrow the reserved midi-control domain to mappings ONLY (CC/pitchbend/note →
action routing), consuming the delivered midi-input domain for device access.
Adds spec 013 defining the contract + intended consumers (feedback-plugin-midi,
drums learn-mode), updates the safety-matrix row, and cross-references it from
capability-domains. Per governance, midi-control stays RESERVED (no runtime
domain) until a concrete mapping consumer + tests exist.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(onboarding): wait for input_setup before the calibration step (#874)

The input-setup wizard is a mandatory onboarding step, but plugins load
asynchronously — in the desktop app (40+ plugins) the user can reach path
selection and click Next before input_setup has registered its
input-calibration owner. The dispatch then got a no-owner outcome and
onboarding fell through to the calibration challenge, silently skipping the
wizard. Now wait (bounded, 8s) for the plugin's public global before
dispatching; fall through only if it never appears. Race-verified.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(onboarding): add Song directory step after name+avatar (#874)

New first-run step (now step 2 of 4: name+avatar → song directory → paths →
calibration challenge) where the player sets their songs folder, fixing the
"folder not configured" error on a fresh install. Saves to settings (dlc_dir)
and kicks a library scan; persists to config.json so it survives restart. A
native folder picker is offered on desktop (window.slopsmithDesktop
.pickDirectory); web users type/paste the path. "Skip for now" leaves it
unconfigured (settable later in Settings).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(input_setup): filter MIDI entries out of the guitar audio-input picker (#876)

Other plugins export pseudonymized MIDI sources ('midi-input-N') into the
audio-input domain; they aren't audio inputs and the cryptic labels confused
the guitar/bass device dropdown. Filter them out so only real audio inputs show.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(input_setup): de-dupe audio input picker entries (#876)

The desktop audio engine enumerates the same device under multiple driver
types, so the guitar audio-input dropdown showed repeated entries. De-dupe by
display label (paired with the desktop fix that surfaces real device names).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(midi-input): drop vanished devices on re-discovery; reset setup confirm on switch

Codex preflight findings:
- midi-input domain `_discover()` only upserted enumerated sources, so an
  unplugged device (statechange re-discovery) lingered in list-sources and later
  open/select hit stale state. Reconcile each provider's sources against the
  fresh enumeration (close any live session, keep the selectedKey preference).
- input_setup MIDI panel left "Continue" enabled (and the instrument marked
  done) after switching the device selection following a prior hit. Reset the
  waiting state + disable Continue on every selection change, and discard a
  stale open if the selection changed mid-await. +1 reconciliation test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(midi-input): coalesce concurrent opens; commit shown audio source pre-calibration

Codex re-review (round 2):
- midi-input domain: two concurrent open-source calls for the same source both
  passed the `sessions.get` guard and each called provider.open(), which for the
  built-in Web-MIDI provider overwrites the shared input.onmidimessage handler
  and orphans the earlier session — leaving the device silent. Coalesce in-flight
  opens onto one provider session (await the pending open, adopt its session;
  re-check after open and release a redundant handle if another open won). +test.
- input_setup: the guitar/bass audio <select> shows its first option by default
  but fires no `change`, so on a first run with nothing selected, audio-input was
  never told before launchCalibration(). Commit the shown option on render.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(midi-input): longer timeout for MIDI permission commands; stale-open guard in wizard

Codex re-review (round 3):
- The advertised command surface ran `discover`/`open-source` through the 250 ms
  default handler timeout, but those front a real Web-MIDI permission prompt /
  device open that commonly takes longer, so dispatch returned `failed` while the
  operation was still completing. Add per-(capability,command) timeout overrides
  (15 s for those two), folding the existing audio-mix special-case into the same
  table so both the command() and dispatch() paths honor it.
- input_setup MIDI panel: openSelected() compared the mutable shared `activeKey`
  after its awaits, so a device switch mid-open could bind the old device's
  listener / close the wrong session. Capture the requested key in a local and
  use a generation guard to discard a superseded open.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(onboarding): detect 200-with-error song-dir saves; close MIDI session on skip

Codex re-review (round 4):
- /api/settings reports an invalid folder as a 200 response with an `error` body
  (a bare dict return, not a non-2xx status), so saveSongDir's res.ok-only check
  treated the failure as success and advanced onboarding without saving. Parse
  the body and throw on `error` too.
- input_setup: the opened MIDI test session was only closed on the Continue
  button, so using the generic "Skip for now" after scanning leaked the listener
  and kept the Web-MIDI input live. Run teardown on every panel exit via a
  per-panel cleanup hook invoked by advance().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(input_setup): don't hard-code Web MIDI in the device wizard

Codex re-review (round 5): the MIDI panel gated availability on
navigator.requestMIDIAccess and filtered sources to providerId === 'web-midi',
which defeats the midi-input domain's provider-coordinator abstraction — a
native/desktop MIDI adapter registered with the domain would be reported
unavailable and hidden from the picker. Gate availability on the domain
(window.slopsmith.midiInput) and show every source it surfaces.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 16:31:16 +02:00
byrongamatos
edf8f46866 Repoint dead slopsmith URLs -> got-feedback
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 11:02:04 +02:00
byrongamatos
6c110398b4 Clean release snapshot 2026-06-16 18:47:13 +02:00