import type { Ref } from 'vue' import { homeCommands } from './commands' interface CmdAction { id: string title: string hotkey?: string parent?: string handler?: Function icon?: VNode | string keywords?: string[] projectName?: string section?: string } export const useCommandPalette = createSharedComposable(() => { const { $api } = useNuxtApp() const router = useRouter() const route = router.currentRoute const commandPalette = ref() const refreshCommandPalette = createEventHook() const activeScope: Ref<{ scope: string; data: any }> = ref({ scope: 'disabled', data: {} }) const cmdLoading = ref(false) const cmdPlaceholder = ref('Search...') const { token, user, signOut } = useGlobal() const commands = ref({ homeCommands, baseCommands: [], } as Record) const staticData = computed(() => { const staticCmd = commands.value.homeCommands // Static Commands staticCmd.map((cmd) => { if (cmd.id === 'user') { if (user.value && user.value.display_name && user.value.email) { cmd.title = user.value.display_name ?? user.value.email.split('@')[0] ?? 'User' } } else if (cmd.id === 'user_account-logout') { cmd.handler = async () => { await signOut() window.location.reload() } } return cmd }) if (activeScope.value.scope === 'root') return staticCmd staticCmd.push(...commands.value.baseCommands) return staticCmd }) const dynamicData = ref([]) const tempData = ref([]) const loadedTemporaryScopes = ref([]) const cmdData = computed(() => { if (cmdLoading.value) { return [{ id: 'loading', title: 'Loading...' }, ...staticData.value] } else { return [...dynamicData.value, ...staticData.value, ...tempData.value] } }) function processHandler(handler: { type: string; payload: string }) { switch (handler.type) { case 'navigate': return () => navigateTo(handler.payload) default: break } } async function loadTemporaryScope(scope: { scope: string; data: any }) { if (loadedTemporaryScopes.value.find((s: any) => s.scope === scope.scope)) return if ( activeScope.value.scope === scope.scope && Object.keys(scope.data).every((k) => activeScope.value.data[k] && scope.data[k] === activeScope.value.data[k]) ) return $api.utils.commandPalette(scope).then((res) => { const fetchData = res.map((item: any) => { if (item.handler) item.handler = processHandler(item.handler) return item }) for (const d of fetchData) { const fnd = tempData.value.find((t: any) => t.id === d.id) if (fnd) { Object.assign(fnd, d) } else { tempData.value.push(d) } } loadedTemporaryScopes.value.push(scope) }) } async function loadScope() { if (activeScope.value.scope === 'disabled') { activeScope.value = { scope: activeScope.value.scope, data: activeScope.value.data } return } dynamicData.value = [] tempData.value = [] loadedTemporaryScopes.value = [] cmdLoading.value = true $api.utils .commandPalette(activeScope.value) .then((res) => { dynamicData.value = res.map((item: any) => { if (item.handler) item.handler = processHandler(item.handler) return item }) cmdLoading.value = false }) .catch((e) => { cmdLoading.value = false console.log(e) }) } refreshCommandPalette.on(() => { loadScope() }) watch( () => route.value.params, () => { // if user is not authenticated, don't load scope if (!token.value) return if (route.value.params.typeOrId && typeof route.value.params.typeOrId === 'string') { if (route.value.params.typeOrId === 'base') { if (activeScope.value.scope === 'disabled') return activeScope.value = { scope: 'disabled', data: {} } loadScope() } else if (route.value.params.typeOrId.startsWith('w')) { if (activeScope.value.data?.workspace_id === route.value.params.typeOrId) return activeScope.value = { scope: `ws-${route.value.params.typeOrId}`, data: { workspace_id: route.value.params.typeOrId }, } loadScope() } else if (route.value.params.typeOrId === 'nc') { if (activeScope.value.data.base_id === route.value.params.baseId) return activeScope.value = { scope: `p-${route.value.params.baseId}`, data: { base_id: route.value.params.baseId }, } } } else { if (route.value.path.startsWith('/account')) { if (activeScope.value.scope === 'account_settings') return activeScope.value = { scope: 'account_settings', data: {} } loadScope() } else { if (activeScope.value.scope === 'root') return activeScope.value = { scope: 'root', data: {} } loadScope() } } }, { immediate: true, deep: true }, ) return { commandPalette, cmdData, activeScope, loadScope, cmdPlaceholder, refreshCommandPalette: refreshCommandPalette.trigger, loadTemporaryScope, } })