keyboard-driven navigation

Resolves #66
This commit is contained in:
talksik
2026-03-30 12:51:15 -07:00
parent 14285575e4
commit 086cf4f1ee
7 changed files with 188 additions and 40 deletions
@@ -77,10 +77,14 @@ function StreamRow({
particle,
networkId,
onClick,
isSelected,
shortcutKey,
}: {
particle: Particle & { type: "stream"; properties: StreamProperties };
networkId: string;
onClick: () => void;
isSelected?: boolean;
shortcutKey?: number;
}) {
const streamPath = particlePath(networkId, [particle.id]);
const { latestChild } = useLiveLatestChild(streamPath);
@@ -148,8 +152,16 @@ function StreamRow({
tabIndex={0}
onClick={onClick}
onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") onClick(); }}
className="flex w-full items-center gap-3 px-4 py-3 text-left cursor-pointer transition-colors hover:bg-accent"
className={cn(
"flex w-full items-center gap-3 px-4 py-3 text-left cursor-pointer transition-colors hover:bg-accent",
isSelected && "bg-accent",
)}
>
{shortcutKey && (
<kbd className="flex size-5 shrink-0 items-center justify-center rounded bg-white/10 font-mono text-xs text-muted-foreground">
{shortcutKey}
</kbd>
)}
<Avatar
className={cn(isUnseen && "ring-2 ring-primary")}
>
@@ -211,12 +223,13 @@ function StreamRow({
interface ParticleListViewProps {
path: ParticlePath;
selectedIndex?: number | null;
}
/**
* List of stream particles for a container (network root, folder, etc.).
*/
export function ParticleListView({ path }: ParticleListViewProps) {
export function ParticleListView({ path, selectedIndex }: ParticleListViewProps) {
const { streams, isLoading, networkId } = useStreamParticles(path);
const navigate = useNavigate();
@@ -238,13 +251,18 @@ export function ParticleListView({ path }: ParticleListViewProps) {
return (
<div>
{streams.map((stream, index) => (
<div key={stream.id}>
<div
key={stream.id}
ref={index === selectedIndex ? (el) => el?.scrollIntoView({ block: "nearest" }) : undefined}
>
<StreamRow
particle={stream}
networkId={networkId}
onClick={() => navigate(`/${networkId}/${stream.id}`)}
isSelected={index === selectedIndex}
shortcutKey={index < 9 ? index + 1 : undefined}
/>
{index < streams.length - 1 && <Separator className="mx-4" />}
{index < streams.length - 1 && <Separator className="px-4" />}
</div>
))}
</div>