mirror of
https://github.com/aleksey-hoffman/sigma-file-manager.git
synced 2026-10-19 01:31:05 -05:00
New feature: duplicate tab
This commit is contained in:
1 parent
662a6988b3
commit
dc5c8cb32e
27 files changed
+252
-38
No files matched your search
@@ -77,6 +77,7 @@
|
||||
},
|
||||
"tabs": {
|
||||
"newTab": "新标签页",
|
||||
"duplicateTab": "复制标签页",
|
||||
"closeOtherTabs": "关闭其他标签",
|
||||
"closeDuplicateTabs": "关闭所有重复标签",
|
||||
"closeAllTabs": "关闭所有标签",
|
||||
@@ -556,6 +557,8 @@
|
||||
"openEntry": "打开地址编辑器",
|
||||
"copyCurrentDirectoryPathToClipboard": "复制当前目录路径到剪贴板",
|
||||
"openCopiedPath": "打开复制的路径",
|
||||
"openNewTab": "打开新标签页",
|
||||
"duplicateCurrentTab": "复制当前标签页",
|
||||
"openCurrentDirInNewTab": "在新标签页中打开当前目录",
|
||||
"openCurrentDirInTerminal": "在终端打开当前目录",
|
||||
"openCurrentDirInTerminalAsAdmin": "以管理员身份在终端中打开当前目录",
|
||||
@@ -1257,4 +1260,4 @@
|
||||
"replaceShareDialogDescription": "文件已在网络上共享。当前共享将被停止并替换为新的共享。",
|
||||
"replaceShareDialogConfirm": "替换"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,7 @@
|
||||
},
|
||||
"tabs": {
|
||||
"newTab": "Neuer Tab",
|
||||
"duplicateTab": "Tab duplizieren",
|
||||
"closeOtherTabs": "Andere Tabs schließen",
|
||||
"closeDuplicateTabs": "Alle Duplikate schließen",
|
||||
"closeAllTabs": "Alle Tabs schließen",
|
||||
@@ -556,6 +557,8 @@
|
||||
"openEntry": "Adresseditor öffnen",
|
||||
"copyCurrentDirectoryPathToClipboard": "Aktuellen Verzeichnispfad in die Zwischenablage kopieren",
|
||||
"openCopiedPath": "Kopierpfad öffnen",
|
||||
"openNewTab": "Neuen Tab öffnen",
|
||||
"duplicateCurrentTab": "Aktuellen Tab duplizieren",
|
||||
"openCurrentDirInNewTab": "Aktuelles Verzeichnis in neuem Tab öffnen",
|
||||
"openCurrentDirInTerminal": "Aktuelles Verzeichnis im Terminal öffnen",
|
||||
"openCurrentDirInTerminalAsAdmin": "Aktuelles Verzeichnis im Terminal als Administrator öffnen",
|
||||
@@ -1257,4 +1260,4 @@
|
||||
"replaceShareDialogDescription": "Es werden bereits Dateien im Netzwerk freigegeben. Die aktuelle Freigabe wird gestoppt und durch eine neue ersetzt.",
|
||||
"replaceShareDialogConfirm": "Ersetzen"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,7 @@
|
||||
},
|
||||
"tabs": {
|
||||
"newTab": "New tab",
|
||||
"duplicateTab": "Duplicate tab",
|
||||
"closeOtherTabs": "Close other tabs",
|
||||
"closeDuplicateTabs": "Close all duplicates",
|
||||
"closeAllTabs": "Close all tabs",
|
||||
@@ -585,6 +586,8 @@
|
||||
"openEntry": "Open address editor",
|
||||
"copyCurrentDirectoryPathToClipboard": "Copy current directory path to clipboard",
|
||||
"openCopiedPath": "Open copied path",
|
||||
"openNewTab": "Open new tab",
|
||||
"duplicateCurrentTab": "Duplicate current tab",
|
||||
"openCurrentDirInNewTab": "Open current dir in new tab",
|
||||
"openCurrentDirInTerminal": "Open current dir in terminal",
|
||||
"openCurrentDirInTerminalAsAdmin": "Open current dir in terminal as admin",
|
||||
|
||||
@@ -77,6 +77,7 @@
|
||||
},
|
||||
"tabs": {
|
||||
"newTab": "Nueva pestaña",
|
||||
"duplicateTab": "Duplicar pestaña",
|
||||
"closeOtherTabs": "Cerrar otras pestañas",
|
||||
"closeDuplicateTabs": "Cerrar todos los duplicados",
|
||||
"closeAllTabs": "Cerrar todas las pestañas",
|
||||
@@ -556,6 +557,8 @@
|
||||
"openEntry": "Abrir editor de dirección",
|
||||
"copyCurrentDirectoryPathToClipboard": "Copiar la ruta del directorio actual al portapapeles",
|
||||
"openCopiedPath": "Abrir ruta copiada",
|
||||
"openNewTab": "Abrir una nueva pestaña",
|
||||
"duplicateCurrentTab": "Duplicar la pestaña actual",
|
||||
"openCurrentDirInNewTab": "Abrir el directorio actual en una nueva pestaña",
|
||||
"openCurrentDirInTerminal": "Abrir el directorio actual en la terminal",
|
||||
"openCurrentDirInTerminalAsAdmin": "Abrir el directorio actual en la terminal como administrador",
|
||||
@@ -1257,4 +1260,4 @@
|
||||
"replaceShareDialogDescription": "Ya se están compartiendo archivos en la red. El recurso compartido actual se detendrá y será reemplazado por uno nuevo.",
|
||||
"replaceShareDialogConfirm": "Reemplazar"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,7 @@
|
||||
},
|
||||
"tabs": {
|
||||
"newTab": "برگه جدید",
|
||||
"duplicateTab": "تکرار برگه",
|
||||
"closeOtherTabs": "بستن سایر برگه ها",
|
||||
"closeDuplicateTabs": "بستن همه تکراریها",
|
||||
"closeAllTabs": "بستن همه برگه ها",
|
||||
@@ -552,6 +553,8 @@
|
||||
"openEntry": "باز کردن ویرایشگر آدرس",
|
||||
"copyCurrentDirectoryPathToClipboard": "کپی مسیر دایرکتوری فعلی در کلیپ بورد",
|
||||
"openCopiedPath": "باز کردن مسیر کپی شده",
|
||||
"openNewTab": "باز کردن برگه جدید",
|
||||
"duplicateCurrentTab": "تکرار برگه فعلی",
|
||||
"openCurrentDirInNewTab": "باز کردن مسیر فعلی در تب جدید",
|
||||
"openCurrentDirInTerminal": "باز کردن مسیر فعلی در ترمینال",
|
||||
"openCurrentDirInTerminalAsAdmin": "باز کردن dir فعلی در ترمینال به عنوان مدیر",
|
||||
@@ -1257,4 +1260,4 @@
|
||||
"replaceShareDialogDescription": "فایلها از قبل از طریق شبکه به اشتراک گذاشته شدهاند. اشتراکگذاری فعلی متوقف و با مورد جدید جایگزین خواهد شد.",
|
||||
"replaceShareDialogConfirm": "جایگزینی"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,7 @@
|
||||
},
|
||||
"tabs": {
|
||||
"newTab": "Nouvel onglet",
|
||||
"duplicateTab": "Dupliquer l'onglet",
|
||||
"closeOtherTabs": "Fermer les autres onglets",
|
||||
"closeDuplicateTabs": "Fermer tous les doublons",
|
||||
"closeAllTabs": "Fermer tous les onglets",
|
||||
@@ -556,6 +557,8 @@
|
||||
"openEntry": "Ouvrir l'éditeur d'adresse",
|
||||
"copyCurrentDirectoryPathToClipboard": "Copier le chemin du répertoire actuel dans le presse-papiers",
|
||||
"openCopiedPath": "Ouvrir le chemin copié",
|
||||
"openNewTab": "Ouvrir un nouvel onglet",
|
||||
"duplicateCurrentTab": "Dupliquer l'onglet actuel",
|
||||
"openCurrentDirInNewTab": "Ouvrir le répertoire actuel dans un nouvel onglet",
|
||||
"openCurrentDirInTerminal": "Ouvrir le répertoire actuel dans le terminal",
|
||||
"openCurrentDirInTerminalAsAdmin": "Ouvrir le répertoire actuel dans le terminal en tant qu'administrateur",
|
||||
@@ -1257,4 +1260,4 @@
|
||||
"replaceShareDialogDescription": "Des fichiers sont déjà partagés sur le réseau. Le partage actuel sera arrêté et remplacé par le nouveau.",
|
||||
"replaceShareDialogConfirm": "Remplacer"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,7 @@
|
||||
},
|
||||
"tabs": {
|
||||
"newTab": "לשונית חדשה",
|
||||
"duplicateTab": "שכפל לשונית",
|
||||
"closeOtherTabs": "סגור לשוניות אחרות",
|
||||
"closeDuplicateTabs": "סגור את כל הכפילויות",
|
||||
"closeAllTabs": "סגור את כל הלשוניות",
|
||||
@@ -585,6 +586,8 @@
|
||||
"openEntry": "פתח את עורך הכתובת",
|
||||
"copyCurrentDirectoryPathToClipboard": "העתק את נתיב התיקייה הנוכחית ללוח",
|
||||
"openCopiedPath": "פתח את הנתיב שהועתק",
|
||||
"openNewTab": "פתח לשונית חדשה",
|
||||
"duplicateCurrentTab": "שכפל את הלשונית הנוכחית",
|
||||
"openCurrentDirInNewTab": "פתח את התיקייה הנוכחית בלשונית חדשה",
|
||||
"openCurrentDirInTerminal": "פתח את התיקייה הנוכחית במסוף",
|
||||
"openCurrentDirInTerminalAsAdmin": "פתח את התיקייה הנוכחית במסוף כמנהל",
|
||||
@@ -1257,4 +1260,4 @@
|
||||
"replaceShareDialogDescription": "קבצים כבר משותפים ברשת. השיתוף הנוכחי ייעצר ויוחלף בחדש.",
|
||||
"replaceShareDialogConfirm": "החלף"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,7 @@
|
||||
},
|
||||
"tabs": {
|
||||
"newTab": "नया टैब",
|
||||
"duplicateTab": "टैब डुप्लिकेट करें",
|
||||
"closeOtherTabs": "अन्य टैब बंद करें",
|
||||
"closeDuplicateTabs": "सभी डुप्लिकेट बंद करें",
|
||||
"closeAllTabs": "सभी टैब को बंद करें",
|
||||
@@ -585,6 +586,8 @@
|
||||
"openEntry": "पता संपादक खोलें",
|
||||
"copyCurrentDirectoryPathToClipboard": "वर्तमान निर्देशिका पथ को क्लिपबोर्ड पर कॉपी करें",
|
||||
"openCopiedPath": "कॉपी किया गया पथ खोलें",
|
||||
"openNewTab": "नया टैब खोलें",
|
||||
"duplicateCurrentTab": "वर्तमान टैब डुप्लिकेट करें",
|
||||
"openCurrentDirInNewTab": "वर्तमान डीआईआर को नए टैब में खोलें",
|
||||
"openCurrentDirInTerminal": "टर्मिनल में वर्तमान डीआईआर खोलें",
|
||||
"openCurrentDirInTerminalAsAdmin": "टर्मिनल में वर्तमान डीआईआर को व्यवस्थापक के रूप में खोलें",
|
||||
@@ -1257,4 +1260,4 @@
|
||||
"replaceShareDialogDescription": "फ़ाइलें पहले से ही नेटवर्क पर साझा की जा रही हैं. वर्तमान शेयर बंद कर दिया जाएगा और उसके स्थान पर नया शेयर ले लिया जाएगा।",
|
||||
"replaceShareDialogConfirm": "प्रतिस्थापित करें"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,7 @@
|
||||
},
|
||||
"tabs": {
|
||||
"newTab": "Նոր թաբ",
|
||||
"duplicateTab": "Կրկնօրինակել թաբը",
|
||||
"closeOtherTabs": "Փակել այլ թաբերը",
|
||||
"closeDuplicateTabs": "Փակել կրկնվող թաբերը",
|
||||
"closeAllTabs": "Փակել բոլոր թաբերը",
|
||||
@@ -585,6 +586,8 @@
|
||||
"openEntry": "Փոփոխել հասցեն",
|
||||
"copyCurrentDirectoryPathToClipboard": "Պատճենել ընտրված պանակի ուղին",
|
||||
"openCopiedPath": "Բացել պատճենված ուղին",
|
||||
"openNewTab": "Բացել նոր թաբ",
|
||||
"duplicateCurrentTab": "Կրկնօրինակել ընթացիկ թաբը",
|
||||
"openCurrentDirInNewTab": "Բացել ընտրված պանակը նոր թաբում",
|
||||
"openCurrentDirInTerminal": "Բացել ընտրված պանակը տերմինալում",
|
||||
"openCurrentDirInTerminalAsAdmin": "Բացել ընտրված պանակը ադմինի անունից՝ տերմինալում",
|
||||
@@ -1257,4 +1260,4 @@
|
||||
"replaceShareDialogDescription": "Ֆայլերն արդեն կիսվում են ցանցում: Ընթացիկ կիսումը կդադարեցվի և կփոխարինվի նորով:",
|
||||
"replaceShareDialogConfirm": "Փոխարինել"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,7 @@
|
||||
},
|
||||
"tabs": {
|
||||
"newTab": "Nuova scheda",
|
||||
"duplicateTab": "Duplica scheda",
|
||||
"closeOtherTabs": "Chiudi altre schede",
|
||||
"closeDuplicateTabs": "Chiudi tutti i duplicati",
|
||||
"closeAllTabs": "Chiudi tutte le schede",
|
||||
@@ -556,6 +557,8 @@
|
||||
"openEntry": "Apri l'editor dell'indirizzo",
|
||||
"copyCurrentDirectoryPathToClipboard": "Copia il percorso della directory corrente negli appunti",
|
||||
"openCopiedPath": "Apri il percorso copiato",
|
||||
"openNewTab": "Apri una nuova scheda",
|
||||
"duplicateCurrentTab": "Duplica la scheda attuale",
|
||||
"openCurrentDirInNewTab": "Apri la cartella attuale in una nuova scheda",
|
||||
"openCurrentDirInTerminal": "Apri la cartella attuale nel terminale",
|
||||
"openCurrentDirInTerminalAsAdmin": "Apri la cartella attuale nel terminale come amministratore",
|
||||
@@ -1257,4 +1260,4 @@
|
||||
"replaceShareDialogDescription": "Ci sono già file condivisi in rete. La condivisione attuale verrà interrotta e sostituita con quella nuova.",
|
||||
"replaceShareDialogConfirm": "Sostituisci"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,7 @@
|
||||
},
|
||||
"tabs": {
|
||||
"newTab": "新しいタブ",
|
||||
"duplicateTab": "タブを複製",
|
||||
"closeOtherTabs": "他のタブを閉じる",
|
||||
"closeDuplicateTabs": "すべての重複を閉じる",
|
||||
"closeAllTabs": "すべてのタブを閉じる",
|
||||
@@ -556,6 +557,8 @@
|
||||
"openEntry": "アドレスエディターを開く",
|
||||
"copyCurrentDirectoryPathToClipboard": "現在のディレクトリパスをクリップボードにコピー",
|
||||
"openCopiedPath": "コピーしたパスを開く",
|
||||
"openNewTab": "新しいタブを開く",
|
||||
"duplicateCurrentTab": "現在のタブを複製",
|
||||
"openCurrentDirInNewTab": "現在のディレクトリを新しいタブで開く",
|
||||
"openCurrentDirInTerminal": "ターミナルで現在のディレクトリを開く",
|
||||
"openCurrentDirInTerminalAsAdmin": "ターミナルで現在のディレクトリを管理者として開く",
|
||||
@@ -1257,4 +1260,4 @@
|
||||
"replaceShareDialogDescription": "すでにネットワーク上でファイルが共有されています。現在の共有を停止し、新しいものに置き換えます。",
|
||||
"replaceShareDialogConfirm": "置き換え"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,7 @@
|
||||
},
|
||||
"tabs": {
|
||||
"newTab": "Nova guia",
|
||||
"duplicateTab": "Duplicar guia",
|
||||
"closeOtherTabs": "Fechar outras guias",
|
||||
"closeDuplicateTabs": "Fechar todos os duplicados",
|
||||
"closeAllTabs": "Fechar todas as guias",
|
||||
@@ -556,6 +557,8 @@
|
||||
"openEntry": "Abrir editor de endereço",
|
||||
"copyCurrentDirectoryPathToClipboard": "Copiar caminho do diretório atual para a área de transferência",
|
||||
"openCopiedPath": "Caminho copiado aberto",
|
||||
"openNewTab": "Abrir nova aba",
|
||||
"duplicateCurrentTab": "Duplicar a aba atual",
|
||||
"openCurrentDirInNewTab": "Abrir o diretório atual em nova aba",
|
||||
"openCurrentDirInTerminal": "Abra o diretório atual no terminal",
|
||||
"openCurrentDirInTerminalAsAdmin": "Abra o diretório atual no terminal como administrador",
|
||||
@@ -1257,4 +1260,4 @@
|
||||
"replaceShareDialogDescription": "Já existem arquivos sendo compartilhados na rede. O compartilhamento atual será interrompido e substituído pelo novo.",
|
||||
"replaceShareDialogConfirm": "Substituir"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,7 @@
|
||||
},
|
||||
"tabs": {
|
||||
"newTab": "Новая вкладка",
|
||||
"duplicateTab": "Дублировать вкладку",
|
||||
"closeOtherTabs": "Закрыть другие вкладки",
|
||||
"closeDuplicateTabs": "Закрыть все дубликаты",
|
||||
"closeAllTabs": "Закрыть все вкладки",
|
||||
@@ -585,6 +586,8 @@
|
||||
"openEntry": "Открыть редактор адреса",
|
||||
"copyCurrentDirectoryPathToClipboard": "Скопировать текущий путь к папке в буфер обмена",
|
||||
"openCopiedPath": "Открыть скопированный путь",
|
||||
"openNewTab": "Открыть новую вкладку",
|
||||
"duplicateCurrentTab": "Дублировать текущую вкладку",
|
||||
"openCurrentDirInNewTab": "Открыть текущую папку в новой вкладке",
|
||||
"openCurrentDirInTerminal": "Открыть текущую папку в терминале",
|
||||
"openCurrentDirInTerminalAsAdmin": "Открыть текущую папку в терминале от имени администратора",
|
||||
@@ -1257,4 +1260,4 @@
|
||||
"replaceShareDialogDescription": "Файлы уже доступны по сети. Текущий доступ будет остановлен и заменён новым.",
|
||||
"replaceShareDialogConfirm": "Заменить"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,7 @@
|
||||
},
|
||||
"tabs": {
|
||||
"newTab": "Nov zavihek",
|
||||
"duplicateTab": "Podvoji zavihek",
|
||||
"closeOtherTabs": "Zapri druge zavihke",
|
||||
"closeDuplicateTabs": "Zapri vse podvojene",
|
||||
"closeAllTabs": "Zapri vse zavihke",
|
||||
@@ -585,6 +586,8 @@
|
||||
"openEntry": "Odpri urejevalnik naslova",
|
||||
"copyCurrentDirectoryPathToClipboard": "Kopiraj pot trenutne mape v odložišče",
|
||||
"openCopiedPath": "Odpri kopirano pot",
|
||||
"openNewTab": "Odpri nov zavihek",
|
||||
"duplicateCurrentTab": "Podvoji trenutni zavihek",
|
||||
"openCurrentDirInNewTab": "Odpri trenutno mapo v novem zavihku",
|
||||
"openCurrentDirInTerminal": "Odpri trenutno mapo v terminalu",
|
||||
"openCurrentDirInTerminalAsAdmin": "Odpri trenutno mapo v terminalu kot admin",
|
||||
@@ -1257,4 +1260,4 @@
|
||||
"replaceShareDialogDescription": "Datoteke se že delijo po omrežju. Trenutno deljenje bo ustavljeno in zamenjano z novim.",
|
||||
"replaceShareDialogConfirm": "Zamenjaj"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,7 @@
|
||||
},
|
||||
"tabs": {
|
||||
"newTab": "Yeni sekme",
|
||||
"duplicateTab": "Sekmeyi çoğalt",
|
||||
"closeOtherTabs": "Diğer sekmeleri kapat",
|
||||
"closeDuplicateTabs": "Tüm yinelenenleri kapat",
|
||||
"closeAllTabs": "Tüm sekmeleri kapat",
|
||||
@@ -556,6 +557,8 @@
|
||||
"openEntry": "Adres düzenleyiciyi aç",
|
||||
"copyCurrentDirectoryPathToClipboard": "Geçerli dizin yolunu panoya kopyala",
|
||||
"openCopiedPath": "Kopyalanan yolu aç",
|
||||
"openNewTab": "Yeni sekme aç",
|
||||
"duplicateCurrentTab": "Geçerli sekmeyi çoğalt",
|
||||
"openCurrentDirInNewTab": "Geçerli dizini yeni sekmede aç",
|
||||
"openCurrentDirInTerminal": "Geçerli dizini terminalde aç",
|
||||
"openCurrentDirInTerminalAsAdmin": "Geçerli dizini terminalde yönetici olarak aç",
|
||||
@@ -1257,4 +1260,4 @@
|
||||
"replaceShareDialogDescription": "Ağ üzerinden zaten dosyalar paylaşılıyor. Mevcut paylaşım durdurulup yenisiyle değiştirilecek.",
|
||||
"replaceShareDialogConfirm": "Değiştir"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,7 @@
|
||||
},
|
||||
"tabs": {
|
||||
"newTab": "نیا ٹیب",
|
||||
"duplicateTab": "ٹیب نقل کریں",
|
||||
"closeOtherTabs": "دوسرے ٹیب بند کریں",
|
||||
"closeDuplicateTabs": "تمام ڈپلیکیٹ بند کریں",
|
||||
"closeAllTabs": "تمام ٹیب بند کریں",
|
||||
@@ -585,6 +586,8 @@
|
||||
"openEntry": "پتہ ایڈیٹر کھولیں",
|
||||
"copyCurrentDirectoryPathToClipboard": "موجودہ فولڈر کا راستہ کلپ بورڈ پر کاپی کریں",
|
||||
"openCopiedPath": "کاپی شدہ راستہ کھولیں",
|
||||
"openNewTab": "نیا ٹیب کھولیں",
|
||||
"duplicateCurrentTab": "موجودہ ٹیب نقل کریں",
|
||||
"openCurrentDirInNewTab": "موجودہ فولڈر نئے ٹیب میں کھولیں",
|
||||
"openCurrentDirInTerminal": "موجودہ فولڈر ٹرمینل میں کھولیں",
|
||||
"openCurrentDirInTerminalAsAdmin": "موجودہ فولڈر ایڈمن ٹرمینل میں کھولیں",
|
||||
@@ -1257,4 +1260,4 @@
|
||||
"replaceShareDialogDescription": "فائلیں پہلے سے نیٹ ورک پر ہیں۔ موجودہ شیئر بند ہو کر نئے سے بدل جائے گا۔",
|
||||
"replaceShareDialogConfirm": "بدل دیں"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,7 @@
|
||||
},
|
||||
"tabs": {
|
||||
"newTab": "Thẻ mới",
|
||||
"duplicateTab": "Nhân bản thẻ",
|
||||
"closeOtherTabs": "Đóng các thẻ khác",
|
||||
"closeDuplicateTabs": "Đóng tất cả bản trùng",
|
||||
"closeAllTabs": "Đóng tất cả thẻ",
|
||||
@@ -556,6 +557,8 @@
|
||||
"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",
|
||||
"openCopiedPath": "Mở đường dẫn đã sao chép",
|
||||
"openNewTab": "Mở tab mới",
|
||||
"duplicateCurrentTab": "Nhân bản tab hiện tại",
|
||||
"openCurrentDirInNewTab": "Mở thư mục hiện tại trong tab mới",
|
||||
"openCurrentDirInTerminal": "Mở thư mục hiện tại trong thiết bị đầu cuối",
|
||||
"openCurrentDirInTerminalAsAdmin": "Mở thư mục hiện tại trong thiết bị đầu cuối với tư cách quản trị viên",
|
||||
@@ -1257,4 +1260,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ế"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -391,14 +391,10 @@ function handleCreateLink(linkKind: LinkCreationKind) {
|
||||
<component
|
||||
:is="menuItemComponent"
|
||||
v-if="isActionVisible('open-in-new-tab')"
|
||||
class="file-browser-actions-menu__item-with-shortcut"
|
||||
@select="emitAction('open-in-new-tab')"
|
||||
>
|
||||
<SquarePlusIcon :size="16" />
|
||||
<span>{{ t('fileBrowser.actions.openInNewTab') }}</span>
|
||||
<ContextMenuShortcut v-if="shortcutsStore.getShortcutLabel('openNewTab')">
|
||||
{{ shortcutsStore.getShortcutLabel('openNewTab') }}
|
||||
</ContextMenuShortcut>
|
||||
</component>
|
||||
<component
|
||||
:is="menuItemComponent"
|
||||
|
||||
@@ -24,7 +24,10 @@ import { useDismissalLayerStore } from '@/stores/runtime/dismissal-layer';
|
||||
import { useGlobalSearchStore } from '@/stores/runtime/global-search';
|
||||
import { useShortcutsStore, getSelectedTextForCopy } from '@/stores/runtime/shortcuts';
|
||||
import { toast, ToastStatic } from '@/components/ui/toaster';
|
||||
import { getVirtualLocationActionContext } from '@/utils/virtual-location-action-target';
|
||||
import {
|
||||
getVirtualLocationActionContext,
|
||||
resolveDuplicateCurrentTabShortcutTarget,
|
||||
} from '@/utils/virtual-location-action-target';
|
||||
import { blurFocusedDirEntry } from '@/modules/navigator/utils/blur-focused-dir-entry';
|
||||
import { useTerminalsStore } from '@/stores/runtime/terminals';
|
||||
import { useDirSizesStore } from '@/stores/runtime/dir-sizes';
|
||||
@@ -950,23 +953,32 @@ async function openTerminalWithOptions(asAdmin: boolean) {
|
||||
}
|
||||
|
||||
async function handleOpenNewTabShortcut() {
|
||||
const actionContext = getNavigatorActionContext();
|
||||
|
||||
if (actionContext.isBrowsingVirtualLocations) {
|
||||
if (actionContext.actionTargetEntries.length > 0) {
|
||||
getActivePaneRef()?.performSelectionAction?.('open-in-new-tab');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
await workspacesStore.openNewTabGroup();
|
||||
}
|
||||
|
||||
async function handleDuplicateCurrentTabShortcut() {
|
||||
const currentPath = getActiveCurrentPath();
|
||||
const target = resolveDuplicateCurrentTabShortcutTarget(
|
||||
currentPath,
|
||||
getNavigatorActionContext(currentPath),
|
||||
);
|
||||
|
||||
if (!currentPath) {
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
await workspacesStore.openNewTabGroup(currentPath);
|
||||
switch (target.kind) {
|
||||
case 'open-selection':
|
||||
getActivePaneRef()?.performSelectionAction?.('open-in-new-tab');
|
||||
return;
|
||||
case 'clone-path':
|
||||
await workspacesStore.openNewTabGroup(target.path);
|
||||
return;
|
||||
default: {
|
||||
const exhaustiveCheck: never = target;
|
||||
return exhaustiveCheck;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCloseCurrentTabShortcut() {
|
||||
@@ -1119,6 +1131,7 @@ function registerShortcutHandlers() {
|
||||
shortcutsStore.registerHandler('print', handlePrintShortcut, { checkItemSelected: hasSelectedItems });
|
||||
shortcutsStore.registerHandler('properties', handlePropertiesShortcut, { checkItemSelected: hasSelectedItems });
|
||||
shortcutsStore.registerHandler('openNewTab', handleOpenNewTabShortcut);
|
||||
shortcutsStore.registerHandler('duplicateCurrentTab', handleDuplicateCurrentTabShortcut);
|
||||
shortcutsStore.registerHandler('closeCurrentTab', handleCloseCurrentTabShortcut);
|
||||
shortcutsStore.registerHandler('restoreLastClosedTab', handleRestoreLastClosedTabShortcut);
|
||||
shortcutsStore.registerHandler('switchToNextTab', handleSwitchToNextTabShortcut);
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
SearchIcon,
|
||||
TextSearchIcon,
|
||||
CopyIcon,
|
||||
CopyPlusIcon,
|
||||
FolderInputIcon,
|
||||
ClipboardPasteIcon,
|
||||
CheckCheckIcon,
|
||||
@@ -133,6 +134,7 @@ const shortcutIcons: Record<ShortcutId, Component> = {
|
||||
print: PrinterIcon,
|
||||
properties: InfoIcon,
|
||||
openNewTab: PlusIcon,
|
||||
duplicateCurrentTab: CopyPlusIcon,
|
||||
closeCurrentTab: XIcon,
|
||||
restoreLastClosedTab: RotateCcwIcon,
|
||||
switchToNextTab: ChevronsRightIcon,
|
||||
|
||||
@@ -201,9 +201,15 @@ onBeforeUnmount(() => {
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<div class="tab-bar__tooltip-row">
|
||||
{{ t('tabs.newTab') }}
|
||||
<ContextMenuShortcut>{{ shortcutsStore.getShortcutLabel('openNewTab') }}</ContextMenuShortcut>
|
||||
<div class="tab-bar__tooltip">
|
||||
<div class="tab-bar__tooltip-row">
|
||||
{{ t('tabs.newTab') }}
|
||||
<ContextMenuShortcut>{{ shortcutsStore.getShortcutLabel('openNewTab') }}</ContextMenuShortcut>
|
||||
</div>
|
||||
<div class="tab-bar__tooltip-row">
|
||||
{{ t('shortcuts.duplicateCurrentTab') }}
|
||||
<ContextMenuShortcut>{{ shortcutsStore.getShortcutLabel('duplicateCurrentTab') }}</ContextMenuShortcut>
|
||||
</div>
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
@@ -244,6 +250,12 @@ onBeforeUnmount(() => {
|
||||
color: hsl(var(--muted-foreground));
|
||||
}
|
||||
|
||||
.tab-bar__tooltip {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tab-bar__tooltip-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -10,7 +10,7 @@ import { useTimeoutFn, useEventListener } from '@vueuse/core';
|
||||
import { useWorkspacesStore } from '@/stores/storage/workspaces';
|
||||
import { useUserSettingsStore } from '@/stores/storage/user-settings';
|
||||
import type { Tab } from '@/types/workspaces';
|
||||
import { Layers, XIcon, XLineTopIcon } from '@lucide/vue';
|
||||
import { CopyPlusIcon, Layers, XIcon, XLineTopIcon } from '@lucide/vue';
|
||||
import { getPathDisplayName, getPathDisplayValue } from '@/utils/normalize-path';
|
||||
import {
|
||||
Tooltip,
|
||||
@@ -21,8 +21,10 @@ import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { useShortcutsStore } from '@/stores/runtime/shortcuts';
|
||||
import { useFileBrowserDragSession } from '@/modules/navigator/components/file-browser/composables/use-file-browser-drag-session';
|
||||
|
||||
interface Props {
|
||||
@@ -42,6 +44,7 @@ const emit = defineEmits<Emits>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const workspacesStore = useWorkspacesStore();
|
||||
const shortcutsStore = useShortcutsStore();
|
||||
const userSettingsStore = useUserSettingsStore();
|
||||
const dragSession = useFileBrowserDragSession();
|
||||
const activeFileBrowserDragState = dragSession.dragState;
|
||||
@@ -192,6 +195,16 @@ function handleTabMouseUp(event: MouseEvent) {
|
||||
dragSession.dropOn(dropTargetTab.value.path, event.shiftKey ? 'copy' : undefined);
|
||||
}
|
||||
|
||||
async function duplicateTab() {
|
||||
const path = props.tabGroup[0]?.path;
|
||||
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
await workspacesStore.openNewTabGroup(path);
|
||||
}
|
||||
|
||||
function closeOtherTabs() {
|
||||
workspacesStore.closeOtherTabGroups(props.tabGroup);
|
||||
}
|
||||
@@ -265,6 +278,19 @@ onBeforeUnmount(() => {
|
||||
align="start"
|
||||
class="tab__dropdown-menu"
|
||||
>
|
||||
<DropdownMenuItem
|
||||
class="tab__menu-item-with-shortcut"
|
||||
@select="duplicateTab"
|
||||
>
|
||||
<CopyPlusIcon
|
||||
class="tab__menu-button-icon"
|
||||
:size="14"
|
||||
/>
|
||||
{{ t('tabs.duplicateTab') }}
|
||||
<DropdownMenuShortcut v-if="shortcutsStore.getShortcutLabel('duplicateCurrentTab')">
|
||||
{{ shortcutsStore.getShortcutLabel('duplicateCurrentTab') }}
|
||||
</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem @select="closeOtherTabs">
|
||||
<XLineTopIcon
|
||||
class="tab__menu-button-icon"
|
||||
@@ -422,7 +448,11 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
|
||||
.tab__dropdown-menu {
|
||||
min-width: 160px;
|
||||
min-width: 220px;
|
||||
}
|
||||
|
||||
.tab__menu-item-with-shortcut {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.tab__menu-button {
|
||||
|
||||
@@ -578,6 +578,41 @@ describe('shortcuts store', () => {
|
||||
expect(event.defaultPrevented).toBe(true);
|
||||
});
|
||||
|
||||
it('matches Ctrl+T for opening a new tab and Ctrl+Alt+T for duplicating the current tab', async () => {
|
||||
const shortcutsStore = useShortcutsStore();
|
||||
const openNewTabHandler = vi.fn();
|
||||
const duplicateCurrentTabHandler = vi.fn();
|
||||
|
||||
shortcutsStore.registerHandler('openNewTab', openNewTabHandler);
|
||||
shortcutsStore.registerHandler('duplicateCurrentTab', duplicateCurrentTabHandler);
|
||||
|
||||
expect(shortcutsStore.getShortcutLabel('openNewTab')).toBe('Ctrl+T');
|
||||
expect(shortcutsStore.getShortcutLabel('duplicateCurrentTab')).toBe('Ctrl+Alt+T');
|
||||
|
||||
const openNewTabEvent = new KeyboardEvent('keydown', {
|
||||
key: 't',
|
||||
code: 'KeyT',
|
||||
ctrlKey: true,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
const duplicateCurrentTabEvent = new KeyboardEvent('keydown', {
|
||||
key: 't',
|
||||
code: 'KeyT',
|
||||
ctrlKey: true,
|
||||
altKey: true,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
|
||||
await expect(shortcutsStore.handleKeydown(openNewTabEvent)).resolves.toBe(true);
|
||||
await expect(shortcutsStore.handleKeydown(duplicateCurrentTabEvent)).resolves.toBe(true);
|
||||
expect(openNewTabHandler).toHaveBeenCalledTimes(1);
|
||||
expect(duplicateCurrentTabHandler).toHaveBeenCalledTimes(1);
|
||||
expect(openNewTabEvent.defaultPrevented).toBe(true);
|
||||
expect(duplicateCurrentTabEvent.defaultPrevented).toBe(true);
|
||||
});
|
||||
|
||||
it('matches Ctrl+Shift+T for restoring the last closed tab', async () => {
|
||||
const shortcutsStore = useShortcutsStore();
|
||||
const restoreLastClosedTabHandler = vi.fn();
|
||||
|
||||
@@ -452,7 +452,7 @@ const DEFAULT_SHORTCUTS: ShortcutDefinition[] = [
|
||||
},
|
||||
{
|
||||
id: 'openNewTab',
|
||||
labelKey: 'shortcuts.openCurrentDirInNewTab',
|
||||
labelKey: 'shortcuts.openNewTab',
|
||||
defaultKeys: {
|
||||
ctrl: true,
|
||||
key: 't',
|
||||
@@ -464,6 +464,21 @@ const DEFAULT_SHORTCUTS: ShortcutDefinition[] = [
|
||||
},
|
||||
isReadOnly: false,
|
||||
},
|
||||
{
|
||||
id: 'duplicateCurrentTab',
|
||||
labelKey: 'shortcuts.duplicateCurrentTab',
|
||||
defaultKeys: {
|
||||
ctrl: true,
|
||||
alt: true,
|
||||
key: 't',
|
||||
},
|
||||
scope: 'navigator',
|
||||
conditions: {
|
||||
inputFieldIsActive: false,
|
||||
dialogIsOpened: false,
|
||||
},
|
||||
isReadOnly: false,
|
||||
},
|
||||
{
|
||||
id: 'closeCurrentTab',
|
||||
labelKey: 'shortcuts.closeCurrentTab',
|
||||
|
||||
@@ -121,6 +121,7 @@ export type ShortcutId
|
||||
| 'print'
|
||||
| 'properties'
|
||||
| 'openNewTab'
|
||||
| 'duplicateCurrentTab'
|
||||
| 'closeCurrentTab'
|
||||
| 'restoreLastClosedTab'
|
||||
| 'switchToNextTab'
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
getVirtualLocationActionContext,
|
||||
isBrowsingVirtualLocations,
|
||||
resolveActionDirectoryPath,
|
||||
resolveDuplicateCurrentTabShortcutTarget,
|
||||
} from '@/utils/virtual-location-action-target';
|
||||
import { LOCATIONS_VIRTUAL_PATH, WSL_HOST_VIRTUAL_PATH } from '@/utils/virtual-path-constants';
|
||||
|
||||
@@ -156,4 +157,43 @@ describe('virtual-location-action-target', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveDuplicateCurrentTabShortcutTarget', () => {
|
||||
it('clones the current path for a normal directory', () => {
|
||||
expect(resolveDuplicateCurrentTabShortcutTarget(
|
||||
'C:/Users',
|
||||
getVirtualLocationActionContext([], 'C:/Users'),
|
||||
)).toEqual({ kind: 'clone-path', path: 'C:/Users' });
|
||||
});
|
||||
|
||||
it('clones the locations virtual path when nothing is selected', () => {
|
||||
expect(resolveDuplicateCurrentTabShortcutTarget(
|
||||
LOCATIONS_VIRTUAL_PATH,
|
||||
getVirtualLocationActionContext([], LOCATIONS_VIRTUAL_PATH),
|
||||
)).toEqual({ kind: 'clone-path', path: LOCATIONS_VIRTUAL_PATH });
|
||||
});
|
||||
|
||||
it('clones the wsl host virtual path when nothing is selected', () => {
|
||||
expect(resolveDuplicateCurrentTabShortcutTarget(
|
||||
WSL_HOST_VIRTUAL_PATH,
|
||||
getVirtualLocationActionContext([], WSL_HOST_VIRTUAL_PATH),
|
||||
)).toEqual({ kind: 'clone-path', path: WSL_HOST_VIRTUAL_PATH });
|
||||
});
|
||||
|
||||
it('opens the selected location when browsing virtual locations', () => {
|
||||
const selectedEntries = [createEntry({ path: 'D:/' })];
|
||||
|
||||
expect(resolveDuplicateCurrentTabShortcutTarget(
|
||||
LOCATIONS_VIRTUAL_PATH,
|
||||
getVirtualLocationActionContext(selectedEntries, LOCATIONS_VIRTUAL_PATH),
|
||||
)).toEqual({ kind: 'open-selection' });
|
||||
});
|
||||
|
||||
it('returns null when there is no current path', () => {
|
||||
expect(resolveDuplicateCurrentTabShortcutTarget(
|
||||
undefined,
|
||||
getVirtualLocationActionContext([], undefined),
|
||||
)).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -99,3 +99,22 @@ export function getVirtualLocationActionContext(
|
||||
actionTargetPathsText: actionTargetEntries.map(entry => entry.path).join('\n'),
|
||||
};
|
||||
}
|
||||
|
||||
export type DuplicateCurrentTabShortcutTarget =
|
||||
| { kind: 'open-selection' }
|
||||
| { kind: 'clone-path'; path: string };
|
||||
|
||||
export function resolveDuplicateCurrentTabShortcutTarget(
|
||||
currentDirectoryPath: string | null | undefined,
|
||||
actionContext: VirtualLocationActionContext,
|
||||
): DuplicateCurrentTabShortcutTarget | null {
|
||||
if (actionContext.isBrowsingVirtualLocations && actionContext.actionTargetEntries.length > 0) {
|
||||
return { kind: 'open-selection' };
|
||||
}
|
||||
|
||||
if (!currentDirectoryPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { kind: 'clone-path', path: currentDirectoryPath };
|
||||
}
|
||||
Reference in new issue
Block a user