feat: simplify chord guide feature; updated help doc; added hotkeys doc

This commit is contained in:
Xiaohan-Tian
2026-05-28 21:03:02 -07:00
parent cd4d5847eb
commit 4d415a7f7d
18 changed files with 522 additions and 51 deletions
@@ -49,7 +49,7 @@ vi.mock('../localLLMConfig', async () => {
};
});
describe('processUserMessage /welcome', () => {
describe('processUserMessage slash commands', () => {
beforeEach(() => {
configState.clear();
configState.set('general.llm_provider', 'local_browser');
@@ -132,6 +132,49 @@ describe('processUserMessage /welcome', () => {
expect(message?.content).toContain('welcome_local_llm.md');
});
it('fetches the hotkeys guide for /hotkeys', async () => {
const result = await processUserMessage('/hotkeys');
expect(fetch).toHaveBeenCalledWith(expect.stringContaining('chat/hotkeys.md'));
expect(result).toMatchObject({
displayUserMessage: false,
sendToLLM: false,
finalMessageForLLM: null,
metadata: { command: 'hotkeys' },
});
expect(result.pseudoAssistantResponse).toContain('chat/hotkeys.md');
});
it('supports /hotkey as an alias of /hotkeys', async () => {
const result = await processUserMessage('/hotkey');
expect(fetch).toHaveBeenCalledWith(expect.stringContaining('chat/hotkeys.md'));
expect(result).toMatchObject({
displayUserMessage: false,
sendToLLM: false,
finalMessageForLLM: null,
metadata: { command: 'hotkeys' },
});
expect(result.pseudoAssistantResponse).toContain('chat/hotkeys.md');
});
it('lists the new hotkeys commands for unknown slash commands', async () => {
const result = await processUserMessage('/unknown foo');
expect(storeState.setStatus).toHaveBeenCalledWith(
'Unknown command: /unknown. Available commands: /clear, /welcome, /help, /hotkeys, /hotkey'
);
expect(result.pseudoAssistantResponse).toBe(
'Unknown command: /unknown foo.\nAvailable commands: /clear, /welcome, /help, /hotkeys, /hotkey'
);
expect(result).toMatchObject({
displayUserMessage: false,
sendToLLM: false,
finalMessageForLLM: null,
metadata: { command: 'unknown' },
});
});
it('blocks local-browser messages when the runtime is hard unsupported', async () => {
detectLocalLLMRuntimeSupportMock.mockReturnValue({
supported: false,
+29 -1
View File
@@ -148,9 +148,37 @@ export async function processUserMessage(originalMessage: string): Promise<UserM
}
}
case '/hotkeys':
case '/hotkey': {
try {
const url = `${import.meta.env.BASE_URL}chat/hotkeys.md`;
const resp = await fetch(url);
if (!resp.ok) {
throw new Error(`Failed to fetch ${url}: ${resp.status}`);
}
const md = await resp.text();
return {
displayUserMessage: false,
sendToLLM: false,
finalMessageForLLM: null,
pseudoAssistantResponse: md,
metadata: { command: 'hotkeys' }
};
} catch (err) {
const fallback = 'Hotkeys guide is currently unavailable.';
return {
displayUserMessage: false,
sendToLLM: false,
finalMessageForLLM: null,
pseudoAssistantResponse: fallback,
metadata: { command: 'hotkeys', error: String(err) }
};
}
}
default: {
const { setStatus } = useProjectStore.getState();
const help = 'Available commands: /clear, /welcome, /help';
const help = 'Available commands: /clear, /welcome, /help, /hotkeys, /hotkey';
setStatus(`Unknown command: ${command}. ${help}`);
return {
displayUserMessage: false,
+8
View File
@@ -17,6 +17,14 @@ export const getRootNoteFromKeySignature = (keySignature: KeySignature): string
return match[1];
};
/**
* Resolves the chord-guide mode from a key signature.
* Chord guiding only supports major/minor quality and maps them to Ionian/Aeolian.
*/
export const getChordGuideModeFromKeySignature = (keySignature: KeySignature): 'ionian' | 'aeolian' => {
return keySignature.endsWith(' minor') ? 'aeolian' : 'ionian';
};
/**
* Converts a note name (without octave) to pitch class (0-11)
* @param noteName - Note name like "C", "C#", "Db", "F#"