mirror of
https://github.com/aleksey-hoffman/sigma-file-manager.git
synced 2026-10-18 17:20:21 -05:00
New shortcuts: switch to previous/next opened tab
This commit is contained in:
1 parent
7f7be33aed
commit
5b3137c8c7
25 files changed
+266
-13
No files matched your search
@@ -535,6 +535,8 @@
|
||||
"uiZoomDecrease": "用户界面缩放减少",
|
||||
"closeCurrentTab": "关闭当前选项卡/关闭应用程序窗口",
|
||||
"restoreLastClosedTab": "恢复上次关闭的标签页",
|
||||
"switchToNextTab": "切换到下一个标签页",
|
||||
"switchToPreviousTab": "切换到上一个标签页",
|
||||
"toggleAddressBar": "使用当前路径打开地址编辑器",
|
||||
"openEntry": "打开地址编辑器",
|
||||
"copyCurrentDirectoryPathToClipboard": "复制当前目录路径到剪贴板",
|
||||
@@ -1240,4 +1242,4 @@
|
||||
"replaceShareDialogDescription": "文件已在网络上共享。当前共享将被停止并替换为新的共享。",
|
||||
"replaceShareDialogConfirm": "替换"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -535,6 +535,8 @@
|
||||
"uiZoomDecrease": "UI-Zoom verringern",
|
||||
"closeCurrentTab": "Aktuellen Tab schließen / App-Fenster schließen",
|
||||
"restoreLastClosedTab": "Zuletzt geschlossenen Tab wiederherstellen",
|
||||
"switchToNextTab": "Zum nächsten Tab wechseln",
|
||||
"switchToPreviousTab": "Zum vorherigen Tab wechseln",
|
||||
"toggleAddressBar": "Adresseditor mit aktuellem Pfad öffnen",
|
||||
"openEntry": "Adresseditor öffnen",
|
||||
"copyCurrentDirectoryPathToClipboard": "Aktuellen Verzeichnispfad in die Zwischenablage kopieren",
|
||||
|
||||
@@ -564,6 +564,8 @@
|
||||
"uiZoomDecrease": "UI zoom decrease",
|
||||
"closeCurrentTab": "Close current tab / close app window",
|
||||
"restoreLastClosedTab": "Restore last closed tab",
|
||||
"switchToNextTab": "Switch to next tab",
|
||||
"switchToPreviousTab": "Switch to previous tab",
|
||||
"toggleAddressBar": "Open address editor with the current path",
|
||||
"openEntry": "Open address editor",
|
||||
"copyCurrentDirectoryPathToClipboard": "Copy current directory path to clipboard",
|
||||
|
||||
@@ -535,6 +535,8 @@
|
||||
"uiZoomDecrease": "Disminución del zoom de la interfaz de usuario",
|
||||
"closeCurrentTab": "Cerrar pestaña actual/cerrar ventana de aplicación",
|
||||
"restoreLastClosedTab": "Restaurar la última pestaña cerrada",
|
||||
"switchToNextTab": "Cambiar a la pestaña siguiente",
|
||||
"switchToPreviousTab": "Cambiar a la pestaña anterior",
|
||||
"toggleAddressBar": "Abrir editor de dirección con la ruta actual",
|
||||
"openEntry": "Abrir editor de dirección",
|
||||
"copyCurrentDirectoryPathToClipboard": "Copiar la ruta del directorio actual al portapapeles",
|
||||
|
||||
@@ -531,6 +531,8 @@
|
||||
"uiZoomDecrease": "کاهش زوم UI",
|
||||
"closeCurrentTab": "بستن برگه فعلی / بستن پنجره برنامه",
|
||||
"restoreLastClosedTab": "بازگرداندن آخرین برگه بسته شده",
|
||||
"switchToNextTab": "رفتن به برگه بعدی",
|
||||
"switchToPreviousTab": "رفتن به برگه قبلی",
|
||||
"toggleAddressBar": "باز کردن ویرایشگر آدرس با مسیر فعلی",
|
||||
"openEntry": "باز کردن ویرایشگر آدرس",
|
||||
"copyCurrentDirectoryPathToClipboard": "کپی مسیر دایرکتوری فعلی در کلیپ بورد",
|
||||
@@ -1240,4 +1242,4 @@
|
||||
"replaceShareDialogDescription": "فایلها از قبل از طریق شبکه به اشتراک گذاشته شدهاند. اشتراکگذاری فعلی متوقف و با مورد جدید جایگزین خواهد شد.",
|
||||
"replaceShareDialogConfirm": "جایگزینی"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -535,6 +535,8 @@
|
||||
"uiZoomDecrease": "diminution du zoom de l'interface utilisateur",
|
||||
"closeCurrentTab": "Fermer l'onglet actuel / fermer la fenêtre de l'application",
|
||||
"restoreLastClosedTab": "Restaurer le dernier onglet fermé",
|
||||
"switchToNextTab": "Passer à l'onglet suivant",
|
||||
"switchToPreviousTab": "Passer à l'onglet précédent",
|
||||
"toggleAddressBar": "Ouvrir l'éditeur d'adresse avec le chemin actuel",
|
||||
"openEntry": "Ouvrir l'éditeur d'adresse",
|
||||
"copyCurrentDirectoryPathToClipboard": "Copier le chemin du répertoire actuel dans le presse-papiers",
|
||||
|
||||
@@ -564,6 +564,8 @@
|
||||
"uiZoomDecrease": "הקטן את זום הממשק",
|
||||
"closeCurrentTab": "סגור את הלשונית הנוכחית / סגור את חלון היישום",
|
||||
"restoreLastClosedTab": "שחזר את הלשונית האחרונה שנסגרה",
|
||||
"switchToNextTab": "עבור ללשונית הבאה",
|
||||
"switchToPreviousTab": "עבור ללשונית הקודמת",
|
||||
"toggleAddressBar": "פתח את עורך הכתובת עם הנתיב הנוכחי",
|
||||
"openEntry": "פתח את עורך הכתובת",
|
||||
"copyCurrentDirectoryPathToClipboard": "העתק את נתיב התיקייה הנוכחית ללוח",
|
||||
@@ -1240,4 +1242,4 @@
|
||||
"replaceShareDialogDescription": "קבצים כבר משותפים ברשת. השיתוף הנוכחי ייעצר ויוחלף בחדש.",
|
||||
"replaceShareDialogConfirm": "החלף"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -564,6 +564,8 @@
|
||||
"uiZoomDecrease": "यूआई ज़ूम में कमी",
|
||||
"closeCurrentTab": "वर्तमान टैब बंद करें / ऐप विंडो बंद करें",
|
||||
"restoreLastClosedTab": "अंतिम बंद टैब पुनर्स्थापित करें",
|
||||
"switchToNextTab": "अगले टैब पर स्विच करें",
|
||||
"switchToPreviousTab": "पिछले टैब पर स्विच करें",
|
||||
"toggleAddressBar": "वर्तमान पथ के साथ पता संपादक खोलें",
|
||||
"openEntry": "पता संपादक खोलें",
|
||||
"copyCurrentDirectoryPathToClipboard": "वर्तमान निर्देशिका पथ को क्लिपबोर्ड पर कॉपी करें",
|
||||
@@ -1240,4 +1242,4 @@
|
||||
"replaceShareDialogDescription": "फ़ाइलें पहले से ही नेटवर्क पर साझा की जा रही हैं. वर्तमान शेयर बंद कर दिया जाएगा और उसके स्थान पर नया शेयर ले लिया जाएगा।",
|
||||
"replaceShareDialogConfirm": "प्रतिस्थापित करें"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -564,6 +564,8 @@
|
||||
"uiZoomDecrease": "Մասշտաբի իջեցում",
|
||||
"closeCurrentTab": "Փակել ընթացիկ թաբը/փակել ծրագիրը",
|
||||
"restoreLastClosedTab": "Վերաբացել վերջին փակված թաբը",
|
||||
"switchToNextTab": "Անցնել հաջորդ թաբ",
|
||||
"switchToPreviousTab": "Անցնել նախորդ թաբ",
|
||||
"toggleAddressBar": "Բացել այս հասցեի փոփոխման էջը",
|
||||
"openEntry": "Փոփոխել հասցեն",
|
||||
"copyCurrentDirectoryPathToClipboard": "Պատճենել ընտրված պանակի ուղին",
|
||||
@@ -1240,4 +1242,4 @@
|
||||
"replaceShareDialogDescription": "Ֆայլերն արդեն կիսվում են ցանցում: Ընթացիկ կիսումը կդադարեցվի և կփոխարինվի նորով:",
|
||||
"replaceShareDialogConfirm": "Փոխարինել"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -535,6 +535,8 @@
|
||||
"uiZoomDecrease": "Diminuzione zoom della UI",
|
||||
"closeCurrentTab": "Chiudi la scheda attuale / chiudi la finestra dell'app",
|
||||
"restoreLastClosedTab": "Ripristina l'ultima scheda chiusa",
|
||||
"switchToNextTab": "Passa alla scheda successiva",
|
||||
"switchToPreviousTab": "Passa alla scheda precedente",
|
||||
"toggleAddressBar": "Apri l'editor dell'indirizzo con il percorso corrente",
|
||||
"openEntry": "Apri l'editor dell'indirizzo",
|
||||
"copyCurrentDirectoryPathToClipboard": "Copia il percorso della directory corrente negli appunti",
|
||||
|
||||
@@ -535,6 +535,8 @@
|
||||
"uiZoomDecrease": "UIズーム減少",
|
||||
"closeCurrentTab": "現在のタブを閉じる / アプリ ウィンドウを閉じる",
|
||||
"restoreLastClosedTab": "最後に閉じたタブを復元",
|
||||
"switchToNextTab": "次のタブに切り替え",
|
||||
"switchToPreviousTab": "前のタブに切り替え",
|
||||
"toggleAddressBar": "現在のパスでアドレスエディターを開く",
|
||||
"openEntry": "アドレスエディターを開く",
|
||||
"copyCurrentDirectoryPathToClipboard": "現在のディレクトリパスをクリップボードにコピー",
|
||||
|
||||
@@ -535,6 +535,8 @@
|
||||
"uiZoomDecrease": "Diminuir zoom da interface do usuário",
|
||||
"closeCurrentTab": "Fechar aba atual/fechar janela do aplicativo",
|
||||
"restoreLastClosedTab": "Restaurar última aba fechada",
|
||||
"switchToNextTab": "Mudar para a aba seguinte",
|
||||
"switchToPreviousTab": "Mudar para a aba anterior",
|
||||
"toggleAddressBar": "Abrir editor de endereço com o caminho atual",
|
||||
"openEntry": "Abrir editor de endereço",
|
||||
"copyCurrentDirectoryPathToClipboard": "Copiar caminho do diretório atual para a área de transferência",
|
||||
|
||||
@@ -564,6 +564,8 @@
|
||||
"uiZoomDecrease": "уменьшение масштаба интерфейса",
|
||||
"closeCurrentTab": "Закрыть текущую вкладку/закрыть окно приложения",
|
||||
"restoreLastClosedTab": "Восстановить последнюю закрытую вкладку",
|
||||
"switchToNextTab": "Переключиться на следующую вкладку",
|
||||
"switchToPreviousTab": "Переключиться на предыдущую вкладку",
|
||||
"toggleAddressBar": "Открыть редактор адреса с текущим путём",
|
||||
"openEntry": "Открыть редактор адреса",
|
||||
"copyCurrentDirectoryPathToClipboard": "Скопировать текущий путь к папке в буфер обмена",
|
||||
|
||||
@@ -564,6 +564,8 @@
|
||||
"uiZoomDecrease": "Zmanjšaj UI povečavo",
|
||||
"closeCurrentTab": "Zapri trenutni zavihek / zapri okno aplikacije",
|
||||
"restoreLastClosedTab": "Obnovi zadnji zaprti zavihek",
|
||||
"switchToNextTab": "Preklopi na naslednji zavihek",
|
||||
"switchToPreviousTab": "Preklopi na prejšnji zavihek",
|
||||
"toggleAddressBar": "Odpri urejevalnik naslova s trenutno potjo",
|
||||
"openEntry": "Odpri urejevalnik naslova",
|
||||
"copyCurrentDirectoryPathToClipboard": "Kopiraj pot trenutne mape v odložišče",
|
||||
@@ -1240,4 +1242,4 @@
|
||||
"replaceShareDialogDescription": "Datoteke se že delijo po omrežju. Trenutno deljenje bo ustavljeno in zamenjano z novim.",
|
||||
"replaceShareDialogConfirm": "Zamenjaj"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -535,6 +535,8 @@
|
||||
"uiZoomDecrease": "UI yakınlaştırma azaltma",
|
||||
"closeCurrentTab": "Geçerli sekmeyi kapat / uygulama penceresini kapat",
|
||||
"restoreLastClosedTab": "Son kapatılan sekmeyi geri yükle",
|
||||
"switchToNextTab": "Sonraki sekmeye geç",
|
||||
"switchToPreviousTab": "Önceki sekmeye geç",
|
||||
"toggleAddressBar": "Geçerli yolla adres düzenleyiciyi aç",
|
||||
"openEntry": "Adres düzenleyiciyi aç",
|
||||
"copyCurrentDirectoryPathToClipboard": "Geçerli dizin yolunu panoya kopyala",
|
||||
@@ -1240,4 +1242,4 @@
|
||||
"replaceShareDialogDescription": "Ağ üzerinden zaten dosyalar paylaşılıyor. Mevcut paylaşım durdurulup yenisiyle değiştirilecek.",
|
||||
"replaceShareDialogConfirm": "Değiştir"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -564,6 +564,8 @@
|
||||
"uiZoomDecrease": "UI زوم کم کریں",
|
||||
"closeCurrentTab": "موجودہ ٹیب بند کریں / ایپ ونڈو بند کریں",
|
||||
"restoreLastClosedTab": "آخری بند ٹیب بحال کریں",
|
||||
"switchToNextTab": "اگلے ٹیب پر جائیں",
|
||||
"switchToPreviousTab": "پچھلے ٹیب پر جائیں",
|
||||
"toggleAddressBar": "موجودہ راستے کے ساتھ پتہ ایڈیٹر کھولیں",
|
||||
"openEntry": "پتہ ایڈیٹر کھولیں",
|
||||
"copyCurrentDirectoryPathToClipboard": "موجودہ فولڈر کا راستہ کلپ بورڈ پر کاپی کریں",
|
||||
@@ -1240,4 +1242,4 @@
|
||||
"replaceShareDialogDescription": "فائلیں پہلے سے نیٹ ورک پر ہیں۔ موجودہ شیئر بند ہو کر نئے سے بدل جائے گا۔",
|
||||
"replaceShareDialogConfirm": "بدل دیں"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -535,6 +535,8 @@
|
||||
"uiZoomDecrease": "Giảm thu phóng giao diện người dùng",
|
||||
"closeCurrentTab": "Đóng thẻ hiện tại / đóng cửa sổ ứng dụng",
|
||||
"restoreLastClosedTab": "Khôi phục thẻ đã đóng gần nhất",
|
||||
"switchToNextTab": "Chuyển sang thẻ tiếp theo",
|
||||
"switchToPreviousTab": "Chuyển sang thẻ trước",
|
||||
"toggleAddressBar": "Chuyển đổi thanh địa chỉ",
|
||||
"openEntry": "Mở trình chỉnh sửa địa chỉ",
|
||||
"copyCurrentDirectoryPathToClipboard": "Sao chép đường dẫn thư mục hiện tại vào clipboard",
|
||||
@@ -1240,4 +1242,4 @@
|
||||
"replaceShareDialogDescription": "Các tập tin đang được chia sẻ qua mạng. Chia sẻ hiện tại sẽ bị dừng và thay thế bằng chia sẻ mới.",
|
||||
"replaceShareDialogConfirm": "Thay thế"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -981,6 +981,14 @@ async function handleRestoreLastClosedTabShortcut() {
|
||||
return workspacesStore.restoreLastClosedTabGroup();
|
||||
}
|
||||
|
||||
async function handleSwitchToNextTabShortcut() {
|
||||
return workspacesStore.switchToAdjacentTabGroup('next');
|
||||
}
|
||||
|
||||
async function handleSwitchToPreviousTabShortcut() {
|
||||
return workspacesStore.switchToAdjacentTabGroup('previous');
|
||||
}
|
||||
|
||||
async function handleOpenTerminalShortcut() {
|
||||
await openTerminalWithOptions(false);
|
||||
}
|
||||
@@ -1113,6 +1121,8 @@ function registerShortcutHandlers() {
|
||||
shortcutsStore.registerHandler('openNewTab', handleOpenNewTabShortcut);
|
||||
shortcutsStore.registerHandler('closeCurrentTab', handleCloseCurrentTabShortcut);
|
||||
shortcutsStore.registerHandler('restoreLastClosedTab', handleRestoreLastClosedTabShortcut);
|
||||
shortcutsStore.registerHandler('switchToNextTab', handleSwitchToNextTabShortcut);
|
||||
shortcutsStore.registerHandler('switchToPreviousTab', handleSwitchToPreviousTabShortcut);
|
||||
shortcutsStore.registerHandler('openTerminal', handleOpenTerminalShortcut);
|
||||
shortcutsStore.registerHandler('openTerminalAdmin', handleOpenTerminalAdminShortcut);
|
||||
shortcutsStore.registerHandler('navigateUp', () => callActivePaneMethod('navigateUp'));
|
||||
|
||||
@@ -64,6 +64,8 @@ import {
|
||||
HomeIcon,
|
||||
FolderClosedIcon,
|
||||
BookmarkIcon,
|
||||
ChevronsRightIcon,
|
||||
ChevronsLeftIcon,
|
||||
} from '@lucide/vue';
|
||||
import type { Component } from 'vue';
|
||||
import {
|
||||
@@ -127,6 +129,8 @@ const shortcutIcons: Record<ShortcutId, Component> = {
|
||||
openNewTab: PlusIcon,
|
||||
closeCurrentTab: XIcon,
|
||||
restoreLastClosedTab: RotateCcwIcon,
|
||||
switchToNextTab: ChevronsRightIcon,
|
||||
switchToPreviousTab: ChevronsLeftIcon,
|
||||
openTerminal: TerminalSquareIcon,
|
||||
openTerminalAdmin: TerminalSquareIcon,
|
||||
navigateUp: ArrowUpIcon,
|
||||
@@ -164,8 +168,16 @@ let recordCaptureKeyDown: ((event: KeyboardEvent) => void) | null = null;
|
||||
let recordCaptureKeyUp: ((event: KeyboardEvent) => void) | null = null;
|
||||
let recordCaptureMouseDown: ((event: MouseEvent) => void) | null = null;
|
||||
|
||||
function shouldIgnorePhysicalCodeForCaptureTracking(code: string): boolean {
|
||||
return code === 'Tab' || code === 'Enter';
|
||||
function isBareTabKey(event: KeyboardEvent): boolean {
|
||||
return event.key === 'Tab' && !event.ctrlKey && !event.altKey && !event.metaKey;
|
||||
}
|
||||
|
||||
function shouldIgnorePhysicalCodeForCaptureTracking(event: KeyboardEvent): boolean {
|
||||
if (event.code === 'Enter') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isBareTabKey(event);
|
||||
}
|
||||
|
||||
function updateCaptureDisplayLabel() {
|
||||
@@ -301,10 +313,26 @@ function attachRecordCaptureListeners() {
|
||||
recordedKeys.value = null;
|
||||
}
|
||||
|
||||
if (!shouldIgnorePhysicalCodeForCaptureTracking(event.code)) {
|
||||
if (!shouldIgnorePhysicalCodeForCaptureTracking(event)) {
|
||||
pressedPhysicalCodes.add(event.code);
|
||||
}
|
||||
|
||||
if (event.key === 'Tab' && !isBareTabKey(event) && !event.repeat) {
|
||||
const mainKey = resolveShortcutKeyFromKeyboardEvent(event, {
|
||||
preferPhysicalMainKey: true,
|
||||
});
|
||||
|
||||
if (mainKey !== null) {
|
||||
recordedKeys.value = {
|
||||
key: mainKey,
|
||||
ctrl: event.ctrlKey,
|
||||
alt: event.altKey,
|
||||
shift: event.shiftKey,
|
||||
meta: event.metaKey,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
lastCaptureKeyboardEvent.value = event;
|
||||
updateCaptureDisplayLabel();
|
||||
refocusRecordControl();
|
||||
@@ -681,7 +709,7 @@ function handleRecordKeyDown(event: KeyboardEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (key === 'Tab') {
|
||||
if (isBareTabKey(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,17 @@ watch(tabGroupCount, (newCount, previousCount) => {
|
||||
});
|
||||
});
|
||||
|
||||
watch(
|
||||
() => workspacesStore.currentTabGroup?.[0]?.id,
|
||||
(currentTabId, previousTabId) => {
|
||||
if (!currentTabId || currentTabId === previousTabId) {
|
||||
return;
|
||||
}
|
||||
|
||||
scrollSelectedTabGroupIntoView();
|
||||
},
|
||||
);
|
||||
|
||||
function handleScrollActivity() {
|
||||
previewEnabled.value = false;
|
||||
|
||||
|
||||
@@ -598,6 +598,71 @@ describe('shortcuts store', () => {
|
||||
expect(event.defaultPrevented).toBe(true);
|
||||
});
|
||||
|
||||
it('matches Ctrl+Tab and Ctrl+Shift+Tab for switching opened tabs', async () => {
|
||||
const shortcutsStore = useShortcutsStore();
|
||||
const switchToNextTabHandler = vi.fn();
|
||||
const switchToPreviousTabHandler = vi.fn();
|
||||
|
||||
shortcutsStore.registerHandler('switchToNextTab', switchToNextTabHandler);
|
||||
shortcutsStore.registerHandler('switchToPreviousTab', switchToPreviousTabHandler);
|
||||
|
||||
expect(shortcutsStore.getShortcutLabel('switchToNextTab')).toBe('Ctrl+Tab');
|
||||
expect(shortcutsStore.getShortcutLabel('switchToPreviousTab')).toBe('Ctrl+Shift+Tab');
|
||||
|
||||
const nextTabEvent = new KeyboardEvent('keydown', {
|
||||
key: 'Tab',
|
||||
code: 'Tab',
|
||||
ctrlKey: true,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
const previousTabEvent = new KeyboardEvent('keydown', {
|
||||
key: 'Tab',
|
||||
code: 'Tab',
|
||||
ctrlKey: true,
|
||||
shiftKey: true,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
|
||||
await expect(shortcutsStore.handleKeydown(nextTabEvent)).resolves.toBe(true);
|
||||
await expect(shortcutsStore.handleKeydown(previousTabEvent)).resolves.toBe(true);
|
||||
expect(switchToNextTabHandler).toHaveBeenCalledTimes(1);
|
||||
expect(switchToPreviousTabHandler).toHaveBeenCalledTimes(1);
|
||||
expect(nextTabEvent.defaultPrevented).toBe(true);
|
||||
expect(previousTabEvent.defaultPrevented).toBe(true);
|
||||
});
|
||||
|
||||
it('prevents Ctrl+Tab default behavior before async shortcut handlers finish', async () => {
|
||||
const shortcutsStore = useShortcutsStore();
|
||||
|
||||
let resolveHandler: (value: boolean) => void = () => {};
|
||||
|
||||
const switchToNextTabHandler = vi.fn(() => new Promise<boolean>((resolve) => {
|
||||
resolveHandler = resolve;
|
||||
}));
|
||||
|
||||
shortcutsStore.registerHandler('switchToNextTab', switchToNextTabHandler);
|
||||
|
||||
const event = new KeyboardEvent('keydown', {
|
||||
key: 'Tab',
|
||||
code: 'Tab',
|
||||
ctrlKey: true,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
|
||||
const handledPromise = shortcutsStore.handleKeydown(event);
|
||||
|
||||
expect(event.defaultPrevented).toBe(true);
|
||||
|
||||
await Promise.resolve();
|
||||
expect(switchToNextTabHandler).toHaveBeenCalledTimes(1);
|
||||
|
||||
resolveHandler(true);
|
||||
await expect(handledPromise).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('prevents default when an async shortcut handler resolves true', async () => {
|
||||
const shortcutsStore = useShortcutsStore();
|
||||
const restoreLastClosedTabHandler = vi.fn(() => Promise.resolve(true));
|
||||
|
||||
@@ -493,6 +493,35 @@ const DEFAULT_SHORTCUTS: ShortcutDefinition[] = [
|
||||
},
|
||||
isReadOnly: false,
|
||||
},
|
||||
{
|
||||
id: 'switchToNextTab',
|
||||
labelKey: 'shortcuts.switchToNextTab',
|
||||
defaultKeys: {
|
||||
ctrl: true,
|
||||
key: 'Tab',
|
||||
},
|
||||
scope: 'navigator',
|
||||
conditions: {
|
||||
inputFieldIsActive: false,
|
||||
dialogIsOpened: false,
|
||||
},
|
||||
isReadOnly: false,
|
||||
},
|
||||
{
|
||||
id: 'switchToPreviousTab',
|
||||
labelKey: 'shortcuts.switchToPreviousTab',
|
||||
defaultKeys: {
|
||||
ctrl: true,
|
||||
shift: true,
|
||||
key: 'Tab',
|
||||
},
|
||||
scope: 'navigator',
|
||||
conditions: {
|
||||
inputFieldIsActive: false,
|
||||
dialogIsOpened: false,
|
||||
},
|
||||
isReadOnly: false,
|
||||
},
|
||||
{
|
||||
id: 'openTerminal',
|
||||
labelKey: 'shortcuts.openCurrentDirInTerminal',
|
||||
@@ -1157,6 +1186,8 @@ const ADDRESS_BAR_PATH_EDITOR_YIELD_NAVIGATOR_IDS: ReadonlySet<ShortcutId> = new
|
||||
|
||||
const EARLY_PREVENT_DEFAULT_SHORTCUT_IDS: ReadonlySet<ShortcutId> = new Set([
|
||||
'copyCurrentDirectoryPath',
|
||||
'switchToNextTab',
|
||||
'switchToPreviousTab',
|
||||
]);
|
||||
|
||||
function keyboardEventTouchesAddressBarPathEditor(event: KeyboardEvent): boolean {
|
||||
|
||||
@@ -323,6 +323,52 @@ describe('workspaces storage duplicate tabs', () => {
|
||||
expect(workspacesStore.currentTabGroup?.[0]?.id).not.toBe(secondTab.id);
|
||||
});
|
||||
|
||||
it('switches to the next and previous opened tab groups and wraps around', async () => {
|
||||
mockDirectoryReadResponses();
|
||||
|
||||
const firstTab = createTab('first-tab', 'C:/Users/aleks/First');
|
||||
const secondTab = createTab('second-tab', 'C:/Users/aleks/Second');
|
||||
const thirdTab = createTab('third-tab', 'C:/Users/aleks/Third');
|
||||
const workspacesStore = useWorkspacesStore();
|
||||
workspacesStore.workspaces = [
|
||||
createWorkspace([
|
||||
[firstTab],
|
||||
[secondTab],
|
||||
[thirdTab],
|
||||
]),
|
||||
];
|
||||
|
||||
await expect(workspacesStore.switchToAdjacentTabGroup('next')).resolves.toBe(true);
|
||||
expect(workspacesStore.currentWorkspace?.currentTabGroupIndex).toBe(1);
|
||||
expect(workspacesStore.currentTabGroup?.[0]?.id).toBe('second-tab');
|
||||
|
||||
await expect(workspacesStore.switchToAdjacentTabGroup('next')).resolves.toBe(true);
|
||||
await expect(workspacesStore.switchToAdjacentTabGroup('next')).resolves.toBe(true);
|
||||
expect(workspacesStore.currentWorkspace?.currentTabGroupIndex).toBe(0);
|
||||
expect(workspacesStore.currentTabGroup?.[0]?.id).toBe('first-tab');
|
||||
|
||||
await expect(workspacesStore.switchToAdjacentTabGroup('previous')).resolves.toBe(true);
|
||||
expect(workspacesStore.currentWorkspace?.currentTabGroupIndex).toBe(2);
|
||||
expect(workspacesStore.currentTabGroup?.[0]?.id).toBe('third-tab');
|
||||
});
|
||||
|
||||
it('keeps the current tab when only one tab group is open', async () => {
|
||||
mockDirectoryReadResponses();
|
||||
|
||||
const onlyTab = createTab('only-tab', 'C:/Users/aleks/Only');
|
||||
const workspacesStore = useWorkspacesStore();
|
||||
workspacesStore.workspaces = [
|
||||
createWorkspace([
|
||||
[onlyTab],
|
||||
]),
|
||||
];
|
||||
|
||||
await expect(workspacesStore.switchToAdjacentTabGroup('next')).resolves.toBe(true);
|
||||
await expect(workspacesStore.switchToAdjacentTabGroup('previous')).resolves.toBe(true);
|
||||
expect(workspacesStore.currentWorkspace?.currentTabGroupIndex).toBe(0);
|
||||
expect(workspacesStore.currentTabGroup?.[0]?.id).toBe('only-tab');
|
||||
});
|
||||
|
||||
it('shows the restored path when restoring a closed tab group', async () => {
|
||||
mockDirectoryReadResponses();
|
||||
|
||||
|
||||
@@ -550,6 +550,31 @@ export const useWorkspacesStore = defineStore('workspaces', () => {
|
||||
) ?? -1;
|
||||
}
|
||||
|
||||
async function switchToAdjacentTabGroup(direction: 'next' | 'previous'): Promise<boolean> {
|
||||
if (!currentWorkspace.value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const tabGroups = currentWorkspace.value.tabGroups;
|
||||
const tabGroupTotal = tabGroups.length;
|
||||
|
||||
if (tabGroupTotal < 2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const currentIndex = currentWorkspace.value.currentTabGroupIndex || 0;
|
||||
const offset = direction === 'next' ? 1 : -1;
|
||||
const nextIndex = (currentIndex + offset + tabGroupTotal) % tabGroupTotal;
|
||||
const nextTabGroup = tabGroups[nextIndex];
|
||||
|
||||
if (!nextTabGroup) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await openTabGroup(nextTabGroup);
|
||||
return true;
|
||||
}
|
||||
|
||||
async function openOrFocusTabGroup(path: string) {
|
||||
const existingIndex = findTabGroupIndexByPath(path);
|
||||
|
||||
@@ -958,6 +983,7 @@ export const useWorkspacesStore = defineStore('workspaces', () => {
|
||||
openPathInCurrentTab,
|
||||
consumeCurrentTabPathNavigationRequest,
|
||||
restoreLastClosedTabGroup,
|
||||
switchToAdjacentTabGroup,
|
||||
openOrFocusTabGroup,
|
||||
preloadDefaultTab,
|
||||
getDirEntry,
|
||||
|
||||
@@ -123,6 +123,8 @@ export type ShortcutId
|
||||
| 'openNewTab'
|
||||
| 'closeCurrentTab'
|
||||
| 'restoreLastClosedTab'
|
||||
| 'switchToNextTab'
|
||||
| 'switchToPreviousTab'
|
||||
| 'openTerminal'
|
||||
| 'openTerminalAdmin'
|
||||
| 'navigateUp'
|
||||
|
||||
Reference in new issue
Block a user