"use client";
import { useRouter } from "next/navigation";
import { useEffect, useState, useSyncExternalStore } from "react";
import { CommandPalette, SearchField, type CommandPaletteItem } from "@unlocalhosted/metalui";
import { CompassIcon, GearIcon, HomeIcon, KeyIcon, PlusIcon, SearchIcon, ShieldLockIcon } from "./icons";
import { RepoAvatar } from "./ui";
const noSubscribe = () => () => {};
const isApple = () => /Mac|iPhone|iPad/.test(navigator.platform);
export type PaletteRepo = { owner: string; name: string; imageVersion: number | null };
/**
* The header's search well (MetalUI's SearchField) and the ⌘K palette it opens: jump to a repository,
* go to a place, or search every repository for the words typed. ⌘K / Ctrl+K or "/" opens it anywhere.
*/
export function SearchBox({ repos = [], signedIn = false }: { repos?: PaletteRepo[]; signedIn?: boolean }) {
const router = useRouter();
const apple = useSyncExternalStore(noSubscribe, isApple, () => true);
const [open, setOpen] = useState(false);
const [query, setQuery] = useState("");
useEffect(() => {
const onKey = (e: KeyboardEvent) => {
const target = e.target as HTMLElement;
const typing = target.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(target.tagName);
const shortcut = (e.key === "k" && (e.metaKey || e.ctrlKey)) || (e.key === "/" && !typing);
if (!shortcut) return;
e.preventDefault();
setOpen(true);
};
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, []);
const q = query.trim();
const items: CommandPaletteItem[] = [
...(q ? [{ id: `search:${q}`, section: "SEARCH", label: `Search repositories for “${q}”`, icon: , hint: "EXPLORE" }] : []),
...repos.map((r) => ({
id: `repo:${r.owner}/${r.name}`,
section: "REPOSITORIES",
label: `${r.owner}/${r.name}`,
icon: ,
})),
{ id: "go:/", section: "PLACES", label: "Overview", icon: },
{ id: "go:/explore", section: "PLACES", label: "Explore", icon: , keywords: ["browse", "discover"] },
...(signedIn
? [
{ id: "go:/new", section: "ACTIONS", label: "New Repository", icon: , keywords: ["create"] },
{ id: "go:/settings/profile", section: "ACTIONS", label: "Settings", icon: , keywords: ["profile", "account"] },
{ id: "go:/settings/keys", section: "ACTIONS", label: "SSH Keys", icon: },
{ id: "go:/settings/tokens", section: "ACTIONS", label: "API Tokens", icon: },
]
: []),
];
return (
<>
}
shortcut={apple ? "⌘K" : "Ctrl K"}
aria-keyshortcuts={apple ? "Meta+K" : "Control+K"}
tone="light"
onClick={() => setOpen(true)}
className="hidden md:flex"
/>
{
setOpen(next);
if (!next) setQuery("");
}}
query={query}
onQueryChange={setQuery}
items={items}
icon={}
placeholder="Repository, place or words to search"
status={signedIn ? "YOUR REPOSITORIES" : "PUBLIC REPOSITORIES"}
empty="Nothing matches"
aria-label="Search"
onRun={(item) => {
const [kind, ...rest] = item.id.split(":");
const value = rest.join(":");
if (kind === "search") router.push(`/explore?q=${encodeURIComponent(value)}`);
else if (kind === "repo") router.push(`/${value}`);
else router.push(value);
}}
/>
>
);
}