"use client";
import Image from "next/image";
import type { StoreAppSummary } from "@/lib/shared/contracts/apps";
import type { RecentCommandAction } from "@/lib/desktop/recent-actions";
import {
CommandDialog,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
CommandShortcut,
} from "@/components/ui/command";
import {
Clock,
FolderOpen,
Search,
Settings,
ShoppingBag,
Sparkles,
TerminalSquare,
} from "@/components/icons/platform-icons";
import { useDebouncedValue } from "@/hooks/useDebouncedValue";
import { useStoreCatalog } from "@/modules/apps/hooks/useStoreCatalog";
export type CommandPaletteSettingsSection = {
id: string;
label: string;
};
export type CommandPaletteRecentAction = RecentCommandAction;
type CommandPaletteProps = {
open: boolean;
onOpenChange: (open: boolean) => void;
query: string;
onQueryChange: (query: string) => void;
settingsSections: readonly CommandPaletteSettingsSection[];
recentActions: readonly CommandPaletteRecentAction[];
onOpenSettingsSection: (sectionId: string) => void;
onOpenAppStoreSearch: (input: { search: string; appId?: string }) => void;
onOpenWindow: (windowId: "files" | "settings" | "app-store" | "terminal") => void;
onSelectRecentAction: (action: CommandPaletteRecentAction) => void;
};
function StoreSearchIcon({ app }: { app: StoreAppSummary }) {
if (app.logoUrl) {
return (
);
}
return (
);
}
export function CommandPalette({
open,
onOpenChange,
query,
onQueryChange,
settingsSections,
recentActions,
onOpenSettingsSection,
onOpenAppStoreSearch,
onOpenWindow,
onSelectRecentAction,
}: CommandPaletteProps) {
const normalizedQuery = query.trim();
const debouncedQuery = useDebouncedValue(normalizedQuery, 180);
const matchingSettings = normalizedQuery
? settingsSections.filter((section) =>
section.label.toLowerCase().includes(normalizedQuery.toLowerCase()),
)
: settingsSections.slice(0, 5);
const storeCatalogQuery = useStoreCatalog({
search: debouncedQuery || undefined,
});
const appResults = (storeCatalogQuery.data?.apps ?? [])
.slice(0, normalizedQuery ? 7 : 5);
const filteredRecentActions = recentActions.filter((action) => {
if (!normalizedQuery) return true;
const haystack = `${action.title} ${action.subtitle}`.toLowerCase();
return haystack.includes(normalizedQuery.toLowerCase());
});
function renderRecentActionIcon(action: CommandPaletteRecentAction) {
if (action.kind === "window") {
if (action.windowId === "files") return ;
if (action.windowId === "settings") return ;
if (action.windowId === "app-store") {
return ;
}
return ;
}
if (action.kind === "settings-section") {
return ;
}
return ;
}
return (
{/* Hero header — matches lock screen / login design language */}
No matches found
Try a settings section, app name, or desktop tool.
{filteredRecentActions.length > 0 ? (
<>
{filteredRecentActions.map((action) => (
onSelectRecentAction(action)}
>
{renderRecentActionIcon(action)}
{action.title}
{action.subtitle}
Recent
))}
>
) : null}
onOpenWindow("settings")}>
Open Settings
System
onOpenWindow("app-store")}>
Open App Store
Apps
onOpenWindow("files")}>
Open Files
/DATA
onOpenWindow("terminal")}>
Open Terminal
Shell
{matchingSettings.map((section) => (
onOpenSettingsSection(section.id)}
>
{section.label}
Settings
))}
{storeCatalogQuery.isLoading && normalizedQuery ? (
Searching apps...
) : null}
{!storeCatalogQuery.isLoading && appResults.length === 0 && normalizedQuery ? (
No App Store apps match “{normalizedQuery}”.
) : null}
{appResults.map((app) => (
onOpenAppStoreSearch({
search: app.name,
appId: app.id,
})
}
className="gap-3 rounded-lg py-2.5"
>
{app.name}
{app.updateAvailable ? (
Update
) : null}
{app.description}
App Store
))}
);
}