| import type { FC } from 'react' | |||||
| import { Command } from 'cmdk' | |||||
| import { useTranslation } from 'react-i18next' | |||||
| import type { ActionItem } from './actions/types' | |||||
| type Props = { | |||||
| actions: Record<string, ActionItem> | |||||
| onCommandSelect: (commandKey: string) => void | |||||
| } | |||||
| const CommandSelector: FC<Props> = ({ actions, onCommandSelect }) => { | |||||
| const { t } = useTranslation() | |||||
| return ( | |||||
| <div className="p-4"> | |||||
| <div className="mb-3 text-left text-sm font-medium text-text-secondary"> | |||||
| {t('app.gotoAnything.selectSearchType')} | |||||
| </div> | |||||
| <Command.Group className="space-y-1"> | |||||
| {Object.values(actions).map(action => ( | |||||
| <Command.Item | |||||
| key={action.key} | |||||
| value={action.shortcut} | |||||
| className="flex cursor-pointer items-center rounded-md | |||||
| p-2.5 | |||||
| transition-all | |||||
| duration-150 hover:bg-state-base-hover aria-[selected=true]:bg-state-base-hover" | |||||
| onSelect={() => onCommandSelect(action.shortcut)} | |||||
| > | |||||
| <span className="min-w-[4.5rem] text-left font-mono text-xs text-text-tertiary"> | |||||
| {action.shortcut} | |||||
| </span> | |||||
| <span className="ml-3 text-sm text-text-secondary"> | |||||
| {(() => { | |||||
| const keyMap: Record<string, string> = { | |||||
| '@app': 'app.gotoAnything.actions.searchApplicationsDesc', | |||||
| '@plugin': 'app.gotoAnything.actions.searchPluginsDesc', | |||||
| '@knowledge': 'app.gotoAnything.actions.searchKnowledgeBasesDesc', | |||||
| '@node': 'app.gotoAnything.actions.searchWorkflowNodesDesc', | |||||
| } | |||||
| return t(keyMap[action.key]) | |||||
| })()} | |||||
| </span> | |||||
| </Command.Item> | |||||
| ))} | |||||
| </Command.Group> | |||||
| </div> | |||||
| ) | |||||
| } | |||||
| export default CommandSelector |
| import InstallFromMarketplace from '../plugins/install-plugin/install-from-marketplace' | import InstallFromMarketplace from '../plugins/install-plugin/install-from-marketplace' | ||||
| import type { Plugin } from '../plugins/types' | import type { Plugin } from '../plugins/types' | ||||
| import { Command } from 'cmdk' | import { Command } from 'cmdk' | ||||
| import CommandSelector from './command-selector' | |||||
| type Props = { | type Props = { | ||||
| onHide?: () => void | onHide?: () => void | ||||
| wait: 300, | wait: 300, | ||||
| }) | }) | ||||
| const isCommandsMode = searchQuery.trim() === '@' | |||||
| const searchMode = useMemo(() => { | const searchMode = useMemo(() => { | ||||
| if (isCommandsMode) return 'commands' | |||||
| const query = searchQueryDebouncedValue.toLowerCase() | const query = searchQueryDebouncedValue.toLowerCase() | ||||
| const action = matchAction(query, Actions) | const action = matchAction(query, Actions) | ||||
| return action ? action.key : 'general' | return action ? action.key : 'general' | ||||
| }, [searchQueryDebouncedValue, Actions]) | |||||
| }, [searchQueryDebouncedValue, Actions, isCommandsMode]) | |||||
| const { data: searchResults = [], isLoading, isError, error } = useQuery( | const { data: searchResults = [], isLoading, isError, error } = useQuery( | ||||
| { | { | ||||
| const action = matchAction(query, Actions) | const action = matchAction(query, Actions) | ||||
| return await searchAnything(defaultLocale, query, action) | return await searchAnything(defaultLocale, query, action) | ||||
| }, | }, | ||||
| enabled: !!searchQueryDebouncedValue, | |||||
| enabled: !!searchQueryDebouncedValue && !isCommandsMode, | |||||
| staleTime: 30000, | staleTime: 30000, | ||||
| gcTime: 300000, | gcTime: 300000, | ||||
| }, | }, | ||||
| ) | ) | ||||
| const handleCommandSelect = useCallback((commandKey: string) => { | |||||
| setSearchQuery(`${commandKey} `) | |||||
| setCmdVal('') | |||||
| setTimeout(() => { | |||||
| inputRef.current?.focus() | |||||
| }, 0) | |||||
| }, []) | |||||
| // Handle navigation to selected result | // Handle navigation to selected result | ||||
| const handleNavigate = useCallback((result: SearchResult) => { | const handleNavigate = useCallback((result: SearchResult) => { | ||||
| setShow(false) | setShow(false) | ||||
| [searchResults]) | [searchResults]) | ||||
| const emptyResult = useMemo(() => { | const emptyResult = useMemo(() => { | ||||
| if (searchResults.length || !searchQueryDebouncedValue.trim() || isLoading) | |||||
| if (searchResults.length || !searchQuery.trim() || isLoading || isCommandsMode) | |||||
| return null | return null | ||||
| const isCommandSearch = searchMode !== 'general' | const isCommandSearch = searchMode !== 'general' | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| ) | ) | ||||
| }, [searchResults, searchQueryDebouncedValue, Actions, searchMode, isLoading, isError]) | |||||
| }, [searchResults, searchQuery, Actions, searchMode, isLoading, isError, isCommandsMode]) | |||||
| const defaultUI = useMemo(() => { | const defaultUI = useMemo(() => { | ||||
| if (searchQueryDebouncedValue.trim()) | |||||
| if (searchQuery.trim()) | |||||
| return null | return null | ||||
| return (<div className="flex items-center justify-center py-8 text-center text-text-tertiary"> | |||||
| return (<div className="flex items-center justify-center py-12 text-center text-text-tertiary"> | |||||
| <div> | <div> | ||||
| <div className='text-sm font-medium'>{t('app.gotoAnything.searchTitle')}</div> | <div className='text-sm font-medium'>{t('app.gotoAnything.searchTitle')}</div> | ||||
| <div className='mt-3 space-y-2 text-xs text-text-quaternary'> | |||||
| {Object.values(Actions).map(action => ( | |||||
| <div key={action.key} className='flex items-center gap-2'> | |||||
| <span className='inline-flex items-center rounded bg-gray-200 px-2 py-1 font-mono text-xs font-medium text-gray-600 dark:bg-gray-700 dark:text-gray-200'>{action.shortcut}</span> | |||||
| <span>{(() => { | |||||
| const keyMap: Record<string, string> = { | |||||
| '@app': 'app.gotoAnything.actions.searchApplicationsDesc', | |||||
| '@plugin': 'app.gotoAnything.actions.searchPluginsDesc', | |||||
| '@knowledge': 'app.gotoAnything.actions.searchKnowledgeBasesDesc', | |||||
| '@node': 'app.gotoAnything.actions.searchWorkflowNodesDesc', | |||||
| } | |||||
| return t(keyMap[action.key]) | |||||
| })()}</span> | |||||
| </div> | |||||
| ))} | |||||
| <div className='mt-3 space-y-1 text-xs text-text-quaternary'> | |||||
| <div>{t('app.gotoAnything.searchHint')}</div> | |||||
| <div>{t('app.gotoAnything.commandHint')}</div> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div>) | </div>) | ||||
| }, [searchQueryDebouncedValue, Actions]) | |||||
| }, [searchQuery, Actions]) | |||||
| useEffect(() => { | useEffect(() => { | ||||
| if (show) { | if (show) { | ||||
| )} | )} | ||||
| {!isLoading && !isError && ( | {!isLoading && !isError && ( | ||||
| <> | <> | ||||
| {Object.entries(groupedResults).map(([type, results], groupIndex) => ( | |||||
| {isCommandsMode ? ( | |||||
| <CommandSelector | |||||
| actions={Actions} | |||||
| onCommandSelect={handleCommandSelect} | |||||
| /> | |||||
| ) : ( | |||||
| Object.entries(groupedResults).map(([type, results], groupIndex) => ( | |||||
| <Command.Group key={groupIndex} heading={(() => { | <Command.Group key={groupIndex} heading={(() => { | ||||
| const typeMap: Record<string, string> = { | const typeMap: Record<string, string> = { | ||||
| 'app': 'app.gotoAnything.groups.apps', | 'app': 'app.gotoAnything.groups.apps', | ||||
| </Command.Item> | </Command.Item> | ||||
| ))} | ))} | ||||
| </Command.Group> | </Command.Group> | ||||
| ))} | |||||
| {emptyResult} | |||||
| {defaultUI} | |||||
| )) | |||||
| )} | |||||
| {!isCommandsMode && emptyResult} | |||||
| {!isCommandsMode && defaultUI} | |||||
| </> | </> | ||||
| )} | )} | ||||
| </Command.List> | </Command.List> |
| useAtForSpecific: 'Verwenden von @ für bestimmte Typen', | useAtForSpecific: 'Verwenden von @ für bestimmte Typen', | ||||
| searchTitle: 'Suchen Sie nach irgendetwas', | searchTitle: 'Suchen Sie nach irgendetwas', | ||||
| searching: 'Suche...', | searching: 'Suche...', | ||||
| selectSearchType: 'Wählen Sie aus, wonach gesucht werden soll', | |||||
| commandHint: 'Geben Sie @ ein, um nach Kategorie zu suchen', | |||||
| searchHint: 'Beginnen Sie mit der Eingabe, um alles sofort zu durchsuchen', | |||||
| }, | }, | ||||
| } | } | ||||
| inScope: 'in {{scope}}s', | inScope: 'in {{scope}}s', | ||||
| clearToSearchAll: 'Clear @ to search all', | clearToSearchAll: 'Clear @ to search all', | ||||
| useAtForSpecific: 'Use @ for specific types', | useAtForSpecific: 'Use @ for specific types', | ||||
| selectSearchType: 'Choose what to search for', | |||||
| searchHint: 'Start typing to search everything instantly', | |||||
| commandHint: 'Type @ to browse by category', | |||||
| actions: { | actions: { | ||||
| searchApplications: 'Search Applications', | searchApplications: 'Search Applications', | ||||
| searchApplicationsDesc: 'Search and navigate to your applications', | searchApplicationsDesc: 'Search and navigate to your applications', |
| searchTitle: 'Busca cualquier cosa', | searchTitle: 'Busca cualquier cosa', | ||||
| someServicesUnavailable: 'Algunos servicios de búsqueda no están disponibles', | someServicesUnavailable: 'Algunos servicios de búsqueda no están disponibles', | ||||
| servicesUnavailableMessage: 'Algunos servicios de búsqueda pueden estar experimentando problemas. Inténtalo de nuevo en un momento.', | servicesUnavailableMessage: 'Algunos servicios de búsqueda pueden estar experimentando problemas. Inténtalo de nuevo en un momento.', | ||||
| searchHint: 'Empieza a escribir para buscar todo al instante', | |||||
| commandHint: 'Escriba @ para buscar por categoría', | |||||
| selectSearchType: 'Elige qué buscar', | |||||
| }, | }, | ||||
| } | } | ||||
| searchTemporarilyUnavailable: 'جستجو به طور موقت در دسترس نیست', | searchTemporarilyUnavailable: 'جستجو به طور موقت در دسترس نیست', | ||||
| servicesUnavailableMessage: 'برخی از سرویس های جستجو ممکن است با مشکل مواجه شوند. یک لحظه دیگر دوباره امتحان کنید.', | servicesUnavailableMessage: 'برخی از سرویس های جستجو ممکن است با مشکل مواجه شوند. یک لحظه دیگر دوباره امتحان کنید.', | ||||
| someServicesUnavailable: 'برخی از سرویس های جستجو دردسترس نیستند', | someServicesUnavailable: 'برخی از سرویس های جستجو دردسترس نیستند', | ||||
| selectSearchType: 'انتخاب کنید چه چیزی را جستجو کنید', | |||||
| commandHint: '@ را برای مرور بر اساس دسته بندی تایپ کنید', | |||||
| searchHint: 'شروع به تایپ کنید تا فورا همه چیز را جستجو کنید', | |||||
| }, | }, | ||||
| } | } | ||||
| searchPlaceholder: 'Recherchez ou tapez @ pour les commandes...', | searchPlaceholder: 'Recherchez ou tapez @ pour les commandes...', | ||||
| searchFailed: 'Echec de la recherche', | searchFailed: 'Echec de la recherche', | ||||
| noResults: 'Aucun résultat trouvé', | noResults: 'Aucun résultat trouvé', | ||||
| commandHint: 'Tapez @ pour parcourir par catégorie', | |||||
| selectSearchType: 'Choisissez les éléments de recherche', | |||||
| searchHint: 'Commencez à taper pour tout rechercher instantanément', | |||||
| }, | }, | ||||
| } | } | ||||
| searchPlaceholder: 'कमांड के लिए खोजें या टाइप करें @...', | searchPlaceholder: 'कमांड के लिए खोजें या टाइप करें @...', | ||||
| searchTemporarilyUnavailable: 'खोज अस्थायी रूप से उपलब्ध नहीं है', | searchTemporarilyUnavailable: 'खोज अस्थायी रूप से उपलब्ध नहीं है', | ||||
| servicesUnavailableMessage: 'कुछ खोज सेवाएँ समस्याओं का सामना कर सकती हैं। थोड़ी देर बाद फिर से प्रयास करें।', | servicesUnavailableMessage: 'कुछ खोज सेवाएँ समस्याओं का सामना कर सकती हैं। थोड़ी देर बाद फिर से प्रयास करें।', | ||||
| commandHint: '@ का उपयोग कर श्रेणी के अनुसार ब्राउज़ करें', | |||||
| selectSearchType: 'खोजने के लिए क्या चुनें', | |||||
| searchHint: 'सब कुछ तुरंत खोजने के लिए टाइप करना शुरू करें', | |||||
| }, | }, | ||||
| } | } | ||||
| noResults: 'Nessun risultato trovato', | noResults: 'Nessun risultato trovato', | ||||
| useAtForSpecific: 'Utilizzare @ per tipi specifici', | useAtForSpecific: 'Utilizzare @ per tipi specifici', | ||||
| clearToSearchAll: 'Cancella @ per cercare tutto', | clearToSearchAll: 'Cancella @ per cercare tutto', | ||||
| selectSearchType: 'Scegli cosa cercare', | |||||
| commandHint: 'Digita @ per sfogliare per categoria', | |||||
| searchHint: 'Inizia a digitare per cercare tutto all\'istante', | |||||
| }, | }, | ||||
| } | } | ||||
| inScope: '{{scope}}s 内', | inScope: '{{scope}}s 内', | ||||
| clearToSearchAll: '@ をクリアしてすべてを検索', | clearToSearchAll: '@ をクリアしてすべてを検索', | ||||
| useAtForSpecific: '特定のタイプには @ を使用', | useAtForSpecific: '特定のタイプには @ を使用', | ||||
| selectSearchType: '検索対象を選択', | |||||
| searchHint: '入力を開始してすべてを瞬時に検索', | |||||
| commandHint: '@ を入力してカテゴリ別に参照', | |||||
| actions: { | actions: { | ||||
| searchApplications: 'アプリケーションを検索', | searchApplications: 'アプリケーションを検索', | ||||
| searchApplicationsDesc: 'アプリケーションを検索してナビゲート', | searchApplicationsDesc: 'アプリケーションを検索してナビゲート', |
| searchFailed: '검색 실패', | searchFailed: '검색 실패', | ||||
| searchPlaceholder: '명령을 검색하거나 @를 입력합니다...', | searchPlaceholder: '명령을 검색하거나 @를 입력합니다...', | ||||
| clearToSearchAll: '@를 지우면 모두 검색됩니다.', | clearToSearchAll: '@를 지우면 모두 검색됩니다.', | ||||
| selectSearchType: '검색할 항목 선택', | |||||
| commandHint: '@를 입력하여 카테고리별로 찾아봅니다.', | |||||
| searchHint: '즉시 모든 것을 검색하려면 입력을 시작하세요.', | |||||
| }, | }, | ||||
| } | } | ||||
| searchTemporarilyUnavailable: 'Wyszukiwanie chwilowo niedostępne', | searchTemporarilyUnavailable: 'Wyszukiwanie chwilowo niedostępne', | ||||
| servicesUnavailableMessage: 'W przypadku niektórych usług wyszukiwania mogą występować problemy. Spróbuj ponownie za chwilę.', | servicesUnavailableMessage: 'W przypadku niektórych usług wyszukiwania mogą występować problemy. Spróbuj ponownie za chwilę.', | ||||
| searchFailed: 'Wyszukiwanie nie powiodło się', | searchFailed: 'Wyszukiwanie nie powiodło się', | ||||
| searchHint: 'Zacznij pisać, aby natychmiast wszystko przeszukać', | |||||
| commandHint: 'Wpisz @, aby przeglądać według kategorii', | |||||
| selectSearchType: 'Wybierz, czego chcesz szukać', | |||||
| }, | }, | ||||
| } | } | ||||
| useAtForSpecific: 'Use @ para tipos específicos', | useAtForSpecific: 'Use @ para tipos específicos', | ||||
| clearToSearchAll: 'Desmarque @ para pesquisar tudo', | clearToSearchAll: 'Desmarque @ para pesquisar tudo', | ||||
| searchFailed: 'Falha na pesquisa', | searchFailed: 'Falha na pesquisa', | ||||
| searchHint: 'Comece a digitar para pesquisar tudo instantaneamente', | |||||
| commandHint: 'Digite @ para navegar por categoria', | |||||
| selectSearchType: 'Escolha o que pesquisar', | |||||
| }, | }, | ||||
| } | } | ||||
| servicesUnavailableMessage: 'Este posibil ca unele servicii de căutare să întâmpine probleme. Încercați din nou într-o clipă.', | servicesUnavailableMessage: 'Este posibil ca unele servicii de căutare să întâmpine probleme. Încercați din nou într-o clipă.', | ||||
| someServicesUnavailable: 'Unele servicii de căutare nu sunt disponibile', | someServicesUnavailable: 'Unele servicii de căutare nu sunt disponibile', | ||||
| clearToSearchAll: 'Ștergeți @ pentru a căuta toate', | clearToSearchAll: 'Ștergeți @ pentru a căuta toate', | ||||
| selectSearchType: 'Alegeți ce să căutați', | |||||
| commandHint: 'Tastați @ pentru a naviga după categorie', | |||||
| searchHint: 'Începeți să tastați pentru a căuta totul instantaneu', | |||||
| }, | }, | ||||
| } | } | ||||
| searchPlaceholder: 'Найдите или введите @ для команд...', | searchPlaceholder: 'Найдите или введите @ для команд...', | ||||
| someServicesUnavailable: 'Некоторые поисковые сервисы недоступны', | someServicesUnavailable: 'Некоторые поисковые сервисы недоступны', | ||||
| servicesUnavailableMessage: 'В некоторых поисковых службах могут возникать проблемы. Повторите попытку через мгновение.', | servicesUnavailableMessage: 'В некоторых поисковых службах могут возникать проблемы. Повторите попытку через мгновение.', | ||||
| searchHint: 'Начните печатать, чтобы мгновенно искать все', | |||||
| commandHint: 'Введите @ для просмотра по категориям', | |||||
| selectSearchType: 'Выберите, что искать', | |||||
| }, | }, | ||||
| } | } | ||||
| searchFailed: 'Iskanje ni uspelo', | searchFailed: 'Iskanje ni uspelo', | ||||
| useAtForSpecific: 'Uporaba znaka @ za določene vrste', | useAtForSpecific: 'Uporaba znaka @ za določene vrste', | ||||
| servicesUnavailableMessage: 'Pri nekaterih iskalnih storitvah se morda pojavljajo težave. Poskusite znova čez trenutek.', | servicesUnavailableMessage: 'Pri nekaterih iskalnih storitvah se morda pojavljajo težave. Poskusite znova čez trenutek.', | ||||
| commandHint: 'Vnesite @ za brskanje po kategoriji', | |||||
| selectSearchType: 'Izberite, kaj želite iskati', | |||||
| searchHint: 'Začnite tipkati, da takoj preiščete vse', | |||||
| }, | }, | ||||
| } | } | ||||
| searchPlaceholder: 'ค้นหาหรือพิมพ์ @ สําหรับคําสั่ง...', | searchPlaceholder: 'ค้นหาหรือพิมพ์ @ สําหรับคําสั่ง...', | ||||
| servicesUnavailableMessage: 'บริการค้นหาบางบริการอาจประสบปัญหา ลองอีกครั้งในอีกสักครู่', | servicesUnavailableMessage: 'บริการค้นหาบางบริการอาจประสบปัญหา ลองอีกครั้งในอีกสักครู่', | ||||
| searching: 'กำลังค้นหา...', | searching: 'กำลังค้นหา...', | ||||
| searchHint: 'เริ่มพิมพ์เพื่อค้นหาทุกอย่างได้ทันที', | |||||
| selectSearchType: 'เลือกสิ่งที่จะค้นหา', | |||||
| commandHint: 'พิมพ์ @ เพื่อเรียกดูตามหมวดหมู่', | |||||
| }, | }, | ||||
| } | } | ||||
| noResults: 'Sonuç bulunamadı', | noResults: 'Sonuç bulunamadı', | ||||
| servicesUnavailableMessage: 'Bazı arama hizmetlerinde sorunlar yaşanıyor olabilir. Kısa bir süre sonra tekrar deneyin.', | servicesUnavailableMessage: 'Bazı arama hizmetlerinde sorunlar yaşanıyor olabilir. Kısa bir süre sonra tekrar deneyin.', | ||||
| searching: 'Araştırıcı...', | searching: 'Araştırıcı...', | ||||
| selectSearchType: 'Ne arayacağınızı seçin', | |||||
| searchHint: 'Her şeyi anında aramak için yazmaya başlayın', | |||||
| commandHint: 'Kategoriye göre göz atmak için @ yazın', | |||||
| }, | }, | ||||
| } | } | ||||
| useAtForSpecific: 'Використовуйте @ для конкретних типів', | useAtForSpecific: 'Використовуйте @ для конкретних типів', | ||||
| someServicesUnavailable: 'Деякі пошукові сервіси недоступні', | someServicesUnavailable: 'Деякі пошукові сервіси недоступні', | ||||
| servicesUnavailableMessage: 'У деяких пошукових службах можуть виникати проблеми. Повторіть спробу за мить.', | servicesUnavailableMessage: 'У деяких пошукових службах можуть виникати проблеми. Повторіть спробу за мить.', | ||||
| selectSearchType: 'Виберіть, що шукати', | |||||
| commandHint: 'Введіть @ для навігації за категоріями', | |||||
| searchHint: 'Почніть вводити текст, щоб миттєво шукати все', | |||||
| }, | }, | ||||
| } | } | ||||
| useAtForSpecific: 'Sử dụng @ cho các loại cụ thể', | useAtForSpecific: 'Sử dụng @ cho các loại cụ thể', | ||||
| someServicesUnavailable: 'Một số dịch vụ tìm kiếm không khả dụng', | someServicesUnavailable: 'Một số dịch vụ tìm kiếm không khả dụng', | ||||
| servicesUnavailableMessage: 'Một số dịch vụ tìm kiếm có thể gặp sự cố. Thử lại trong giây lát.', | servicesUnavailableMessage: 'Một số dịch vụ tìm kiếm có thể gặp sự cố. Thử lại trong giây lát.', | ||||
| searchHint: 'Bắt đầu nhập để tìm kiếm mọi thứ ngay lập tức', | |||||
| commandHint: 'Nhập @ để duyệt theo danh mục', | |||||
| selectSearchType: 'Chọn nội dung để tìm kiếm', | |||||
| }, | }, | ||||
| } | } | ||||
| inScope: '在 {{scope}}s 中', | inScope: '在 {{scope}}s 中', | ||||
| clearToSearchAll: '清除 @ 以搜索全部', | clearToSearchAll: '清除 @ 以搜索全部', | ||||
| useAtForSpecific: '使用 @ 进行特定类型搜索', | useAtForSpecific: '使用 @ 进行特定类型搜索', | ||||
| selectSearchType: '选择搜索内容', | |||||
| searchHint: '开始输入即可立即搜索所有内容', | |||||
| commandHint: '输入 @ 按类别浏览', | |||||
| actions: { | actions: { | ||||
| searchApplications: '搜索应用程序', | searchApplications: '搜索应用程序', | ||||
| searchApplicationsDesc: '搜索并导航到您的应用程序', | searchApplicationsDesc: '搜索并导航到您的应用程序', |
| someServicesUnavailable: '某些搜索服務不可用', | someServicesUnavailable: '某些搜索服務不可用', | ||||
| useAtForSpecific: '對特定類型使用 @', | useAtForSpecific: '對特定類型使用 @', | ||||
| searchTemporarilyUnavailable: '搜索暫時不可用', | searchTemporarilyUnavailable: '搜索暫時不可用', | ||||
| selectSearchType: '選擇要搜索的內容', | |||||
| commandHint: '鍵入 @ 按類別流覽', | |||||
| searchHint: '開始輸入以立即搜索所有內容', | |||||
| }, | }, | ||||
| } | } | ||||