Skip to content
Open
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
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
dist
data/kv_data.json
data/kv.db
.springboard
index.html
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
registry=http://localhost:4873/
27 changes: 27 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Springboard Development Guide

This application is built with the **Springboard framework**.

## Getting Started

**Before writing any code, run:**

```bash
npx sb docs context
```

This outputs comprehensive framework information including available documentation
sections, key concepts, and workflow guidance.

## Recommended Workflow

1. **Run `sb docs context`** at the start of your session
2. **Write code** using your knowledge + the context from step 1
3. **Fetch specific docs** only when needed: `sb docs get <section>`
4. **View examples** for reference code: `sb docs examples show <name>`

## Other Useful Commands

- `sb docs --help` - See all available commands
- `sb docs types` - Get TypeScript type definitions
- `sb docs examples list` - See available example modules
27 changes: 27 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Springboard Development Guide

This application is built with the **Springboard framework**.

## Getting Started

**Before writing any code, run:**

```bash
npx sb docs context
```

This outputs comprehensive framework information including available documentation
sections, key concepts, and workflow guidance.

## Recommended Workflow

1. **Run `sb docs context`** at the start of your session
2. **Write code** using your knowledge + the context from step 1
3. **Fetch specific docs** only when needed: `sb docs get <section>`
4. **View examples** for reference code: `sb docs examples show <name>`

## Other Useful Commands

- `sb docs --help` - See all available commands
- `sb docs types` - Get TypeScript type definitions
- `sb docs examples list` - See available example modules
288 changes: 288 additions & 0 deletions notes/chord-communication-feature-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
# Chord Communication Feature Plan

## Context

This app is currently a collection of prototype features around realtime multiplayer, MIDI, music interaction, dashboards, and chord display. The product goal is to help musicians communicate chords while playing together.

The app should support two primary personas:

1. **The Maestro** — a power user with a MIDI keyboard or controller, making tonal decisions and leading structure/variation.
2. **The Jammer** — a participant who mostly needs to know what chord to play right now.

## Code Areas Researched

Primary relevant files:

- `src/features/src/modules/dashboards/keytar_and_foot_dashboard/*`
- `src/features/src/modules/dashboards/keytar_and_foot_dashboard/single_octave_root_mode_supervisor.tsx`
- `src/features/src/modules/dashboards/keytar_and_foot_dashboard/chord_player.ts`
- `src/features/src/modules/song_structures_dashboards/song_structures_dashboards_module.tsx`
- `src/features/src/modules/song_structures/components/chord_display.tsx`
- `src/features/src/modules/song_structures/components/guitar_tab_view.tsx`
- `src/features/src/snacks/root_mode_snack/*`
- `src/features/src/modules/phone_jam/phone_jam_module.tsx`
- Springboard state-management docs and APIs

Existing useful concepts:

- `createSharedState` — realtime, ephemeral, cross-device state.
- `createPersistentState` — database-backed state that survives restarts.
- `createUserAgentState` — local-only UI/device preferences.
- Current chord concepts are split across:
- simple `{ root, quality }` chord choices
- generated `ChordWithName`
- debug MIDI state
- draft/confirmed chord arrays

## Product Direction

The app should become a realtime chord communication surface.

### Maestro

The Maestro needs to:

- Declare current chord from MIDI input.
- Change key/scale.
- Build, save, and revisit progressions.
- Optionally send MIDI output elsewhere.
- See current musical state and MIDI/debug status.
- Control what Jammers see.

### Jammer

The Jammer needs to:

- Know the current chord immediately.
- Optionally see the next chord or progression position.
- Use the app on phones in portrait and landscape.
- Use the app on large TV/kiosk screens with giant readable chords.
- Avoid unnecessary controls and configuration.

## Proposed Data Model

Separate domain state from UI props and MIDI transport.

```ts
type PitchClass =
| 'C' | 'C#' | 'Db' | 'D' | 'D#' | 'Eb'
| 'E' | 'F' | 'F#' | 'Gb' | 'G' | 'G#'
| 'Ab' | 'A' | 'A#' | 'Bb' | 'B';

type ChordQuality =
| 'major'
| 'minor'
| 'dominant7'
| 'major7'
| 'minor7'
| 'diminished'
| 'sus2'
| 'sus4'
| 'power'
| 'unknown';

type ChordSymbol = {
root: PitchClass;
quality: ChordQuality;
bass?: PitchClass;
extensions?: string[];
display: string;
};

type SessionKey = {
tonic: PitchClass;
mode: 'major' | 'minor' | 'dorian' | 'mixolydian' | 'unknown';
};

type ChordEvent = {
id: string;
chord: ChordSymbol;
declaredAt: number;
declaredByUserId?: string;
source: 'midi' | 'manual' | 'progression' | 'imported';
midiInput?: {
notes: number[];
velocity?: number;
channel?: number;
};
};

type Progression = {
id: string;
name: string;
chords: ChordSymbol[];
createdAt: number;
updatedAt: number;
lastUsedAt?: number;
tags?: string[];
};

type JamSessionState = {
sessionId: string;
key: SessionKey;
currentChord: ChordEvent | null;
previousChords: ChordEvent[];
activeProgressionId: string | null;
progressionCursor: number | null;
savedProgressions: Progression[];
};
```

## Springboard State Mapping

Recommended state storage:

- `createSharedState<JamSessionState>` for live session state.
- `createPersistentState<Progression[]>` for reusable progressions.
- `createUserAgentState<ViewPrefs>` for per-device layout, theme, and debug preferences.
- Optional future `createSharedState<PresenceState>` for connected devices/personas.

## Proposed View Props

### Jammer Chord Display

```ts
type JammerChordDisplayProps = {
currentChord: ChordSymbol | null;
nextChord?: ChordSymbol | null;
keySignature?: SessionKey;
mode: 'phonePortrait' | 'phoneLandscape' | 'tv';
isStale?: boolean;
};
```

### Maestro Dashboard

```ts
type MaestroDashboardProps = {
session: JamSessionState;
midiStatus: {
inputConnected: boolean;
outputConnected: boolean;
lastInputNotes: number[];
};
onSetKey: (key: SessionKey) => void;
onDeclareChord: (chord: ChordSymbol) => void;
onSelectProgression: (id: string) => void;
onSaveProgression: (progression: Progression) => void;
onAdvanceProgression: () => void;
onGoBackProgression: () => void;
};
```

## Storybook Scenarios

Storybook should become the design lab for personas and data scenarios.

Recommended stories:

- No chord yet.
- Current chord only.
- Current plus next chord.
- Rapid chord changes.
- Long chord names.
- Phone portrait.
- Phone landscape.
- TV/kiosk display.
- Maestro with MIDI connected.
- Maestro without MIDI connected.
- Saved progression recall.
- Draft progression editing.

## Implementation Plan

### Phase 1 — Model and Pure Components

Goal: create a mergeable foundation without risky Springboard or MIDI rewrites.

Tasks:

1. Add domain model files, likely:
- `src/features/src/modules/chord_session/chord_session_types.ts`
- `src/features/src/modules/chord_session/chord_session_fixtures.ts`
2. Add props-only components:
- `JammerChordDisplay`
- `MaestroDashboard`
- `ProgressionStrip`
- `ChordCard`
3. Keep components pure and Storybook-friendly.
4. Add fixture scenarios for personas and layouts.

### Phase 2 — Storybook

Goal: establish a reliable visual scenario environment.

Target structure:

- `.storybook/*`
- `src/**/*.stories.tsx`
- fixture-driven stories

Storybook should cover:

- Jammer phone views.
- Jammer TV/kiosk view.
- Maestro control surface.
- Progression recall.
- Empty/error/awkward states.

### Phase 3 — Springboard Module Integration

Goal: wire the new model into the app while preserving prototypes.

Add a new module rather than rewriting old dashboards immediately:

```ts
springboard.registerModule('chord_session', {}, async (moduleAPI) => {
const liveSession = await moduleAPI.statesAPI.createSharedState<JamSessionState>(...);
const savedProgressions = await moduleAPI.statesAPI.createPersistentState<Progression[]>(...);
const viewPrefs = await moduleAPI.statesAPI.createUserAgentState(...);

// routes:
// /maestro
// /jammer
// /kiosk
});
```

This lets the old prototype routes continue to work while the real product surface is built beside them.

### Phase 4 — MIDI Adapter

Goal: extract MIDI behavior into testable adapters feeding the domain model.

Relevant source prototypes:

- `single_octave_root_mode_supervisor.tsx`
- `chord_player.ts`
- `root_mode_snack`

Extract testable functions:

- MIDI notes to chord symbol.
- Key plus note to scale-degree chord.
- Chord symbol to MIDI notes.
- MIDI buttons to progression commands.

### Phase 5 — Verification and Merge Readiness

Before merge:

- Install dependencies if needed.
- Run type checks.
- Run app build.
- Verify Storybook starts successfully.
- Verify stories render without runtime errors.
- Verify existing prototype routes still work.

## Recommendation

Start with **Phase 1 plus Storybook fixtures**.

Avoid wiring MIDI first. The current risk is allowing prototype MIDI state shapes to leak into the product model. If the session model and props stabilize first, the Maestro/Jammer views can be designed around real use cases, and MIDI can become an adapter into that model.

## Current Tooling Notes

- `serena` CLI currently fails with: `bad interpreter: Permission denied`.
- `bd` currently reports no beads database found in this worktree.
- A subagent was started to independently investigate Storybook setup and create a starter Storybook shell/story template.
41 changes: 41 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"name": "jamapp",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"start": "node dist/node/node-entry.mjs",
"build": "npm run build:web && npm run build:node",
"build:web": "SPRINGBOARD_PLATFORM=web vite build",
"build:node": "SPRINGBOARD_PLATFORM=node vite build --outDir dist/node",
"check-types": "tsc --noEmit",
"postinstall": "npm rebuild better-sqlite3"
},
"dependencies": {
"@hono/node-server": "^1.19.9",
"@jamtools/core": "0.0.1-dev-jamapp-10",
"better-sqlite3": "^12.6.2",
"crossws": "^0.4.4",
"hono": "^4.12.3",
"immer": "^11.1.4",
"kysely": "^0.28.11",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-router": "^7.13.1",
"rxjs": "^7.8.2",
"springboard": "0.0.1-dev-jamapp-10"
},
"devDependencies": {
"@types/node": "^25.3.2",
"@types/qrcode": "^1.5.6",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"jsdom": "25.0.1",
"qrcode": "^1.5.4",
"react-guitar": "^1.1.3",
"tonal": "^6.4.3",
"typescript": "^5.9.3",
"vite": "^7.3.1",
"wled-client": "^0.22.1"
}
}
Loading