fix: wire all AI tool actions to new floating panels#589
Conversation
- CanvasContextMenu: "Music Enhancer" now opens MusicEnhancerPanel, "Add a Layer" now opens AddLayerPanel (was calling old setShowGenerationPanel) - Timeline: right-click on select window now shows CanvasContextMenu with AI Tools submenu (replaced old RegionContextMenu with single item) - ClipBlock: "Create Cover..." opens MusicEnhancerPanel instead of old CoverModal - ClipBlock: "Add Layer here..." opens AddLayerPanel via uiStore instead of local modal state - TrackLane: double-click on empty stems/sample area opens AddLayerPanel directly instead of showing confusing LaneContextMenu popup Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR updates timeline/context-menu AI tool entry points so they open the newer floating panels (AddLayerPanel, MusicEnhancerPanel) and unifies the select-window context menu with the existing canvas context menu.
Changes:
- Double-click on empty stems/sample/audio track space now opens
AddLayerPanel. - Select-window right-click now uses
CanvasContextMenu(instead ofRegionContextMenu). - Clip context menu actions “Add Layer here...” / “Create Cover...” now open floating panels via
uiStore.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| src/components/timeline/TrackLane.tsx | Double-click behavior updated to open AddLayerPanel. |
| src/components/timeline/Timeline.tsx | Select-window context menu swapped to CanvasContextMenu. |
| src/components/timeline/ClipBlock.tsx | Clip context menu actions rewired to open floating panels via uiStore. |
| src/components/timeline/CanvasContextMenu.tsx | AI Tools submenu actions updated to open AddLayerPanel / MusicEnhancerPanel. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| {/* Region context menu — right-click on select window → same as canvas context menu with AI Tools */} | ||
| {regionCtxMenu && selectWindow && ( | ||
| <RegionContextMenu | ||
| <CanvasContextMenu | ||
| x={regionCtxMenu.x} | ||
| y={regionCtxMenu.y} | ||
| onRegenerateRegion={() => { | ||
| setRegionCtxMenu(null); | ||
| setRegionRegenerateTarget({ | ||
| startTime: selectWindow.startTime, | ||
| endTime: selectWindow.endTime, | ||
| trackIds: selectWindow.trackIds, | ||
| }); | ||
| }} | ||
| onClose={() => setRegionCtxMenu(null)} | ||
| hasReadyClips={ | ||
| project.tracks.some((t) => | ||
| selectWindow.trackIds.includes(t.id) && | ||
| t.clips.some((c) => { | ||
| const clipEnd = c.startTime + c.duration; | ||
| return Math.min(selectWindow.endTime, clipEnd) > Math.max(selectWindow.startTime, c.startTime) | ||
| && c.generationStatus === 'ready'; | ||
| }), | ||
| ) | ||
| } | ||
| /> | ||
| )} |
There was a problem hiding this comment.
CanvasContextMenu no longer provides a way to trigger region regeneration, but Timeline still renders RegionRegenerateModal and keeps setRegionRegenerateTarget/regionRegenerateTarget state. After this change nothing in the codebase calls setRegionRegenerateTarget, so the modal becomes unreachable (and related imports/hooks become dead code). Either add a region-specific action back into this context menu (e.g., set the target from selectWindow before opening the modal) or remove the unused modal wiring/imports/state if the feature is intentionally deprecated.
| onConsolidate={() => { void handleConsolidate(); }} | ||
| onDelete={() => { closeCtxMenu(); removeClip(clip.id); }} | ||
| onAddLayer={() => { closeCtxMenu(); setAddLayerOpen(true); }} | ||
| onAddLayer={() => { closeCtxMenu(); useUIStore.getState().setAddLayerOpen(true); }} |
There was a problem hiding this comment.
Opening AddLayerPanel here no longer sets a selection/range for the action. Previously this menu action had clip-local context (startTime/duration); now AddLayerPanel will fall back to the existing selectWindow (or the whole song if none), which makes "Add Layer here..." likely operate on the wrong time range. Consider setting selectWindow to { startTime: clip.startTime, endTime: clip.startTime + clip.duration, trackIds: [track.id] } (or an appropriate range) before opening the panel.
| onAddLayer={() => { closeCtxMenu(); useUIStore.getState().setAddLayerOpen(true); }} | |
| onAddLayer={() => { | |
| closeCtxMenu(); | |
| // Ensure Add Layer operates on this clip's time range and track | |
| useProjectStore.getState().setSelectWindow({ | |
| startTime: clip.startTime, | |
| endTime: clip.startTime + clip.duration, | |
| trackIds: [track.id], | |
| }); | |
| useUIStore.getState().setAddLayerOpen(true); | |
| }} |
| onCreateCover={() => { | ||
| closeCtxMenu(); | ||
| setCoverModal(clip.id); | ||
| useUIStore.getState().setMusicEnhancerOpen(true); |
There was a problem hiding this comment.
MusicEnhancerPanel determines the target clip from the current selectWindow (and its trackIds). This handler opens the panel but does not set/adjust selectWindow to the clicked clip’s range, so "Create Cover..." can open the panel with no selected clip (or a different clip) and prefill the wrong prompt/lyrics. Set selectWindow to the clip’s time range + track before opening, or introduce a dedicated "cover target" in uiStore that the panel can consume.
| useUIStore.getState().setMusicEnhancerOpen(true); | |
| const uiState = useUIStore.getState(); | |
| uiState.setSelectWindow({ | |
| startTime: clip.startTime, | |
| endTime: clip.startTime + clip.duration, | |
| trackIds: [track.id], | |
| }); | |
| uiState.setMusicEnhancerOpen(true); |
| const rect = e.currentTarget.getBoundingClientRect(); | ||
| const laneX = e.clientX - rect.left; | ||
| const clickTime = laneX / pixelsPerSecond; | ||
| if (hitsClip(clickTime)) return; |
There was a problem hiding this comment.
This now opens AddLayerPanel on double-click but does not set a time range/selection for where the layer should be generated. Since AddLayerPanel uses selectWindow to compute startTime/endTime (defaulting to the whole song when selectWindow is null), double-clicking empty space will likely open the panel targeting 0..projectDuration rather than the clicked location. Consider restoring the prior range calculation (snap click time, clamp duration) and calling useUIStore.getState().setSelectWindow(...) (and optionally trackIds: [track.id]) before setAddLayerOpen(true).
| if (hitsClip(clickTime)) return; | |
| if (hitsClip(clickTime)) return; | |
| // Set a selection window around the clicked time so AddLayerPanel | |
| // can derive an appropriate start/end range. | |
| const rawTime = laneX / pixelsPerSecond; | |
| const startTime = Math.max(0, snapToGrid(rawTime, project.bpm, 1)); | |
| const remaining = project.totalDuration - startTime; | |
| const duration = Math.max(10, Math.min(30, remaining)); | |
| const end = startTime + duration; | |
| useUIStore.getState().setSelectWindow({ | |
| start: startTime, | |
| end, | |
| trackIds: [track.id], | |
| }); |
Summary
MusicEnhancerPanel, "Add a Layer" → opensAddLayerPanel(was incorrectly callingsetShowGenerationPanel)CanvasContextMenuwith full AI Tools submenu (replaced oldRegionContextMenuthat only had "Regenerate Selected Region...")MusicEnhancerPanelfloating panel (was opening oldCoverModal)AddLayerPanelviauiStore(was using local state + oldAddLayerModal)AddLayerPanelfor stems/sample tracks (was showing confusingLaneContextMenupopup with "Create Quick Sampler..." / "Add Layer...")Test plan
npx tsc --noEmit— 0 errorsnpm test— 1854 tests pass🤖 Generated with Claude Code