diff --git a/src/components/KGOnePanel.css b/src/components/KGOnePanel.css index d7d79c9..dbfc3bf 100644 --- a/src/components/KGOnePanel.css +++ b/src/components/KGOnePanel.css @@ -296,24 +296,37 @@ /* Generate / Separate button */ .kgone-btn-generate { - background-color: #4a5fa0; - border: none; border-radius: 4px; - color: #e0e0e0; font-size: 12px; font-weight: 600; padding: 8px 12px; - cursor: pointer; width: 100%; margin-top: 6px; +} + +.kgone-btn-generate-accent { + background-color: #4a5fa0; + border: none; + color: #e0e0e0; + cursor: pointer; transition: background-color 0.15s; } -.kgone-btn-generate:hover:not(:disabled) { +.kgone-btn-generate-accent:hover:not(:disabled) { background-color: #5a70b8; } -.kgone-btn-generate:disabled { +.kgone-btn-generate-accent:disabled { + background-color: #3a3a3a; + color: #666; + cursor: not-allowed; +} + +.kgone-btn-generate.dialog-btn { + min-height: 32px; +} + +.kgone-btn-generate.dialog-btn:disabled { background-color: #3a3a3a; color: #666; cursor: not-allowed; diff --git a/src/components/KGOnePanel.tsx b/src/components/KGOnePanel.tsx index a0d48ce..4c26bdb 100644 --- a/src/components/KGOnePanel.tsx +++ b/src/components/KGOnePanel.tsx @@ -524,7 +524,7 @@ const ClipTab: React.FC = ({ bpm, keySignature }) => { )} ) : ( - + )} diff --git a/src/index.css b/src/index.css index 4bb29a6..74651de 100644 --- a/src/index.css +++ b/src/index.css @@ -33,6 +33,11 @@ body { min-height: 100vh; } +a, +a:visited { + color: rgb(90, 159, 212); +} + #root { width: 100%; height: 100%; diff --git a/src/stores/projectStore.test.ts b/src/stores/projectStore.test.ts index 5457b24..66b1478 100644 --- a/src/stores/projectStore.test.ts +++ b/src/stores/projectStore.test.ts @@ -250,4 +250,80 @@ describe('projectStore piano roll state', () => { expect(useProjectStore.getState().trackAutomationRedrawVersion).toBe(initialVersion + 2); }); + + it('restores Chat after closing Settings when Chat was active on entry', async () => { + const { useProjectStore } = await import('./projectStore'); + + act(() => { + useProjectStore.getState().toggleChatBox(); + useProjectStore.getState().setShowSettings(true); + }); + + let state = useProjectStore.getState(); + expect(state.showSettings).toBe(true); + expect(state.settingsReturnSidePanel).toBe('chat'); + + act(() => { + useProjectStore.getState().setShowSettings(false); + }); + + state = useProjectStore.getState(); + expect(state.showSettings).toBe(false); + expect(state.showChatBox).toBe(true); + expect(state.showKGOnePanel).toBe(false); + expect(state.showEventListPanel).toBe(false); + }); + + it('restores K.G.One after closing Settings when K.G.One was active on entry', async () => { + const { useProjectStore } = await import('./projectStore'); + + act(() => { + useProjectStore.getState().toggleKGOnePanel(); + useProjectStore.getState().setShowSettings(true); + useProjectStore.getState().setShowSettings(false); + }); + + const state = useProjectStore.getState(); + expect(state.showSettings).toBe(false); + expect(state.showKGOnePanel).toBe(true); + expect(state.showChatBox).toBe(false); + expect(state.showEventListPanel).toBe(false); + }); + + it('restores no side panel after closing Settings when none was active on entry', async () => { + const { useProjectStore } = await import('./projectStore'); + + act(() => { + useProjectStore.getState().toggleChatBox(); + useProjectStore.getState().toggleChatBox(); + useProjectStore.getState().setShowSettings(true); + useProjectStore.getState().setShowSettings(false); + }); + + const state = useProjectStore.getState(); + expect(state.showSettings).toBe(false); + expect(state.showChatBox).toBe(false); + expect(state.showKGOnePanel).toBe(false); + expect(state.showEventListPanel).toBe(false); + expect(state.settingsReturnSidePanel).toBeNull(); + expect(state.lastActiveSidePanel).toBe('chat'); + }); + + it('opens Event List and exits Settings when Event List is activated from Settings', async () => { + const { useProjectStore } = await import('./projectStore'); + + act(() => { + useProjectStore.getState().toggleChatBox(); + useProjectStore.getState().setShowSettings(true); + useProjectStore.getState().activateSidePanel('eventList'); + }); + + const state = useProjectStore.getState(); + expect(state.showSettings).toBe(false); + expect(state.showEventListPanel).toBe(true); + expect(state.showChatBox).toBe(false); + expect(state.showKGOnePanel).toBe(false); + expect(state.settingsReturnSidePanel).toBeNull(); + expect(state.lastActiveSidePanel).toBe('eventList'); + }); }); diff --git a/src/stores/projectStore.ts b/src/stores/projectStore.ts index 32f34cf..c8bf4dd 100644 --- a/src/stores/projectStore.ts +++ b/src/stores/projectStore.ts @@ -53,6 +53,16 @@ function updateBarWidthMultiplierCSS(multiplier: number): void { ); } +type SidePanelType = 'kgone' | 'chat' | 'eventList'; + +function getSidePanelVisibilityState(activePanel: SidePanelType | null) { + return { + showKGOnePanel: activePanel === 'kgone', + showChatBox: activePanel === 'chat', + showEventListPanel: activePanel === 'eventList', + }; +} + // Define the store state interface interface ProjectState { // State @@ -101,6 +111,8 @@ interface ProjectState { // Event list panel state showEventListPanel: boolean; + lastActiveSidePanel: SidePanelType | null; + settingsReturnSidePanel: SidePanelType | null; // Instrument selection panel state showInstrumentSelection: boolean; @@ -199,6 +211,7 @@ interface ProjectState { // Event List panel actions toggleEventListPanel: () => void; + activateSidePanel: (panel: SidePanelType) => void; // Instrument selection panel actions openInstrumentSelectionForTrack: () => void; @@ -430,6 +443,8 @@ export const useProjectStore = create((set, get) => { // Initial Event List panel state showEventListPanel: false, + lastActiveSidePanel: initialChatBoxState ? 'chat' : null, + settingsReturnSidePanel: null, // Initial Instrument Selection panel state showInstrumentSelection: initialShowInstrumentSelection, @@ -1579,27 +1594,52 @@ export const useProjectStore = create((set, get) => { }, // ChatBox action implementations - setShowChatBox: (show: boolean) => { + activateSidePanel: (panel: SidePanelType) => { set({ - showChatBox: show, - showKGOnePanel: show ? false : get().showKGOnePanel, - showEventListPanel: show ? false : get().showEventListPanel + ...getSidePanelVisibilityState(panel), + lastActiveSidePanel: panel, + showSettings: false, + settingsReturnSidePanel: null, }); }, + setShowChatBox: (show: boolean) => { + if (show) { + get().activateSidePanel('chat'); + return; + } + + set({ showChatBox: false }); + }, + toggleChatBox: () => { - const { showChatBox } = get(); - set({ showChatBox: !showChatBox, showKGOnePanel: false, showEventListPanel: false }); + const { showChatBox, showSettings } = get(); + if (showSettings || !showChatBox) { + get().activateSidePanel('chat'); + return; + } + + set({ showChatBox: false }); }, toggleKGOnePanel: () => { - const { showKGOnePanel } = get(); - set({ showKGOnePanel: !showKGOnePanel, showChatBox: false, showEventListPanel: false }); + const { showKGOnePanel, showSettings } = get(); + if (showSettings || !showKGOnePanel) { + get().activateSidePanel('kgone'); + return; + } + + set({ showKGOnePanel: false }); }, toggleEventListPanel: () => { - const { showEventListPanel } = get(); - set({ showEventListPanel: !showEventListPanel, showChatBox: false, showKGOnePanel: false }); + const { showEventListPanel, showSettings } = get(); + if (showSettings || !showEventListPanel) { + get().activateSidePanel('eventList'); + return; + } + + set({ showEventListPanel: false }); }, // Instrument selection panel actions @@ -1615,7 +1655,29 @@ export const useProjectStore = create((set, get) => { // Settings action implementations setShowSettings: (show: boolean) => { - set({ showSettings: show }); + if (show) { + const { showKGOnePanel, showChatBox, showEventListPanel } = get(); + const activePanel = showKGOnePanel + ? 'kgone' + : showChatBox + ? 'chat' + : showEventListPanel + ? 'eventList' + : null; + + set({ + showSettings: true, + settingsReturnSidePanel: activePanel, + }); + return; + } + + const { settingsReturnSidePanel } = get(); + set({ + showSettings: false, + settingsReturnSidePanel: null, + ...getSidePanelVisibilityState(settingsReturnSidePanel), + }); }, toggleSettings: () => {