feat: add Cmd+Shift+. keyboard shortcut for previous mode switching (#5695)

* feat: add Cmd+Shift+. keyboard shortcut for previous mode switching

- Add switchToPreviousMode function that cycles backwards through modes array
- Update handleKeyDown to detect Cmd+Shift+. keyboard combination
- Update modeShortcutText to display both next and previous mode shortcuts
- Add forPreviousMode translation key to all 18 language files
- Implements backwards mode cycling using modulo arithmetic for proper array wrapping

Fixes #5692

* fix: correct keyboard shortcut detection for Cmd+Shift+. (previous mode)

When Shift is pressed with the period key, event.key becomes ">" instead of ".".
Fixed line 1576 to check for event.key === ">" for proper Cmd+Shift+. detection.

Fixes keyboard shortcut issue reported in PR comment.

* fix: use event.code for cross-platform keyboard shortcut compatibility

- Replace event.key checks with event.code === "Period" for both shortcuts
- Fixes keyboard layout compatibility issue where Shift+Period produces different characters on non-US layouts
- Consolidates both shortcuts into a single conditional block for better maintainability
- Addresses feedback from @daniel-lxs in PR #5695
This commit is contained in:
Roomote Agent 2025-07-14 20:44:45 -04:00 • committed by GitHub
parent 88c4261829
commit 8f5c130e21
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 40 additions and 5 deletions

View file

@ -79,7 +79,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
})
const { t } = useAppTranslation()
const { t: tSettings } = useTranslation("settings")
const modeShortcutText = `${isMac ? "⌘" : "Ctrl"} + . ${t("chat:forNextMode")}`
const modeShortcutText = `${isMac ? "⌘" : "Ctrl"} + . ${t("chat:forNextMode")}, ${isMac ? "⌘" : "Ctrl"} + Shift + . ${t("chat:forPreviousMode")}`
const {
clineMessages: messages,
currentTaskItem,
@ -1555,16 +1555,33 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
switchToMode(allModes[nextModeIndex].slug)
}, [mode, customModes, switchToMode])
// Function to handle switching to previous mode
const switchToPreviousMode = useCallback(() => {
const allModes = getAllModes(customModes)
const currentModeIndex = allModes.findIndex((m) => m.slug === mode)
const previousModeIndex = (currentModeIndex - 1 + allModes.length) % allModes.length
// Update local state and notify extension to sync mode change
switchToMode(allModes[previousModeIndex].slug)
}, [mode, customModes, switchToMode])
// Add keyboard event handler
const handleKeyDown = useCallback(
(event: KeyboardEvent) => {
// Check for Command + . (period)
if ((event.metaKey || event.ctrlKey) && event.key === ".") {
// Check for Command/Ctrl + Period (with or without Shift)
// Using event.code for better cross-platform compatibility
if ((event.metaKey || event.ctrlKey) && event.code === "Period") {
event.preventDefault() // Prevent default browser behavior
switchToNextMode()
if (event.shiftKey) {
// Shift + Period = Previous mode
switchToPreviousMode()
} else {
// Just Period = Next mode
switchToNextMode()
}
}
},
[switchToNextMode],
[switchToNextMode, switchToPreviousMode],
)
// Add event listener

View file

@ -123,6 +123,7 @@
"separator": "Separador",
"edit": "Edita...",
"forNextMode": "per al següent mode",
"forPreviousMode": "per al mode anterior",
"error": "Error",
"diffError": {
"title": "Edició fallida"

View file

@ -123,6 +123,7 @@
"separator": "Trennlinie",
"edit": "Bearbeiten...",
"forNextMode": "für nächsten Modus",
"forPreviousMode": "für vorherigen Modus",
"error": "Fehler",
"diffError": {
"title": "Bearbeitung fehlgeschlagen"

View file

@ -131,6 +131,7 @@
"separator": "Separator",
"edit": "Edit...",
"forNextMode": "for next mode",
"forPreviousMode": "for previous mode",
"apiRequest": {
"title": "API Request",
"failed": "API Request Failed",

View file

@ -123,6 +123,7 @@
"separator": "Separador",
"edit": "Editar...",
"forNextMode": "para el siguiente modo",
"forPreviousMode": "para el modo anterior",
"error": "Error",
"diffError": {
"title": "Edición fallida"

View file

@ -123,6 +123,7 @@
"separator": "Séparateur",
"edit": "Éditer...",
"forNextMode": "pour le prochain mode",
"forPreviousMode": "pour le mode précédent",
"error": "Erreur",
"diffError": {
"title": "Modification échouée"

View file

@ -123,6 +123,7 @@
"separator": "विभाजक",
"edit": "संपादित करें...",
"forNextMode": "अगले मोड के लिए",
"forPreviousMode": "पिछले मोड के लिए",
"error": "त्रुटि",
"diffError": {
"title": "संपादन असफल"

View file

@ -137,6 +137,7 @@
"separator": "Pemisah",
"edit": "Edit...",
"forNextMode": "untuk mode selanjutnya",
"forPreviousMode": "untuk mode sebelumnya",
"apiRequest": {
"title": "Permintaan API",
"failed": "Permintaan API Gagal",

View file

@ -123,6 +123,7 @@
"separator": "Separatore",
"edit": "Modifica...",
"forNextMode": "per la prossima modalità",
"forPreviousMode": "per la modalità precedente",
"instructions": {
"wantsToFetch": "Roo vuole recuperare istruzioni dettagliate per aiutare con l'attività corrente"
},

View file

@ -123,6 +123,7 @@
"separator": "区切り",
"edit": "編集...",
"forNextMode": "次のモード用",
"forPreviousMode": "前のモード用",
"error": "エラー",
"diffError": {
"title": "編集に失敗しました"

View file

@ -123,6 +123,7 @@
"separator": "구분자",
"edit": "편집...",
"forNextMode": "다음 모드용",
"forPreviousMode": "이전 모드용",
"error": "오류",
"diffError": {
"title": "편집 실패"

View file

@ -123,6 +123,7 @@
"separator": "Scheidingsteken",
"edit": "Bewerken...",
"forNextMode": "voor volgende modus",
"forPreviousMode": "voor vorige modus",
"apiRequest": {
"title": "API-verzoek",
"failed": "API-verzoek mislukt",

View file

@ -123,6 +123,7 @@
"separator": "Separator",
"edit": "Edytuj...",
"forNextMode": "dla następnego trybu",
"forPreviousMode": "dla poprzedniego trybu",
"error": "Błąd",
"diffError": {
"title": "Edycja nieudana"

View file

@ -123,6 +123,7 @@
"separator": "Separador",
"edit": "Editar...",
"forNextMode": "para o próximo modo",
"forPreviousMode": "para o modo anterior",
"error": "Erro",
"diffError": {
"title": "Edição mal-sucedida"

View file

@ -123,6 +123,7 @@
"separator": "Разделитель",
"edit": "Редактировать...",
"forNextMode": "для следующего режима",
"forPreviousMode": "для предыдущего режима",
"apiRequest": {
"title": "API-запрос",
"failed": "API-запрос не выполнен",

View file

@ -123,6 +123,7 @@
"separator": "Ayırıcı",
"edit": "Düzenle...",
"forNextMode": "sonraki mod için",
"forPreviousMode": "önceki mod için",
"error": "Hata",
"diffError": {
"title": "Düzenleme Başarısız"

View file

@ -123,6 +123,7 @@
"separator": "Dấu phân cách",
"edit": "Chỉnh sửa...",
"forNextMode": "cho chế độ tiếp theo",
"forPreviousMode": "cho chế độ trước đó",
"error": "Lỗi",
"diffError": {
"title": "Chỉnh sửa không thành công"

View file

@ -123,6 +123,7 @@
"separator": "分隔符",
"edit": "编辑...",
"forNextMode": "用于下一个模式",
"forPreviousMode": "用于上一个模式",
"error": "错误",
"diffError": {
"title": "编辑失败"

View file

@ -123,6 +123,7 @@
"separator": "分隔符號",
"edit": "編輯...",
"forNextMode": "用於下一個模式",
"forPreviousMode": "用於上一個模式",
"error": "錯誤",
"diffError": {
"title": "編輯失敗"