Skip to content

Commit c3001b9

Browse files
committed
Repair MIDI Studio V2 fullscreen and expose spreadsheet note editor for UAT - PR_26146_024-midi-studio-v2-studio-layout-uat-repair
1 parent ad17dd4 commit c3001b9

8 files changed

Lines changed: 529 additions & 240 deletions

File tree

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# PR_26146_024 MIDI Studio V2 Studio Layout Correction Validation
2+
3+
## Result
4+
- PASS: Expanded view keeps the shared header and MIDI Studio header visible.
5+
- PASS: Expanded view keeps the top transport/action bar visible.
6+
- PASS: Expanded view keeps the left Instruments column visible.
7+
- PASS: Expanded view expands the center timeline/editor area while minimizing secondary diagnostics.
8+
- PASS: The left column is now labeled `Instruments` and contains editable lane rows.
9+
- PASS: Instrument rows include lane name, mute, solo, volume, pan, and preview instrument controls.
10+
- PASS: Clicking an instrument row highlights the matching editable timeline row.
11+
- PASS: The spreadsheet-style note editor is immediately visible without opening an accordion.
12+
- PASS: Imported UAT song data populates visible note cells immediately after Import JSON Manifest.
13+
- PASS: Editing a visible note cell updates the normalized playback data used by Play.
14+
- PASS: Diagnostics and parser summaries are no longer dominant in the Studio tab.
15+
16+
## Scope
17+
- Corrected MIDI Studio V2 expanded/fullscreen behavior so the header, top transport, and Instruments column remain visible.
18+
- Replaced the static `Tracks` list with the authoritative editable `Instruments` lane list.
19+
- Moved lane mute/solo/preview-instrument control ownership into the left Instruments column and added visible volume/pan controls.
20+
- Simplified center timeline row headers so they map to lane names while avoiding duplicate lane-control surfaces.
21+
- Converted the note grid into a visible center spreadsheet panel with explicit `Timeline`, `Edit Notes Here`, and `Instrument` labels.
22+
- Added lane-row selection highlighting across the corresponding spreadsheet timeline row.
23+
- Added note-cell edit synchronization so visible spreadsheet edits update playback data before Play.
24+
- Reduced Studio-tab diagnostics by moving parser summary and audio diagnostics to Diagnostics-only surfaces.
25+
26+
## Lanes
27+
- recovery/UAT: executed because this PR corrects MIDI Studio V2 UAT layout/workflow confusion from PR_26146_023.
28+
- tools: executed through targeted MIDI Studio V2 syntax and Playwright checks.
29+
- runtime: executed through targeted Playwright because browser UI, expanded mode, timeline editing, and Preview Synth playback data changed.
30+
- integration: skipped because Workspace Manager registration/handoff was not changed.
31+
- engine: skipped because no `src/engine` runtime files changed.
32+
- samples: skipped because no sample JSON was modified and full samples smoke was explicitly out of scope.
33+
34+
## Validation Commands
35+
- PASS: `node --check tools/midi-studio-v2/js/MidiStudioV2App.js`
36+
- PASS: `node --check tools/midi-studio-v2/js/bootstrap.js`
37+
- PASS: `node --check tools/midi-studio-v2/js/controls/InstrumentGridControl.js`
38+
- PASS: `node --check tools/midi-studio-v2/js/controls/ToolShellControl.js`
39+
- PASS: `node --check tests/playwright/tools/MidiStudioV2.spec.mjs`
40+
- PASS: HTML external-only guard for `tools/midi-studio-v2/index.html`; no inline `<script>`, `<style>`, or inline event handlers found.
41+
- PASS: `rg -n "track-list|Load Example|Use Example|imageDataUrl" tools/midi-studio-v2 tests/fixtures/midi-studio-v2 tests/playwright/tools/MidiStudioV2.spec.mjs` returned no matches.
42+
- PASS: `$env:PLAYWRIGHT_BROWSERS_PATH='0'; npx.cmd playwright test tests/playwright/tools/MidiStudioV2.spec.mjs --project=playwright --grep "imports UAT manifest and plays|expands and restores" --timeout=90000 --reporter=list`
43+
- Result: 2 passed.
44+
- Proved fullscreen/expanded view keeps header, transport, and left Instruments column visible.
45+
- Proved the center timeline expands while secondary diagnostics are minimized.
46+
- Proved spreadsheet note editor is visible without an accordion.
47+
- Proved instrument rows correspond to editable timeline rows and row click highlights the matching lane.
48+
- Proved imported UAT song populates visible note cells.
49+
- Proved editing a visible note cell updates playback data used by Play.
50+
- Proved diagnostics no longer dominate the Studio tab.
51+
- PASS: `git diff --check`
52+
- Note: Git reported LF-to-CRLF normalization warnings for touched files; no whitespace errors were reported.
53+
54+
## Coverage Reports
55+
- Updated `docs/dev/reports/playwright_v8_coverage_report.txt`.
56+
- Updated `docs/dev/reports/coverage_changed_js_guardrail.txt`.
57+
- Guardrail status: advisory PASS, no changed runtime JS coverage warnings.
58+
59+
## Warnings
60+
- WARN: An exploratory broader MIDI Studio V2 subset timed out and was not used as the validation gate. The requested PR_024 targeted behaviors passed in the focused 2-test lane above.
61+
62+
## Manual UAT Steps
63+
1. Open `tools/midi-studio-v2/index.html`.
64+
2. Click `Import JSON Manifest`.
65+
3. Choose `tests/fixtures/midi-studio-v2/uat-midi-studio-v2.game.manifest.json`.
66+
4. Confirm `Camptown Races UAT Reel` populates the visible center spreadsheet cells.
67+
5. Confirm the left column is labeled `Instruments` and contains Lead, Bass, Pad/Chords, Pad Layer, and Drums controls.
68+
6. Click the Lead row and confirm the Lead timeline row highlights.
69+
7. Edit the first Lead note cell and click `Play`; expected result is audible preview state using the edited playback data.
70+
8. Click `Enter Expanded View`; expected result is header, top transport, left Instruments column, center timeline, and status remain visible while secondary diagnostics are minimized.
71+
9. Click `Exit Expanded View`; expected result is the normal Studio layout restored.
72+
73+
## Skips
74+
- Full samples smoke test: SKIPPED per PR instructions.
75+
- Workspace lane: SKIPPED because Workspace contract/runtime files were not touched.
76+
- Sample JSON modification: SKIPPED per repo and PR instructions.
77+
78+
## Artifacts
79+
- `docs/dev/reports/codex_review.diff`
80+
- `docs/dev/reports/codex_changed_files.txt`
81+
- `docs/dev/reports/playwright_v8_coverage_report.txt`
82+
- `docs/dev/reports/coverage_changed_js_guardrail.txt`
83+
- `docs/dev/codex_commands.md`
84+
- `docs/dev/commit_comment.txt`
85+
- Repo-structured delta ZIP: `tmp/PR_26146_024-midi-studio-v2-studio-layout-correction_delta.zip`

tests/playwright/tools/MidiStudioV2.spec.mjs

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,8 @@ function spreadsheetCell(page, lane, stepIndex) {
308308
}
309309

310310
async function setSpreadsheetRowToggle(page, laneLabel, kind, checked) {
311-
const toggle = page.locator(`.midi-studio-v2__lane-header-cell [aria-label="${kind} ${laneLabel}"]`);
311+
const labelKind = kind.charAt(0).toUpperCase() + kind.slice(1);
312+
const toggle = page.locator(`.midi-studio-v2__instrument-row [aria-label="${labelKind} ${laneLabel}"]`);
312313
await expect(toggle).toHaveCount(1);
313314
await toggle.evaluate(
314315
(input, nextChecked) => {
@@ -357,23 +358,46 @@ test.describe("MIDI Studio V2", () => {
357358
await expect(page.locator("#previewInstrumentDrumsSelect")).toHaveValue("basic-drums");
358359

359360
await expect(page.locator('[data-midi-studio-tab="studio"]')).toHaveAttribute("aria-selected", "true");
360-
await expect(page.locator(".midi-studio-v2__track-list-panel")).toBeVisible();
361-
await expect(page.locator(".midi-studio-v2__track-list li")).toContainText(["Lead", "Bass", "Chords/Pad", "Drums"]);
361+
await expect(page.locator(".midi-studio-v2__instrument-list-panel")).toBeVisible();
362+
await expect(page.locator(".midi-studio-v2__instrument-list-panel h2")).toHaveText("Instruments");
363+
await expect(page.locator(".midi-studio-v2__instrument-row-name")).toContainText(["Lead", "Bass", "Pad/Chords", "Pad Layer", "Drums"]);
364+
await expect(page.locator('.midi-studio-v2__instrument-row[data-preview-lane="lead"] [aria-label="Mute Lead"]')).toHaveCount(1);
365+
await expect(page.locator('.midi-studio-v2__instrument-row[data-preview-lane="lead"] [aria-label="Solo Lead"]')).toHaveCount(1);
366+
await expect(page.locator('.midi-studio-v2__instrument-row[data-preview-lane="lead"] [aria-label="Volume Lead"]')).toHaveCount(1);
367+
await expect(page.locator('.midi-studio-v2__instrument-row[data-preview-lane="lead"] [aria-label="Pan Lead"]')).toHaveCount(1);
368+
await expect(page.locator('.midi-studio-v2__instrument-row[data-preview-lane="lead"] #previewInstrumentLeadSelect')).toHaveCount(1);
362369
await expect(page.locator("#instrumentGridContent")).toBeVisible();
370+
await expect(page.locator('.accordion-v2__header[aria-controls="instrumentGridContent"]')).toHaveCount(0);
371+
await expect(page.locator(".midi-studio-v2__timeline-title")).toContainText("Timeline");
372+
await expect(page.locator(".midi-studio-v2__timeline-title")).toContainText("Edit Notes Here");
363373
await expect(page.locator(".midi-studio-v2__note-table")).toHaveCount(1);
364374
await expect(page.locator(".midi-studio-v2__note-block")).not.toHaveCount(0);
365375
await expect(page.locator('.midi-studio-v2__note-table-cell[data-lane="lead"] .midi-studio-v2__note-block').first()).toHaveText("G4");
366376
await expect(page.locator('.midi-studio-v2__note-table-cell[data-lane="bass"] .midi-studio-v2__note-block').first()).toHaveText("G2");
367377
await expect(page.locator('.midi-studio-v2__note-table-cell[data-lane="chords"] .midi-studio-v2__note-block').first()).toHaveText("G");
368378
await expect(page.locator('.midi-studio-v2__note-table-cell[data-lane="pad"] .midi-studio-v2__note-block').first()).toHaveText("G");
369379
await expect(page.locator('.midi-studio-v2__note-table-cell[data-lane="drums"] .midi-studio-v2__note-block').first()).toHaveText("kick");
380+
await page.locator('.midi-studio-v2__instrument-row[data-preview-lane="lead"]').click();
381+
await expect(page.locator('.midi-studio-v2__instrument-row[data-preview-lane="lead"]')).toHaveClass(/is-selected/);
382+
await expect(page.locator('.midi-studio-v2__note-table-cell[data-lane="lead"].midi-studio-v2__grid-cell--lane-selected')).not.toHaveCount(0);
383+
await expect(page.locator("#audioDiagnosticsContent")).toBeHidden();
384+
await expect(page.locator("#inspectorContent")).toBeHidden();
385+
await spreadsheetCell(page, "lead", 0).fill("C5");
386+
await expect(spreadsheetCell(page, "lead", 0)).toHaveText("C5");
387+
await expect(page.locator("#instrumentGridLeadInput")).toHaveValue(/C5 G4 E4 G4/);
388+
expect(await page.evaluate(() => window.__midiStudioV2App.lastInstrumentGridResult.timeline.some((event) => (
389+
event.lane === "lead" && event.stepIndex === 0 && event.value === "C5"
390+
)))).toBe(true);
370391

371392
await page.locator("#playButton").click();
372393
await expect(page.locator("#playbackState")).toContainText("Playing audible preview: Camptown Races UAT Reel");
373394
await expect(page.locator("#nowPlayingLabel")).toHaveText("Playing: Camptown Races UAT Reel");
374395
await expect(page.locator("#statusLog")).toHaveValue(/OK Audible preview playback started for Camptown Races UAT Reel\./);
375396
expect(await page.evaluate(() => window.__midiStudioPreviewSynthEvents.some((event) => event.action === "oscillator-start"))).toBe(true);
376397
expect(await page.evaluate(() => window.__midiStudioV2App.previewSynth.getSnapshot().playing)).toBe(true);
398+
expect(await page.evaluate(() => window.__midiStudioV2App.lastInstrumentGridResult.timeline.some((event) => (
399+
event.lane === "lead" && event.stepIndex === 0 && event.value === "C5"
400+
)))).toBe(true);
377401

378402
await page.locator("#stopButton").click();
379403
await expect(page.locator("#playbackState")).toContainText("Stopped audible preview: Camptown Races UAT Reel");
@@ -425,8 +449,7 @@ test.describe("MIDI Studio V2", () => {
425449
await expect(page.locator(".midi-studio-v2__tool-menu #exportMp3Button")).toBeVisible();
426450
await expect(page.locator(".midi-studio-v2__tool-menu #exportOggButton")).toBeVisible();
427451
await expect(page.locator("#midiSourceDetails")).toContainText("No MIDI source inspected.");
428-
await expect(page.locator("#audioDiagnostics")).toContainText("Audio context state");
429-
await expect(page.locator("#audioDiagnostics")).toContainText("Playable note count");
452+
await expect(page.locator("#audioDiagnosticsContent")).toBeHidden();
430453
await expect(page.locator("#playbackState")).toContainText("Audible preview ready: Main Theme.");
431454
await expect(page.locator("#statusLog")).toHaveValue(/OK Loaded 3 MIDI songs/);
432455
await expect(page.locator("#statusLog")).toHaveValue(/INFO Next: select a MIDI Studio song, review the Studio tab timeline, then press Play to audition the imported arrangement\./);
@@ -1190,27 +1213,30 @@ Am F`);
11901213
const normalWidth = (await centerPanel.boundingBox())?.width || 0;
11911214
await page.locator("[data-midi-studio-summary]").click();
11921215
await expect(page.locator("body")).toHaveClass(/midi-studio-v2--expanded/);
1193-
await expect(page.locator("[data-midi-studio-summary]")).toContainText("Show Header and Details");
1194-
await expect(page.locator("#shared-theme-header")).toBeHidden();
1195-
await expect(page.locator(".tool-starter__panel--left")).toBeHidden();
1196-
await expect(page.locator("#songDetailsContent")).toBeHidden();
1197-
await expect(page.locator("#instrumentGridContent")).toBeVisible();
1216+
await expect(page.locator("[data-midi-studio-summary]")).toContainText("Exit Expanded View");
1217+
await expect(page.locator("#shared-theme-header")).toBeVisible();
1218+
await expect(page.locator("[data-midi-studio-header]")).toBeVisible();
11981219
await expect(page.locator(".midi-studio-v2__tool-menu")).toBeVisible();
1220+
await expect(page.locator("#playButton")).toBeVisible();
1221+
await expect(page.locator(".tool-starter__panel--left")).toBeVisible();
1222+
await expect(page.locator(".midi-studio-v2__instrument-list-panel")).toBeVisible();
1223+
await expect(page.locator("#instrumentGridContent")).toBeVisible();
11991224
await expect(page.locator("#toolImportManifestButton")).toBeVisible();
12001225
await expect(page.locator("#statusLogContent")).toBeVisible();
12011226
await expect(page.locator("#clearStatusButton")).toBeVisible();
12021227
const expandedWidth = (await centerPanel.boundingBox())?.width || 0;
12031228
expect(expandedWidth).toBeGreaterThan(normalWidth);
1204-
await expect(page.locator("#statusLog")).toHaveValue(/INFO Entered expanded MIDI Studio workspace view/);
1229+
await expect(page.locator("#audioDiagnosticsContent")).toBeHidden();
1230+
await expect(page.locator("#statusLog")).toHaveValue(/INFO Entered expanded MIDI Studio view\. Header, top transport, and Instruments column remain visible; secondary diagnostics are minimized\./);
12051231

12061232
await page.locator("[data-midi-studio-summary]").click();
12071233
await expect(page.locator("body")).not.toHaveClass(/midi-studio-v2--expanded/);
1208-
await expect(page.locator("[data-midi-studio-summary]")).toContainText("Hide Header and Details");
1234+
await expect(page.locator("[data-midi-studio-summary]")).toContainText("Enter Expanded View");
12091235
await expect(page.locator("#shared-theme-header")).toBeVisible();
12101236
await expect(page.locator(".tool-starter__panel--left")).toBeVisible();
12111237
await expect(page.locator('[data-midi-studio-tab="studio"]')).toHaveAttribute("aria-selected", "true");
12121238
await expect(page.locator("#instrumentGridContent")).toBeVisible();
1213-
await expect(page.locator("#statusLog")).toHaveValue(/INFO Exited expanded MIDI Studio workspace view/);
1239+
await expect(page.locator("#statusLog")).toHaveValue(/INFO Exited expanded MIDI Studio view\. Full Studio, setup, and diagnostics layout restored\./);
12141240
} finally {
12151241
await workspaceV2CoverageReporter.stop(page);
12161242
await server.close();

0 commit comments

Comments
 (0)