Desktop Spotlight

import { Spotlight } from '@hanzo/ui'

Application launcher

A button opens the spotlight; type to filter, Enter or a click runs the highlighted app.

export function Default() {
const [isOpen, setIsOpen] = useState(false)
const [picked, setPicked] = useState<string | null>(null)
return (
<YStack gap="$3" items="flex-start">
<Button onPress={() => setIsOpen(true)}>Open Spotlight</Button>
{picked ? <SizableText size="$2" color="$quiet">Last picked: {picked}</SizableText> : null}
<Spotlight isOpen={isOpen} onClose={() => setIsOpen(false)} items={appItems} onSelect={(item) => setPicked(item.title)} />
</YStack>
)
}

Search with subtitles

Each row carries a secondary line, useful for records that need more than a title to tell apart.

export function WithSubtitles() {
const [isOpen, setIsOpen] = useState(false)
return (
<YStack gap="$3" items="flex-start">
<Button variant="outline" onPress={() => setIsOpen(true)}>
Search records
</Button>
<Spotlight isOpen={isOpen} onClose={() => setIsOpen(false)} items={withSubtitles} placeholder="Search invoices and contacts..." />
</YStack>
)
}

Command runner

Actions and recent files share the list under separate category headings; picking an action logs it instead of opening anything.

export function CommandRunner() {
const [isOpen, setIsOpen] = useState(false)
const [log, setLog] = useState<string[]>([])
return (
<YStack gap="$3" items="flex-start">
<Button onPress={() => setIsOpen(true)}>⌘K</Button>
<XStack gap="$2" flexWrap="wrap">
{log.map((entry, i) => (
<SizableText key={i} size="$1" color="$quiet">
{entry}
</SizableText>
))}
</XStack>
<Spotlight isOpen={isOpen} onClose={() => setIsOpen(false)} items={commandItems} placeholder="Type a command or search files..." onSelect={(item) => setLog((prev) => [...prev, `Ran: ${item.title}`])} />
</YStack>
)
}

Types

export interface SpotlightItem {
id: string
title: string
subtitle?: string
icon?: ReactNode
category?: string
keywords?: string[]
action?: () => void
}
export interface SpotlightProps {
/** Controls visibility of the spotlight dialog. */
isOpen: boolean
/** Called when the spotlight is closed — Escape, backdrop, or a selection. */
onClose: () => void
/** The searchable items, in the order groups should fall when unfiltered. */
items?: SpotlightItem[]
/** Called with the picked item, after its own `action` (if any) runs. */
onSelect?: (item: SpotlightItem) => void
/** Search input placeholder text. */
placeholder?: string
}

Exports

Spotlighttype SpotlightItemtype SpotlightProps