"use client";
import { LayoutGroup, motion, useReducedMotion } from "motion/react";
import { type ReactNode, useId } from "react";
import { SPRING_PANEL } from "@/lib/ease";
import { cn } from "@/lib/utils";
export interface ThreadListProps {
className?: string;
children?: ReactNode;
}
/**
* Sidebar conversation/task list root — a tight `flex flex-col` stack of
* `ThreadListSection`s. Wraps `children` in a `LayoutGroup` scoped to this
* instance (via `useId`), so the active-row indicator's `layoutId="active"`
* animation (see `ThreadListItem`) stays local to this list — two
* `ThreadList`s rendered on the same page never fight over which one owns
* the moving highlight.
*/
export function ThreadList({ className, children }: ThreadListProps) {
const groupId = useId();
return (
{children}
);
}
export interface ThreadListSectionProps {
/** Muted section label, e.g. "Pinned" or "Recent". */
title?: ReactNode;
/** Trailing slot beside the title, e.g. a "new" `ThreadListAction`. */
action?: ReactNode;
className?: string;
children?: ReactNode;
}
/**
* Named group of rows inside a `ThreadList`. The header row (label plus an
* optional trailing action) only renders when `title` or `action` is
* passed, so a section can also be used as a bare, unlabeled row group.
*/
export function ThreadListSection({ title, action, className, children }: ThreadListSectionProps) {
return (
{title !== undefined || action !== undefined ? (
{title}
{action}
) : null}
{children}
);
}
export interface ThreadListItemProps {
/** Highlights the row and pins the moving indicator here. */
active?: boolean;
/** Renders a small blue dot after the title. */
unread?: boolean;
/** 16px leading icon, e.g. ``. */
icon?: ReactNode;
/** Trailing meta, e.g. a relative timestamp. Hidden on row hover in favor of `actions` when both are passed; stays visible when `actions` is omitted. */
meta?: ReactNode;
/** Row of `ThreadListAction`s revealed on hover, replacing `meta`. */
actions?: ReactNode;
onSelect?: () => void;
className?: string;
/** The row's title — truncates to a single line. */
children?: ReactNode;
}
/**
* One row in a `ThreadList` — a conversation or task entry. The signature
* motion is the active indicator: a `layoutId="active"` pill that glides
* between rows as selection moves (`SPRING_PANEL`), scoped to the enclosing
* `ThreadList`'s `LayoutGroup` so unrelated lists sharing a page don't
* collide. Reduced motion swaps the animated pill for a static span.
*
* Deliberately *not* a single `