Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions PLANS.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ M1 should include:
Use this section as the current execution order for agent work. Feature document numbering describes the planned product sequence, but actual issue order may change as dependencies, review feedback, or implementation risks become clearer.

1. #23 Sampler for advanced sustain -> `docs/features/09-sampler-advanced-sustain.md` (PR in review)
2. #30 Sidebar clip and instrument management -> `docs/features/12-sidebar-clip-instrument-management.md`

## Planned Milestones

Expand All @@ -43,13 +44,14 @@ Use this section as the current execution order for agent work. Feature document
8. Tempo control and live BPM updates.
9. Arrangement view UI shell.
10. Sampler advanced sustain.
11. Hybrid clip loop playback.
12. Sample import.
13. Project export/import.
14. IndexedDB autosave.
15. Arrangement data model.
16. Arrangement editing and playback.
17. Mixer and basic effects.
11. Sidebar clip and instrument management.
12. Hybrid clip loop playback.
13. Sample import.
14. Project export/import.
15. IndexedDB autosave.
16. Arrangement data model.
17. Arrangement editing and playback.
18. Mixer and basic effects.

## Backlog

Expand Down
27 changes: 27 additions & 0 deletions docs/data-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,32 @@ Early clips may contain both drum events and note events. This keeps the M1 edit

Later, the model can evolve toward separate drum, MIDI, and audio clip types if arrangement and editing workflows need stronger separation.

## M1 Clip Collection and Sidebar Membership

The M1 browser app should maintain an ordered collection of reusable 1-bar hybrid clips. This may live in app-level project state before full export/import exists, but the data itself should be serializable and compatible with the future `Project.clips` field.

Runtime UI selection, such as `selectedClipId` and the selected sidebar item, may remain app state. The clip list, clip names, drum lane settings, pitched instrument membership, drum events, and note events should be serializable.

Every hybrid clip has a mandatory `Drums` child item in the sidebar. The `Drums` item represents the clip's `drumLanes` and `drumEvents`; it is not stored as a pitched instrument and should not be removable in the first sidebar management feature.

Pitched instruments that are available inside a clip should be stored by serializable ID, for example:

```ts
export interface Clip {
id: string;
name: string;
lengthTicks: Tick;
drumLanes: DrumLaneDefinition[];
drumEvents: DrumEvent[];
pitchedInstrumentIds: string[];
noteEvents: NoteEvent[];
}
```

`pitchedInstrumentIds` controls which pitched instrument child items appear under the clip. `NoteEvent.instrumentId` still owns each note, so multiple pitched instruments can coexist inside one hybrid clip and play together.

Deleting a pitched instrument from a clip must deliberately handle notes owned by that instrument. Prefer requiring confirmation before deleting those notes. If confirmation UI is not available, disable deletion while owned notes exist and make the reason clear.

## Bundled Drum Sample Naming and Display

Bundled drum sample files live under `public/samples/drums/`.
Expand Down Expand Up @@ -212,6 +238,7 @@ export interface Clip {
lengthTicks: Tick;
drumLanes: DrumLaneDefinition[];
drumEvents: DrumEvent[];
pitchedInstrumentIds: string[];
noteEvents: NoteEvent[];
}

Expand Down
150 changes: 150 additions & 0 deletions docs/features/12-sidebar-clip-instrument-management.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Feature: 12 Sidebar Clip and Instrument Management

## Status
Planned

## Goal

Make the left project sidebar a real clip and instrument management surface.

Users should be able to create, select, rename, and delete clips. Within each clip, users should be able to add and remove available pitched instruments, then select an instrument to edit its piano roll notes.

## Context

The current sidebar already presents `CLIP 1`, a mandatory `Drums` item, and pitched instrument entries such as `Default Synth` and `Iowa Piano`. Selection is useful because the piano roll shows notes for the selected pitched instrument.

The next step is to make that sidebar state editable instead of static. This requires a small serializable clip collection model, per-clip pitched instrument membership, and safe selection fallback when clips or instruments are removed.

This feature is separate from arrangement editing. Creating clips here creates reusable 1-bar hybrid clips for the M1 clip editor; it does not place clip instances on an arrangement timeline.

## Scope

Included:
- Create new 1-bar hybrid clips from the sidebar.
- Select clips from the sidebar and update the visible clip editor.
- Rename or otherwise edit clip names from the sidebar.
- Delete clips from the sidebar.
- Keep at least one clip available; do not allow the project to end in a zero-clip state.
- Show `+` and `-` icon buttons for add/delete actions where practical.
- Keep `Drums` as a mandatory child item for every hybrid clip.
- Show a scrollable or compact list of available pitched instruments when adding an instrument to a clip.
- Initial available pitched instruments are `Default Synth` and `Iowa Piano`.
- Add pitched instruments to the selected clip.
- Prevent duplicate pitched instrument entries in the same clip for the first version.
- Remove pitched instruments from the selected clip.
- If removing a pitched instrument would delete that instrument's notes, require an explicit confirmation or a clearly documented safe fallback.
- Store clip list and per-clip pitched instrument membership in serializable state.
- Preserve `NoteEvent.instrumentId` ownership so multiple pitched instruments can coexist inside one hybrid clip.
- Update relevant data model, UI, and testing docs.

Excluded:
- Arrangement clip placement or `ClipInstance` editing.
- Clip drag-and-drop reordering.
- Instrument drag-and-drop reordering.
- Drum lane add/delete.
- User-created custom instruments.
- User sample import.
- Full project export/import.
- IndexedDB autosave.
- Clip duplication.
- Keyboard shortcuts.
- Audio effects, mixer, or routing changes.

## Constraints

- Keep React UI separate from audio scheduling logic.
- Project and clip state must remain serializable.
- Do not store `AudioBuffer`, `AudioNode`, or decoded sample data in project state.
- Store musical event time in ticks.
- Use CSS Modules and semantic design tokens for sidebar UI changes.
- Use semantic buttons with accessible labels for icon-only `+` and `-` controls.
- Do not add a runtime dependency unless the implementation clearly justifies it.
- Do not remove the mandatory `Drums` child item in this first version.
- Do not close issue #30 until all acceptance criteria are implemented and verified.

## Data Model Notes

The implementation should introduce or formalize app-level project state containing an ordered clip list. A selected clip ID and selected sidebar item may remain runtime UI state.

Each hybrid clip should store a serializable list of pitched instruments available inside that clip, for example:

```ts
export interface Clip {
id: string;
name: string;
lengthTicks: Tick;
drumLanes: DrumLaneDefinition[];
drumEvents: DrumEvent[];
pitchedInstrumentIds: string[];
noteEvents: NoteEvent[];
}
```

`pitchedInstrumentIds` controls which pitched instrument entries are visible under the clip. `NoteEvent.instrumentId` still controls which instrument owns each note.

Deleting a pitched instrument must deliberately handle note events with that `instrumentId`. The preferred first behavior is:

- Ask for confirmation if notes exist for the instrument.
- On confirmation, remove the instrument entry and delete only note events owned by that instrument.
- If confirmation UI is not implemented, keep the delete action disabled while owned notes exist and explain why.

## UI Notes

The sidebar should keep the existing compact DAW style:

- Clip rows show selection state.
- Clip rows expose an add-instrument `+` control and a delete-clip `-` control.
- The Clips section or project area exposes an add-clip `+` control.
- Pitched instrument rows expose a remove-instrument `-` control.
- Icon-only controls must have `aria-label`.
- `aria-expanded` should reflect expanded or collapsed clip groups when practical.
- The instrument picker should not be clipped by sidebar or workspace overflow.

The first implementation may use inline rename controls, a small popover, or a simple focused editing state. A large modal is not required.

## Done when

- Users can create a new 1-bar hybrid clip from the sidebar.
- Users can select between clips and the main clip editor updates to the selected clip.
- Users can rename or edit a clip name.
- Users can delete a clip, with safe fallback selection and no zero-clip project state.
- Each clip shows its own mandatory `Drums` child item.
- Users can open an available-instrument list from the sidebar.
- Users can add `Default Synth` or `Iowa Piano` to a clip when it is not already present.
- Users cannot add duplicate pitched instrument entries to the same clip.
- Users can remove a pitched instrument safely.
- Removing a pitched instrument handles that instrument's note events according to the documented behavior.
- Pitched instrument selection updates the piano roll visible/editable instrument.
- Serializable state represents the clip list and per-clip pitched instrument membership.
- Existing drum sequencer, piano roll editing, transport, and scheduler behavior continue to work for the selected clip.
- Relevant docs and tests are updated.

## Verification

Run:
- `npm run typecheck`
- `npm run lint`
- `npm run test`
- `npm run build`

Manual check:
- Add a new clip and confirm it appears in the sidebar.
- Select each clip and confirm the editor shows that clip's drum and note state.
- Rename a clip and confirm the label updates.
- Delete a non-selected clip and confirm the current selection remains valid.
- Delete the selected clip and confirm a nearby remaining clip becomes selected.
- Confirm the last remaining clip cannot be deleted or is immediately replaced with a valid empty clip.
- Add `Default Synth` and `Iowa Piano` to a clip from the instrument picker.
- Confirm unavailable or already-added instruments cannot be duplicated.
- Remove a pitched instrument with no notes.
- Remove a pitched instrument with notes and confirm the implemented confirmation or safe fallback behavior.
- Confirm `Drums` cannot be removed in this version.
- Confirm icon buttons have accessible names and visible focus states.

## PR notes

- Reference issue #30.
- Explain how clip list state is represented.
- Explain how per-clip pitched instrument membership is represented.
- Explain the note deletion behavior when removing a pitched instrument.
- Mention that arrangement placement, persistence, sample import, and custom instruments are intentionally deferred.
4 changes: 4 additions & 0 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ CI uses `--if-present` while the repository is still before the Vite scaffold.
- Pure utilities: unit tests.
- Tick/time conversion: unit tests.
- Data model transformations: unit tests.
- Clip collection and sidebar membership transformations: unit tests.
- Pitched instrument metadata and sample-zone mapping: unit tests.
- Tempo control and scheduler tempo update behavior: unit tests where practical.
- Sustain loop point calculations: unit tests.
Expand Down Expand Up @@ -52,6 +53,9 @@ tests/unit/utils/tick-time.test.ts
- Playhead wrapping at loop boundaries.
- BPM changes while stopped, paused, and playing.
- Pitched instrument selection.
- Clip add/delete/rename selection fallback.
- Per-clip pitched instrument add/delete behavior.
- Removing pitched instruments that own note events.
- Sample start offsets, optional sustain loop points, and note release behavior.
- Sampler sustain fallback behavior when loop metadata is missing or invalid.
- Clip duplication.
Expand Down
18 changes: 17 additions & 1 deletion docs/ui-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,23 @@ Pitched instruments should appear as selectable clip child items in the left pro

Changing the selected pitched instrument should not mutate existing `NoteEvent` timing or pitch data. Note events belong to a specific pitched instrument by serializable `instrumentId`, so `Default Synth` and `Iowa Piano` notes may coexist in the same clip and can play at the same time.

The current implementation keeps the selected sidebar item as runtime app state. The note events themselves store serializable instrument IDs.
The current implementation keeps the selected sidebar item as runtime app state. The note events themselves store serializable instrument IDs. Once sidebar clip and instrument management is implemented, each clip should also store a serializable list of pitched instrument IDs that controls which pitched instrument child items appear under that clip.

## Sidebar Clip and Instrument Management

The left project sidebar should become the primary place to manage reusable M1 hybrid clips.

- The project or Clips section should expose a `+` button for creating a new clip.
- Clip rows should expose a `+` button for adding a pitched instrument and a `-` button for deleting the clip.
- Pitched instrument rows should expose a `-` button for removing that instrument from the clip.
- `Drums` remains a mandatory child item for every hybrid clip and should not expose a remove action in the first version.
- Available pitched instruments should be shown in a compact picker or popover when adding an instrument.
- The instrument picker should not be clipped by sidebar, panel, or workspace overflow.
- Icon-only controls must be semantic buttons with accessible labels.
- Selection state should remain visually clear for clip rows, `Drums`, and pitched instrument rows.
- Expanded or collapsed clip groups should use `aria-expanded` when practical.

Deleting a clip or pitched instrument should avoid surprising data loss. The first implementation should keep at least one clip available and should require confirmation or a documented safe fallback before deleting notes owned by a removed pitched instrument.

## Sample Display Names

Expand Down
Loading