feat: support Command Menu (CMD + J) (#10271)

* feat: Init

* feat: Add more commands

* opti: Use lazy load

* feat: More command

* fix: CMDK position

* style: Add shortkey hint

* feat: Add entry

* feat: Add About entries

* feat: Add shortcut hint

* feat: Create agent in CMDK

* feat: Ues cmd + J temproraily

* fix: Add missing translation
This commit is contained in:
René Wang
2025-11-20 23:27:08 +08:00
committed by GitHub
parent 9472001461
commit a9aed0bc44
44 changed files with 972 additions and 0 deletions

View File

@@ -135,6 +135,27 @@
}
},
"close": "إغلاق",
"cmdk": {
"about": "حول",
"communitySupport": "دعم المجتمع",
"discover": "استكشاف",
"knowledgeBase": "قاعدة المعرفة",
"navigate": "التنقل",
"newAgent": "إنشاء مساعد جديد",
"noResults": "لم يتم العثور على نتائج",
"openSettings": "فتح الإعدادات",
"painting": "الرسم بالذكاء الاصطناعي",
"searchPlaceholder": "أدخل أمرًا أو ابحث...",
"settings": "الإعدادات",
"starOnGitHub": "قيّمنا على GitHub",
"submitIssue": "إرسال مشكلة",
"theme": "السمة",
"themeAuto": "اتباع النظام",
"themeDark": "الوضع الداكن",
"themeLight": "الوضع الفاتح",
"toOpen": "فتح",
"toSelect": "تحديد"
},
"confirm": "تأكيد",
"contact": "اتصل بنا",
"copy": "نسخ",

View File

@@ -7,6 +7,10 @@
"desc": "مسح الرسائل والملفات المرفوعة في المحادثة الحالية",
"title": "مسح رسائل المحادثة"
},
"commandPalette": {
"desc": "افتح لوحة الأوامر العامة للوصول السريع إلى الميزات",
"title": "لوحة الأوامر"
},
"deleteAndRegenerateMessage": {
"desc": "حذف الرسالة الأخيرة وإعادة إنشائها",
"title": "حذف وإعادة إنشاء"

View File

@@ -135,6 +135,27 @@
}
},
"close": "Затвори",
"cmdk": {
"about": "Относно",
"communitySupport": "Общностна поддръжка",
"discover": "Открий",
"knowledgeBase": "База знания",
"navigate": "Навигация",
"newAgent": "Създай агент",
"noResults": "Няма намерени резултати",
"openSettings": "Отвори настройките",
"painting": "AI Рисуване",
"searchPlaceholder": "Въведете команда или търсене...",
"settings": "Настройки",
"starOnGitHub": "Дайте ни звезда в GitHub",
"submitIssue": "Подайте проблем",
"theme": "Тема",
"themeAuto": "Следвай системата",
"themeDark": "Тъмен режим",
"themeLight": "Светъл режим",
"toOpen": "Отвори",
"toSelect": "Избери"
},
"confirm": "Потвърди",
"contact": "Свържете се с нас",
"copy": "Копирай",

View File

@@ -7,6 +7,10 @@
"desc": "Изтриване на текущите съобщения и качените файлове в сесията",
"title": "Изтриване на съобщенията в сесията"
},
"commandPalette": {
"desc": "Отворете глобалния панел с команди за бърз достъп до функции",
"title": "Панел с команди"
},
"deleteAndRegenerateMessage": {
"desc": "Изтриване на последното съобщение и повторно генериране",
"title": "Изтрий и генерирай отново"

View File

@@ -135,6 +135,27 @@
}
},
"close": "Schließen",
"cmdk": {
"about": "Über",
"communitySupport": "Community-Support",
"discover": "Entdecken",
"knowledgeBase": "Wissensdatenbank",
"navigate": "Navigieren",
"newAgent": "Neuen Assistenten erstellen",
"noResults": "Keine Ergebnisse gefunden",
"openSettings": "Einstellungen öffnen",
"painting": "KI-Malerei",
"searchPlaceholder": "Befehl eingeben oder suchen...",
"settings": "Einstellungen",
"starOnGitHub": "Gib uns einen Stern auf GitHub",
"submitIssue": "Problem melden",
"theme": "Design",
"themeAuto": "Systemeinstellung folgen",
"themeDark": "Dunkles Design",
"themeLight": "Helles Design",
"toOpen": "Öffnen",
"toSelect": "Auswählen"
},
"confirm": "Bestätigen",
"contact": "Kontakt",
"copy": "Kopieren",

View File

@@ -7,6 +7,10 @@
"desc": "Aktuelle Nachrichten und hochgeladene Dateien im Gespräch löschen",
"title": "Gesprächsnachrichten löschen"
},
"commandPalette": {
"desc": "Öffne die globale Befehlspalette für schnellen Zugriff auf Funktionen",
"title": "Befehlspalette"
},
"deleteAndRegenerateMessage": {
"desc": "Letzte Nachricht löschen und neu generieren",
"title": "Löschen und neu generieren"

View File

@@ -135,6 +135,27 @@
}
},
"close": "Close",
"cmdk": {
"about": "About",
"communitySupport": "Community Support",
"discover": "Discover",
"knowledgeBase": "Knowledge Base",
"navigate": "Navigate",
"newAgent": "Create New Assistant",
"noResults": "No results found",
"openSettings": "Open Settings",
"painting": "AI Painting",
"searchPlaceholder": "Enter a command or search...",
"settings": "Settings",
"starOnGitHub": "Star us on GitHub",
"submitIssue": "Submit Issue",
"theme": "Theme",
"themeAuto": "Auto",
"themeDark": "Dark",
"themeLight": "Light",
"toOpen": "to Open",
"toSelect": "to Select"
},
"confirm": "Confirm",
"contact": "Contact Us",
"copy": "Copy",

View File

@@ -7,6 +7,10 @@
"desc": "Clear the messages and uploaded files from the current conversation",
"title": "Clear Conversation Messages"
},
"commandPalette": {
"desc": "Open the global command palette for quick access to features",
"title": "Command Palette"
},
"deleteAndRegenerateMessage": {
"desc": "Delete the last message and regenerate",
"title": "Delete and Regenerate"

View File

@@ -135,6 +135,27 @@
}
},
"close": "Cerrar",
"cmdk": {
"about": "Acerca de",
"communitySupport": "Soporte de la comunidad",
"discover": "Descubrir",
"knowledgeBase": "Base de conocimientos",
"navigate": "Navegar",
"newAgent": "Nuevo asistente",
"noResults": "No se encontraron resultados",
"openSettings": "Abrir configuración",
"painting": "Dibujo con IA",
"searchPlaceholder": "Escribe un comando o busca...",
"settings": "Configuración",
"starOnGitHub": "Danos una estrella en GitHub",
"submitIssue": "Informar de un problema",
"theme": "Tema",
"themeAuto": "Seguir el sistema",
"themeDark": "Modo oscuro",
"themeLight": "Modo claro",
"toOpen": "Abrir",
"toSelect": "Seleccionar"
},
"confirm": "Confirmar",
"contact": "Contacto",
"copy": "Copiar",

View File

@@ -7,6 +7,10 @@
"desc": "Eliminar los mensajes y archivos subidos de la conversación actual",
"title": "Eliminar mensajes de la conversación"
},
"commandPalette": {
"desc": "Abre el panel de comandos global para acceder rápidamente a las funciones",
"title": "Panel de Comandos"
},
"deleteAndRegenerateMessage": {
"desc": "Eliminar el último mensaje y volver a generarlo",
"title": "Eliminar y regenerar"

View File

@@ -135,6 +135,27 @@
}
},
"close": "بستن",
"cmdk": {
"about": "درباره",
"communitySupport": "پشتیبانی جامعه",
"discover": "کشف",
"knowledgeBase": "پایگاه دانش",
"navigate": "ناوبری",
"newAgent": "دستیار جدید",
"noResults": "نتیجه‌ای یافت نشد",
"openSettings": "باز کردن تنظیمات",
"painting": "نقاشی با هوش مصنوعی",
"searchPlaceholder": "دستور وارد کنید یا جستجو کنید...",
"settings": "تنظیمات",
"starOnGitHub": "به ما در GitHub ستاره بدهید",
"submitIssue": "ارسال مشکل",
"theme": "تم",
"themeAuto": "همگام با سیستم",
"themeDark": "حالت تیره",
"themeLight": "حالت روشن",
"toOpen": "باز کردن",
"toSelect": "انتخاب"
},
"confirm": "تأیید",
"contact": "تماس با ما",
"copy": "کپی",

View File

@@ -7,6 +7,10 @@
"desc": "حذف پیام‌ها و فایل‌های بارگذاری شده در جلسه جاری",
"title": "حذف پیام‌های جلسه"
},
"commandPalette": {
"desc": "باز کردن پنل فرمان جهانی برای دسترسی سریع به قابلیت‌ها",
"title": "پنل فرمان"
},
"deleteAndRegenerateMessage": {
"desc": "آخرین پیام را حذف کرده و دوباره تولید کن",
"title": "حذف و تولید مجدد"

View File

@@ -135,6 +135,27 @@
}
},
"close": "Fermer",
"cmdk": {
"about": "À propos",
"communitySupport": "Support communautaire",
"discover": "Découvrir",
"knowledgeBase": "Base de connaissances",
"navigate": "Naviguer",
"newAgent": "Nouvel assistant",
"noResults": "Aucun résultat trouvé",
"openSettings": "Ouvrir les paramètres",
"painting": "Peinture IA",
"searchPlaceholder": "Entrez une commande ou recherchez...",
"settings": "Paramètres",
"starOnGitHub": "Donnez-nous une étoile sur GitHub",
"submitIssue": "Soumettre un problème",
"theme": "Thème",
"themeAuto": "Suivre le système",
"themeDark": "Mode sombre",
"themeLight": "Mode clair",
"toOpen": "Ouvrir",
"toSelect": "Sélectionner"
},
"confirm": "Confirmer",
"contact": "Nous contacter",
"copy": "Copier",

View File

@@ -7,6 +7,10 @@
"desc": "Effacer les messages de la session actuelle et les fichiers téléchargés",
"title": "Effacer les messages de la session"
},
"commandPalette": {
"desc": "Ouvrez le panneau de commande global pour accéder rapidement aux fonctionnalités",
"title": "Panneau de commande"
},
"deleteAndRegenerateMessage": {
"desc": "Supprimer le dernier message et régénérer",
"title": "Supprimer et régénérer"

View File

@@ -135,6 +135,27 @@
}
},
"close": "Chiudi",
"cmdk": {
"about": "Informazioni",
"communitySupport": "Supporto della comunità",
"discover": "Scopri",
"knowledgeBase": "Base di conoscenza",
"navigate": "Naviga",
"newAgent": "Nuovo assistente",
"noResults": "Nessun risultato trovato",
"openSettings": "Apri impostazioni",
"painting": "Disegno AI",
"searchPlaceholder": "Inserisci un comando o cerca...",
"settings": "Impostazioni",
"starOnGitHub": "Dacci una stella su GitHub",
"submitIssue": "Segnala un problema",
"theme": "Tema",
"themeAuto": "Segui il sistema",
"themeDark": "Tema scuro",
"themeLight": "Tema chiaro",
"toOpen": "Apri",
"toSelect": "Seleziona"
},
"confirm": "Conferma",
"contact": "Contattaci",
"copy": "Copia",

View File

@@ -7,6 +7,10 @@
"desc": "Cancella i messaggi e i file caricati della conversazione attuale",
"title": "Cancella messaggi della conversazione"
},
"commandPalette": {
"desc": "Apri il pannello comandi globale per accedere rapidamente alle funzioni",
"title": "Pannello comandi"
},
"deleteAndRegenerateMessage": {
"desc": "Elimina l'ultimo messaggio e rigeneralo",
"title": "Elimina e rigenera"

View File

@@ -135,6 +135,27 @@
}
},
"close": "閉じる",
"cmdk": {
"about": "概要",
"communitySupport": "コミュニティサポート",
"discover": "発見",
"knowledgeBase": "ナレッジベース",
"navigate": "ナビゲート",
"newAgent": "新しいエージェントを作成",
"noResults": "該当する結果が見つかりませんでした",
"openSettings": "設定を開く",
"painting": "AI ペインティング",
"searchPlaceholder": "コマンドを入力または検索...",
"settings": "設定",
"starOnGitHub": "GitHub でスターを付ける",
"submitIssue": "問題を報告する",
"theme": "テーマ",
"themeAuto": "システムに従う",
"themeDark": "ダークモード",
"themeLight": "ライトモード",
"toOpen": "開く",
"toSelect": "選択"
},
"confirm": "確認",
"contact": "お問い合わせ",
"copy": "コピー",

View File

@@ -7,6 +7,10 @@
"desc": "現在のセッションのメッセージとアップロードされたファイルをクリアする",
"title": "セッションメッセージをクリア"
},
"commandPalette": {
"desc": "グローバルコマンドパレットを開いて機能に素早くアクセス",
"title": "コマンドパレット"
},
"deleteAndRegenerateMessage": {
"desc": "最後のメッセージを削除して再生成する",
"title": "削除して再生成"

View File

@@ -135,6 +135,27 @@
}
},
"close": "닫기",
"cmdk": {
"about": "정보",
"communitySupport": "커뮤니티 지원",
"discover": "탐색",
"knowledgeBase": "지식 베이스",
"navigate": "탐색",
"newAgent": "새 에이전트 만들기",
"noResults": "관련 결과를 찾을 수 없습니다",
"openSettings": "설정 열기",
"painting": "AI 그림",
"searchPlaceholder": "명령어 입력 또는 검색...",
"settings": "설정",
"starOnGitHub": "GitHub에서 Star 주기",
"submitIssue": "문제 제출",
"theme": "테마",
"themeAuto": "시스템 설정 따르기",
"themeDark": "다크 모드",
"themeLight": "라이트 모드",
"toOpen": "열기",
"toSelect": "선택"
},
"confirm": "확인",
"contact": "문의하기",
"copy": "복사",

View File

@@ -7,6 +7,10 @@
"desc": "현재 대화의 메시지와 업로드된 파일을 모두 삭제",
"title": "대화 메시지 지우기"
},
"commandPalette": {
"desc": "전역 명령 팔레트를 열어 기능에 빠르게 접근합니다",
"title": "명령 팔레트"
},
"deleteAndRegenerateMessage": {
"desc": "마지막 메시지를 삭제하고 다시 생성합니다",
"title": "삭제 후 다시 생성"

View File

@@ -135,6 +135,27 @@
}
},
"close": "Sluiten",
"cmdk": {
"about": "Over",
"communitySupport": "Communityondersteuning",
"discover": "Ontdekken",
"knowledgeBase": "Kennisbank",
"navigate": "Navigeren",
"newAgent": "Nieuwe Assistent",
"noResults": "Geen resultaten gevonden",
"openSettings": "Instellingen openen",
"painting": "AI-schilderen",
"searchPlaceholder": "Voer een commando in of zoek...",
"settings": "Instellingen",
"starOnGitHub": "Geef ons een ster op GitHub",
"submitIssue": "Probleem melden",
"theme": "Thema",
"themeAuto": "Systeem volgen",
"themeDark": "Donkere modus",
"themeLight": "Lichte modus",
"toOpen": "Openen",
"toSelect": "Selecteren"
},
"confirm": "Bevestigen",
"contact": "Contacteer ons",
"copy": "Kopiëren",

View File

@@ -7,6 +7,10 @@
"desc": "Verwijder de berichten en geüploade bestanden van de huidige sessie",
"title": "Verwijder sessieberichten"
},
"commandPalette": {
"desc": "Open het globale opdrachtpaneel voor snelle toegang tot functies",
"title": "Opdrachtpaneel"
},
"deleteAndRegenerateMessage": {
"desc": "Verwijder het laatste bericht en genereer opnieuw",
"title": "Verwijderen en opnieuw genereren"

View File

@@ -135,6 +135,27 @@
}
},
"close": "Zamknij",
"cmdk": {
"about": "O nas",
"communitySupport": "Wsparcie społeczności",
"discover": "Odkrywaj",
"knowledgeBase": "Baza wiedzy",
"navigate": "Nawigacja",
"newAgent": "Nowy asystent",
"noResults": "Brak wyników",
"openSettings": "Otwórz ustawienia",
"painting": "Sztuka AI",
"searchPlaceholder": "Wpisz polecenie lub wyszukaj...",
"settings": "Ustawienia",
"starOnGitHub": "Daj nam gwiazdkę na GitHubie",
"submitIssue": "Zgłoś problem",
"theme": "Motyw",
"themeAuto": "Zgodnie z systemem",
"themeDark": "Tryb ciemny",
"themeLight": "Tryb jasny",
"toOpen": "Otwórz",
"toSelect": "Wybierz"
},
"confirm": "Potwierdź",
"contact": "Skontaktuj się z nami",
"copy": "Kopiuj",

View File

@@ -7,6 +7,10 @@
"desc": "Wyczyść wiadomości i przesłane pliki w bieżącej rozmowie",
"title": "Wyczyść wiadomości rozmowy"
},
"commandPalette": {
"desc": "Otwórz globalny panel poleceń, aby szybko uzyskać dostęp do funkcji",
"title": "Panel poleceń"
},
"deleteAndRegenerateMessage": {
"desc": "Usuń ostatnią wiadomość i wygeneruj ponownie",
"title": "Usuń i wygeneruj ponownie"

View File

@@ -135,6 +135,27 @@
}
},
"close": "Fechar",
"cmdk": {
"about": "Sobre",
"communitySupport": "Suporte da Comunidade",
"discover": "Descobrir",
"knowledgeBase": "Base de Conhecimento",
"navigate": "Navegar",
"newAgent": "Novo Assistente",
"noResults": "Nenhum resultado encontrado",
"openSettings": "Abrir Configurações",
"painting": "Pintura com IA",
"searchPlaceholder": "Digite um comando ou pesquise...",
"settings": "Configurações",
"starOnGitHub": "Dê uma estrela no GitHub",
"submitIssue": "Reportar um problema",
"theme": "Tema",
"themeAuto": "Seguir o sistema",
"themeDark": "Modo Escuro",
"themeLight": "Modo Claro",
"toOpen": "Abrir",
"toSelect": "Selecionar"
},
"confirm": "Confirmar",
"contact": "Entre em contato",
"copy": "Copiar",

View File

@@ -7,6 +7,10 @@
"desc": "Limpar as mensagens da conversa atual e os arquivos enviados",
"title": "Limpar mensagens da conversa"
},
"commandPalette": {
"desc": "Abra o painel de comandos global para acessar funções rapidamente",
"title": "Painel de Comandos"
},
"deleteAndRegenerateMessage": {
"desc": "Excluir a última mensagem e gerar novamente",
"title": "Excluir e gerar novamente"

View File

@@ -135,6 +135,27 @@
}
},
"close": "Закрыть",
"cmdk": {
"about": "О нас",
"communitySupport": "Поддержка сообщества",
"discover": "Обзор",
"knowledgeBase": "База знаний",
"navigate": "Навигация",
"newAgent": "Создать помощника",
"noResults": "Ничего не найдено",
"openSettings": "Открыть настройки",
"painting": "AI-рисование",
"searchPlaceholder": "Введите команду или выполните поиск...",
"settings": "Настройки",
"starOnGitHub": "Поставьте звезду на GitHub",
"submitIssue": "Сообщить о проблеме",
"theme": "Тема",
"themeAuto": "Системная тема",
"themeDark": "Тёмная тема",
"themeLight": "Светлая тема",
"toOpen": "Открыть",
"toSelect": "Выбрать"
},
"confirm": "Подтвердить",
"contact": "Свяжитесь с нами",
"copy": "Копировать",

View File

@@ -7,6 +7,10 @@
"desc": "Очистить сообщения текущего сеанса и загруженные файлы",
"title": "Очистить сообщения сеанса"
},
"commandPalette": {
"desc": "Откройте глобальную панель команд для быстрого доступа к функциям",
"title": "Панель команд"
},
"deleteAndRegenerateMessage": {
"desc": "Удалить последнее сообщение и сгенерировать заново",
"title": "Удалить и сгенерировать заново"

View File

@@ -135,6 +135,27 @@
}
},
"close": "Kapat",
"cmdk": {
"about": "Hakkında",
"communitySupport": "Topluluk Desteği",
"discover": "Keşfet",
"knowledgeBase": "Bilgi Tabanı",
"navigate": "Gezin",
"newAgent": "Yeni Asistan Oluştur",
"noResults": "Sonuç bulunamadı",
"openSettings": "Ayarları Aç",
"painting": "Yapay Zeka Resmi",
"searchPlaceholder": "Komut girin veya arayın...",
"settings": "Ayarlar",
"starOnGitHub": "GitHub'da Bize Yıldız Verin",
"submitIssue": "Sorun Bildir",
"theme": "Tema",
"themeAuto": "Sistemle Eşle",
"themeDark": "Karanlık Mod",
"themeLight": "Aydınlık Mod",
"toOpen": "Aç",
"toSelect": "Seç"
},
"confirm": "Onayla",
"contact": "Bize Ulaşın",
"copy": "Kopyala",

View File

@@ -7,6 +7,10 @@
"desc": "Geçerli oturumun mesajlarını ve yüklenen dosyaları temizle",
"title": "Oturum mesajlarını temizle"
},
"commandPalette": {
"desc": "Genel komut panelini açarak işlevlere hızlı erişim sağlayın",
"title": "Komut Paneli"
},
"deleteAndRegenerateMessage": {
"desc": "Son mesajı sil ve yeniden oluştur",
"title": "Sil ve Yeniden Oluştur"

View File

@@ -135,6 +135,27 @@
}
},
"close": "Đóng",
"cmdk": {
"about": "Giới thiệu",
"communitySupport": "Hỗ trợ cộng đồng",
"discover": "Khám phá",
"knowledgeBase": "Cơ sở kiến thức",
"navigate": "Điều hướng",
"newAgent": "Tạo trợ lý mới",
"noResults": "Không tìm thấy kết quả phù hợp",
"openSettings": "Mở cài đặt",
"painting": "Vẽ bằng AI",
"searchPlaceholder": "Nhập lệnh hoặc tìm kiếm...",
"settings": "Cài đặt",
"starOnGitHub": "Đánh giá sao trên GitHub",
"submitIssue": "Gửi vấn đề",
"theme": "Giao diện",
"themeAuto": "Theo hệ thống",
"themeDark": "Chế độ tối",
"themeLight": "Chế độ sáng",
"toOpen": "Mở",
"toSelect": "Chọn"
},
"confirm": "Xác nhận",
"contact": "Liên hệ chúng tôi",
"copy": "Sao chép",

View File

@@ -7,6 +7,10 @@
"desc": "Xóa tất cả tin nhắn và tệp đã tải lên trong cuộc trò chuyện hiện tại",
"title": "Xóa tin nhắn cuộc trò chuyện"
},
"commandPalette": {
"desc": "Mở bảng lệnh toàn cục để truy cập nhanh các chức năng",
"title": "Bảng lệnh"
},
"deleteAndRegenerateMessage": {
"desc": "Xoá tin nhắn cuối cùng và tạo lại",
"title": "Xoá và tạo lại"

View File

@@ -135,6 +135,27 @@
}
},
"close": "关闭",
"cmdk": {
"about": "关于",
"communitySupport": "社区支持",
"discover": "发现",
"knowledgeBase": "知识库",
"navigate": "导航",
"newAgent": "新建助手",
"noResults": "未找到相关结果",
"openSettings": "打开设置",
"painting": "AI 绘画",
"searchPlaceholder": "输入命令或搜索...",
"settings": "设置",
"starOnGitHub": "在 GitHub 上给我们 Star",
"submitIssue": "提交问题",
"theme": "主题",
"themeAuto": "跟随系统",
"themeDark": "深色模式",
"themeLight": "浅色模式",
"toOpen": "打开",
"toSelect": "选择"
},
"confirm": "确认",
"contact": "联系我们",
"copy": "复制",

View File

@@ -7,6 +7,10 @@
"desc": "清空当前会话的消息和上传的文件",
"title": "清空会话消息"
},
"commandPalette": {
"desc": "打开全局命令面板快速访问功能",
"title": "命令面板"
},
"deleteAndRegenerateMessage": {
"desc": "删除最后一条消息并重新生成",
"title": "删除并重新生成"

View File

@@ -135,6 +135,27 @@
}
},
"close": "關閉",
"cmdk": {
"about": "關於",
"communitySupport": "社群支援",
"discover": "探索",
"knowledgeBase": "知識庫",
"navigate": "導覽",
"newAgent": "新增助手",
"noResults": "未找到相關結果",
"openSettings": "開啟設定",
"painting": "AI 繪圖",
"searchPlaceholder": "輸入指令或搜尋...",
"settings": "設定",
"starOnGitHub": "在 GitHub 上給我們星標",
"submitIssue": "提交問題",
"theme": "主題",
"themeAuto": "依系統設定",
"themeDark": "深色模式",
"themeLight": "淺色模式",
"toOpen": "開啟",
"toSelect": "選取"
},
"confirm": "確認",
"contact": "聯繫我們",
"copy": "複製",

View File

@@ -7,6 +7,10 @@
"desc": "清空當前會話的消息和上傳的檔案",
"title": "清空會話消息"
},
"commandPalette": {
"desc": "開啟全域指令面板以快速存取功能",
"title": "指令面板"
},
"deleteAndRegenerateMessage": {
"desc": "刪除最後一則訊息並重新產生",
"title": "刪除並重新產生"

View File

@@ -197,6 +197,7 @@
"antd-style": "^3.7.1",
"brotli-wasm": "^3.0.1",
"chroma-js": "^3.1.2",
"cmdk": "^1.1.1",
"cookie": "^1.0.2",
"countries-and-timezones": "^3.8.0",
"dayjs": "^1.11.19",

View File

@@ -15,6 +15,12 @@ export type HotkeyRegistration = HotkeyItem[];
// mod is the command key on Mac, alt is the ctrl key on Windows
export const HOTKEYS_REGISTRATION: HotkeyRegistration = [
// basic
{
group: HotkeyGroupEnum.Essential,
id: HotkeyEnum.CommandPalette,
keys: combineKeys([KeyEnum.Mod, 'j']),
scopes: [HotkeyScopeEnum.Global],
},
{
group: HotkeyGroupEnum.Essential,
id: HotkeyEnum.Search,

View File

@@ -60,6 +60,7 @@ export const KeyEnum = {
export const HotkeyEnum = {
AddUserMessage: 'addUserMessage',
ClearCurrentMessages: 'clearCurrentMessages',
CommandPalette: 'commandPalette',
DeleteAndRegenerateMessage: 'deleteAndRegenerateMessage',
DeleteLastMessage: 'deleteLastMessage',
EditMessage: 'editMessage',

View File

@@ -0,0 +1,470 @@
'use client';
import { Tag } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { Command } from 'cmdk';
import {
ArrowLeft,
ArrowUpDown,
BookOpen,
Bot,
Compass,
CornerDownLeft,
Github,
MessageCircle,
Monitor,
Moon,
Palette,
Settings,
Star,
Sun,
} from 'lucide-react';
import { usePathname, useRouter } from 'next/navigation';
import { memo, useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import { useTranslation } from 'react-i18next';
import { useHotkeyById } from '@/hooks/useHotkeys/useHotkeyById';
import { useGlobalStore } from '@/store/global';
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
import { useSessionStore } from '@/store/session';
import { HotkeyEnum } from '@/types/hotkey';
const useStyles = createStyles(({ css, token }) => ({
backTag: css`
cursor: pointer;
&:hover {
opacity: 0.8;
}
`,
commandFooter: css`
display: flex;
gap: 16px;
align-items: center;
justify-content: flex-end;
padding-block: 8px;
padding-inline: 16px;
border-block-start: 1px solid ${token.colorBorderSecondary};
background: ${token.colorBgContainer};
`,
commandRoot: css`
overflow: hidden;
display: flex;
flex-direction: column;
width: min(640px, 90vw);
max-height: min(500px, 70vh);
border-radius: ${token.borderRadiusLG}px;
background: ${token.colorBgElevated};
box-shadow: ${token.boxShadowSecondary};
animation: slide-down 0.12s ease-out;
@keyframes slide-down {
from {
transform: translateY(-20px) scale(0.96);
opacity: 0;
}
to {
transform: translateY(0) scale(1);
opacity: 1;
}
}
[cmdk-input] {
flex: 1;
min-width: 0;
padding: 0;
border: none;
font-family: inherit;
font-size: 16px;
color: ${token.colorText};
background: transparent;
outline: none;
&::placeholder {
color: ${token.colorTextPlaceholder};
}
}
[cmdk-list] {
overflow-y: auto;
max-height: 400px;
padding: 8px;
}
[cmdk-empty] {
padding-block: 32px;
padding-inline: 16px;
font-size: 14px;
color: ${token.colorTextTertiary};
text-align: center;
}
[cmdk-item] {
cursor: pointer;
user-select: none;
display: flex;
gap: 12px;
align-items: center;
padding-block: 12px;
padding-inline: 16px;
border-radius: ${token.borderRadius}px;
color: ${token.colorText};
transition: all 0.15s ease;
&[aria-selected='true'] {
background: ${token.colorBgTextHover};
}
&:hover {
background: ${token.colorBgTextHover};
}
}
[cmdk-group-heading] {
user-select: none;
padding-block: 8px;
padding-inline: 16px;
font-size: 12px;
font-weight: 500;
color: ${token.colorTextSecondary};
}
[cmdk-separator] {
height: 1px;
margin-block: 4px;
background: ${token.colorBorderSecondary};
}
`,
icon: css`
flex-shrink: 0;
width: 20px;
height: 20px;
color: ${token.colorTextSecondary};
`,
inputWrapper: css`
display: flex;
gap: 8px;
align-items: center;
padding: 16px;
border-block-end: 1px solid ${token.colorBorderSecondary};
`,
itemContent: css`
flex: 1;
min-width: 0;
`,
itemDescription: css`
margin-block-start: 2px;
font-size: 12px;
line-height: 1.4;
color: ${token.colorTextTertiary};
`,
itemLabel: css`
font-size: 14px;
font-weight: 500;
line-height: 1.4;
`,
kbd: css`
display: inline-flex;
gap: 4px;
align-items: center;
padding-block: 2px;
padding-inline: 6px;
border-radius: ${token.borderRadiusSM}px;
font-size: 11px;
font-weight: 500;
line-height: 1.2;
color: ${token.colorTextSecondary};
background: ${token.colorFillQuaternary};
`,
kbdIcon: css`
width: 12px;
height: 12px;
`,
overlay: css`
position: fixed;
z-index: 9999;
inset: 0;
display: flex;
justify-content: center;
padding-block-start: 15vh;
background: ${token.colorBgMask};
animation: fade-in 0.1s ease-in-out;
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
`,
}));
const Cmdk = memo(() => {
const [open, setOpen] = useState(false);
const [mounted, setMounted] = useState(false);
const [search, setSearch] = useState('');
const [pages, setPages] = useState<string[]>([]);
const router = useRouter();
const pathname = usePathname();
const { t } = useTranslation('common');
const { styles } = useStyles();
const switchThemeMode = useGlobalStore((s) => s.switchThemeMode);
const createSession = useSessionStore((s) => s.createSession);
const { showCreateSession } = useServerConfigStore(featureFlagsSelectors);
const page = pages.at(-1);
// Ensure we're mounted on the client
useEffect(() => {
setMounted(true);
}, []);
// Register Cmd+K / Ctrl+K hotkey
useHotkeyById(HotkeyEnum.CommandPalette, () => {
setOpen((prev) => !prev);
});
// Close on Escape key and prevent body scroll
useEffect(() => {
if (open) {
const originalStyle = window.getComputedStyle(document.body).overflow;
document.body.style.overflow = 'hidden';
return () => {
document.body.style.overflow = originalStyle;
};
}
}, [open]);
// Reset pages and search when opening/closing
useEffect(() => {
if (open) {
setPages([]);
setSearch('');
}
}, [open]);
const handleNavigate = (path: string) => {
router.push(path);
setOpen(false);
};
const handleExternalLink = (url: string) => {
window.open(url, '_blank', 'noopener,noreferrer');
setOpen(false);
};
const handleThemeChange = (theme: 'light' | 'dark' | 'auto') => {
switchThemeMode(theme);
setOpen(false);
};
if (!mounted || !open) return null;
return createPortal(
<div className={styles.overlay} onClick={() => setOpen(false)}>
<div onClick={(e) => e.stopPropagation()}>
<Command
className={styles.commandRoot}
onKeyDown={(e) => {
// Escape goes to previous page or closes
if (e.key === 'Escape') {
e.preventDefault();
if (pages.length > 0) {
setPages((prev) => prev.slice(0, -1));
} else {
setOpen(false);
}
}
// Backspace goes to previous page when search is empty
if (e.key === 'Backspace' && !search && pages.length > 0) {
e.preventDefault();
setPages((prev) => prev.slice(0, -1));
}
}}
shouldFilter={true}
>
<div className={styles.inputWrapper}>
{pages.length > 0 && (
<Tag
className={styles.backTag}
icon={<ArrowLeft size={12} />}
onClick={() => setPages((prev) => prev.slice(0, -1))}
/>
)}
<Command.Input
autoFocus
onValueChange={setSearch}
placeholder={t('cmdk.searchPlaceholder')}
value={search}
/>
<Tag>ESC</Tag>
</div>
<Command.List>
<Command.Empty>{t('cmdk.noResults')}</Command.Empty>
{!page && (
<>
{showCreateSession && (
<Command.Item
onSelect={() => {
createSession();
setOpen(false);
}}
value="new-agent"
>
<Bot className={styles.icon} />
<div className={styles.itemContent}>
<div className={styles.itemLabel}>{t('cmdk.newAgent')}</div>
</div>
</Command.Item>
)}
{!pathname?.startsWith('/settings') && (
<Command.Item onSelect={() => handleNavigate('/settings')} value="settings">
<Settings className={styles.icon} />
<div className={styles.itemContent}>
<div className={styles.itemLabel}>{t('cmdk.settings')}</div>
</div>
</Command.Item>
)}
<Command.Item onSelect={() => setPages([...pages, 'theme'])} value="theme">
<Monitor className={styles.icon} />
<div className={styles.itemContent}>
<div className={styles.itemLabel}>{t('cmdk.theme')}</div>
</div>
</Command.Item>
<Command.Group heading={t('cmdk.navigate')}>
{!pathname?.startsWith('/discover') && (
<Command.Item onSelect={() => handleNavigate('/discover')} value="discover">
<Compass className={styles.icon} />
<div className={styles.itemContent}>
<div className={styles.itemLabel}>{t('cmdk.discover')}</div>
</div>
</Command.Item>
)}
{!pathname?.startsWith('/image') && (
<Command.Item onSelect={() => handleNavigate('/image')} value="painting">
<Palette className={styles.icon} />
<div className={styles.itemContent}>
<div className={styles.itemLabel}>{t('cmdk.painting')}</div>
</div>
</Command.Item>
)}
{!pathname?.startsWith('/knowledge') && (
<Command.Item onSelect={() => handleNavigate('/knowledge')} value="knowledge">
<BookOpen className={styles.icon} />
<div className={styles.itemContent}>
<div className={styles.itemLabel}>{t('cmdk.knowledgeBase')}</div>
</div>
</Command.Item>
)}
</Command.Group>
<Command.Group heading={t('cmdk.about')}>
<Command.Item
onSelect={() =>
handleExternalLink('https://github.com/lobehub/lobe-chat/issues/new/choose')
}
value="submit-issue"
>
<Github className={styles.icon} />
<div className={styles.itemContent}>
<div className={styles.itemLabel}>{t('cmdk.submitIssue')}</div>
</div>
</Command.Item>
<Command.Item
onSelect={() => handleExternalLink('https://github.com/lobehub/lobe-chat')}
value="star-github"
>
<Star className={styles.icon} />
<div className={styles.itemContent}>
<div className={styles.itemLabel}>{t('cmdk.starOnGitHub')}</div>
</div>
</Command.Item>
<Command.Item
onSelect={() => handleExternalLink('https://discord.gg/AYFPHvv2jT')}
value="discord"
>
<MessageCircle className={styles.icon} />
<div className={styles.itemContent}>
<div className={styles.itemLabel}>{t('cmdk.communitySupport')}</div>
</div>
</Command.Item>
</Command.Group>
</>
)}
{page === 'theme' && (
<>
<Command.Item onSelect={() => handleThemeChange('light')} value="theme-light">
<Sun className={styles.icon} />
<div className={styles.itemContent}>
<div className={styles.itemLabel}>{t('cmdk.themeLight')}</div>
</div>
</Command.Item>
<Command.Item onSelect={() => handleThemeChange('dark')} value="theme-dark">
<Moon className={styles.icon} />
<div className={styles.itemContent}>
<div className={styles.itemLabel}>{t('cmdk.themeDark')}</div>
</div>
</Command.Item>
<Command.Item onSelect={() => handleThemeChange('auto')} value="theme-auto">
<Monitor className={styles.icon} />
<div className={styles.itemContent}>
<div className={styles.itemLabel}>{t('cmdk.themeAuto')}</div>
</div>
</Command.Item>
</>
)}
</Command.List>
<div className={styles.commandFooter}>
<div className={styles.kbd}>
<CornerDownLeft className={styles.kbdIcon} />
<span>{t('cmdk.toOpen')}</span>
</div>
<div className={styles.kbd}>
<ArrowUpDown className={styles.kbdIcon} />
<span>{t('cmdk.toSelect')}</span>
</div>
</div>
</Command>
</div>
</div>,
document.body,
);
});
Cmdk.displayName = 'Cmdk';
export default Cmdk;

View File

@@ -0,0 +1,17 @@
'use client';
import dynamic from 'next/dynamic';
import { memo } from 'react';
// Lazy load the CMDK component with Next.js dynamic import
// This splits the CMDK code into a separate chunk that only loads when needed
// ssr: false ensures it only loads on the client side
const CmdkComponent = dynamic(() => import('./Cmdk'), {
ssr: false,
});
const CmdkLazy = memo(() => <CmdkComponent />);
CmdkLazy.displayName = 'CmdkLazy';
export default CmdkLazy;

View File

@@ -10,6 +10,7 @@ import { getAntdLocale } from '@/utils/locale';
import AntdV5MonkeyPatch from './AntdV5MonkeyPatch';
import AppTheme from './AppTheme';
import CmdkLazy from './CmdkLazy';
import ImportSettings from './ImportSettings';
import Locale from './Locale';
import QueryProvider from './Query';
@@ -65,6 +66,7 @@ const GlobalLayout = async ({
<ImportSettings />
{process.env.NODE_ENV === 'development' && <DevPanel />}
</Suspense>
<CmdkLazy />
</ServerConfigStoreProvider>
</AppTheme>
</Locale>

View File

@@ -138,6 +138,27 @@ export default {
},
},
close: '关闭',
cmdk: {
about: '关于',
communitySupport: '社区支持',
discover: '发现',
knowledgeBase: '知识库',
navigate: '导航',
newAgent: '新建助手',
noResults: '未找到相关结果',
openSettings: '打开设置',
painting: 'AI 绘画',
searchPlaceholder: '输入命令或搜索...',
settings: '设置',
starOnGitHub: '在 GitHub 上给我们 Star',
submitIssue: '提交问题',
theme: '主题',
themeAuto: '跟随系统',
themeDark: '深色模式',
themeLight: '浅色模式',
toOpen: '打开',
toSelect: '选择',
},
confirm: '确认',
contact: '联系我们',
copy: '复制',

View File

@@ -11,6 +11,10 @@ const hotkey: HotkeyI18nTranslations & {
desc: '清空当前会话的消息和上传的文件',
title: '清空会话消息',
},
commandPalette: {
desc: '打开全局命令面板快速访问功能',
title: '命令面板',
},
deleteAndRegenerateMessage: {
desc: '删除最后一条消息并重新生成',
title: '删除并重新生成',