diff --git a/src-web/components/CommandPalette.tsx b/src-web/components/CommandPalette.tsx index c62229e9..a25e8e2d 100644 --- a/src-web/components/CommandPalette.tsx +++ b/src-web/components/CommandPalette.tsx @@ -1,22 +1,72 @@ import classNames from 'classnames'; import type { ReactNode } from 'react'; -import { useCallback, useState } from 'react'; +import { useMemo, useCallback, useState } from 'react'; +import { useActiveEnvironmentId } from '../hooks/useActiveEnvironmentId'; +import { useAppRoutes } from '../hooks/useAppRoutes'; +import { getRecentEnvironments } from '../hooks/useRecentEnvironments'; import { useRequests } from '../hooks/useRequests'; import { useWorkspaces } from '../hooks/useWorkspaces'; import { fallbackRequestName } from '../lib/fallbackRequestName'; import { Input } from './core/Input'; -export function CommandPalette() { +export function CommandPalette({ onClose }: { onClose: () => void }) { const [selectedIndex, setSelectedIndex] = useState(0); + const routes = useAppRoutes(); + const activeEnvironmentId = useActiveEnvironmentId(); const workspaces = useWorkspaces(); const requests = useRequests(); - const handleKeyDown = useCallback((e: KeyboardEvent) => { - if (e.key === 'ArrowDown') { - setSelectedIndex((prev) => prev + 1); - } else if (e.key === 'ArrowUp') { - setSelectedIndex((prev) => prev - 1); + + const items = useMemo<{ label: string; onSelect: () => void; key: string }[]>(() => { + const items = []; + for (const r of requests) { + items.push({ + key: `switch-request-${r.id}`, + label: `Switch Request → ${fallbackRequestName(r)}`, + onSelect: () => { + return routes.navigate('request', { + workspaceId: r.workspaceId, + requestId: r.id, + environmentId: activeEnvironmentId ?? undefined, + }); + }, + }); } - }, []); + for (const w of workspaces) { + items.push({ + key: `switch-workspace-${w.id}`, + label: `Switch Workspace → ${w.name}`, + onSelect: async () => { + const environmentId = (await getRecentEnvironments(w.id))[0]; + return routes.navigate('workspace', { + workspaceId: w.id, + environmentId, + }); + }, + }); + } + return items; + }, [activeEnvironmentId, requests, routes, workspaces]); + + const handleSelectAndClose = (cb: () => void) => { + onClose(); + cb(); + }; + + const handleKeyDown = useCallback( + (e: KeyboardEvent) => { + if (e.key === 'ArrowDown') { + setSelectedIndex((prev) => prev + 1); + } else if (e.key === 'ArrowUp') { + setSelectedIndex((prev) => prev - 1); + } else if (e.key === 'Enter') { + const item = items[selectedIndex]; + if (item) { + handleSelectAndClose(item.onSelect); + } + } + }, + [items, onClose, selectedIndex], + ); return (
@@ -30,14 +80,13 @@ export function CommandPalette() { />
- {requests.map((r, i) => ( - - Switch Request → {fallbackRequestName(r)} - - ))} - {workspaces.map((w, i) => ( - - Switch Workspace → {w.name} + {items.map((v, i) => ( + handleSelectAndClose(v.onSelect)} + > + {v.label} ))}
@@ -45,15 +94,24 @@ export function CommandPalette() { ); } -function CommandPaletteItem({ children, active }: { children: ReactNode; active: boolean }) { +function CommandPaletteItem({ + children, + active, + onClick, +}: { + children: ReactNode; + active: boolean; + onClick: () => void; +}) { return ( -
{children} -
+ ); } diff --git a/src-web/hooks/useCommandPalette.tsx b/src-web/hooks/useCommandPalette.tsx index 50960cb7..432e322a 100644 --- a/src-web/hooks/useCommandPalette.tsx +++ b/src-web/hooks/useCommandPalette.tsx @@ -18,7 +18,7 @@ export function useCommandPalette() { hideX: true, noPadding: true, noScroll: true, - render: () => , + render: ({ hide }) => , }); }); }