import type { Accessor, ParentComponent } from 'solid-js'; import type { Document } from '../documents/documents.types'; import { safely } from '@corentinth/chisels'; import { useNavigate, useParams } from '@solidjs/router'; import { createContext, createEffect, createSignal, For, on, onCleanup, onMount, Show, useContext } from 'solid-js'; import { getDocumentIcon } from '../documents/document.models'; import { searchDocuments } from '../documents/documents.services'; import { useI18n } from '../i18n/i18n.provider'; import { cn } from '../shared/style/cn'; import { debounce } from '../shared/utils/timing'; import { useThemeStore } from '../theme/theme.store'; import { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandLoading } from '../ui/components/command'; const CommandPaletteContext = createContext<{ getIsCommandPaletteOpen: Accessor; openCommandPalette: () => void; closeCommandPalette: () => void; }>(); export function useCommandPalette() { const context = useContext(CommandPaletteContext); if (!context) { throw new Error('CommandPalette context not found'); } return context; } export const CommandPaletteProvider: ParentComponent = (props) => { const [getIsCommandPaletteOpen, setIsCommandPaletteOpen] = createSignal(false); const [getMatchingDocuments, setMatchingDocuments] = createSignal([]); const [getSearchQuery, setSearchQuery] = createSignal(''); const [getIsLoading, setIsLoading] = createSignal(false); const params = useParams(); const { t } = useI18n(); const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'k' && (e.metaKey || e.ctrlKey)) { e.preventDefault(); setIsCommandPaletteOpen(true); } }; onMount(() => { document.addEventListener('keydown', handleKeyDown); }); onCleanup(() => { document.removeEventListener('keydown', handleKeyDown); }); const navigate = useNavigate(); const { setColorMode } = useThemeStore(); const searchDocs = debounce(async ({ searchQuery }: { searchQuery: string }) => { const [result] = await safely(searchDocuments({ searchQuery, organizationId: params.organizationId, pageIndex: 0, pageSize: 5 })); const { documents = [] } = result ?? {}; setMatchingDocuments(documents); setIsLoading(false); }, 300); createEffect(on( getSearchQuery, (searchQuery) => { setMatchingDocuments([]); if (searchQuery.length > 1) { setIsLoading(true); searchDocs({ searchQuery }); } }, )); createEffect(on( getIsCommandPaletteOpen, (isCommandPaletteOpen) => { if (isCommandPaletteOpen) { setMatchingDocuments([]); } }, )); const getCommandData = (): { label: string; forceMatch?: boolean; options: { label: string; icon: string; action: () => void; forceMatch?: boolean }[]; }[] => [ { label: t('command-palette.sections.documents'), forceMatch: true, options: getMatchingDocuments().map(document => ({ label: document.name, icon: getDocumentIcon({ document }), action: () => navigate(`/organizations/${params.organizationId}/documents/${document.id}`), forceMatch: true, })), }, { label: t('command-palette.sections.theme'), options: [ { label: t('layout.theme.light'), icon: 'i-tabler-sun', action: () => setColorMode({ mode: 'light' }), }, { label: t('layout.theme.dark'), icon: 'i-tabler-moon', action: () => setColorMode({ mode: 'dark' }), }, { label: t('layout.theme.system'), icon: 'i-tabler-device-laptop', action: () => setColorMode({ mode: 'system' }), }, ], }, ]; const onCommandSelect = ({ action }: { action: () => void }) => { action(); setIsCommandPaletteOpen(false); }; return ( setIsCommandPaletteOpen(true), closeCommandPalette: () => setIsCommandPaletteOpen(false), }} >
{t('command-palette.no-results')} section.options.length > 0)}> {section => ( {item => ( onCommandSelect(item)} forceMount={item.forceMatch ?? false}> {item.label} )} )}
{props.children}
); };