import {
  memo,
  useCallback,
  useEffect,
  useLayoutEffect,
  useMemo,
  useRef,
  useState,
} from "react";
import {
  Archive,
  ArchiveRestore,
  ChevronDown,
  Folder,
  ListChecks,
  MessageCircleDashed,
  MoreHorizontal,
  MoveRight,
  PanelsTopLeft,
  Pencil,
  Pin,
  PinOff,
  Plus,
  Square,
  SquareCheckBig,
  SquareMinus,
  Trash2,
  Ungroup,
  Unplug,
  X,
} from "lucide-react";
import { useTranslation } from "react-i18next";

import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSub,
  DropdownMenuSubContent,
  DropdownMenuSubTrigger,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { MAX_WORKBENCH_PANES } from "@/components/workbench/workbench-model";
import { SIDEBAR_SELECTION_ITEM_CLASS } from "@/components/SidebarSelectionHighlight";
import { deriveTitle, relativeTime, visibleSessionPreview } from "@/lib/format";
import {
  COLLAPSED_CHATS_VISIBLE_COUNT,
  displayTitle,
  groupSessions,
  isCollapsedProject,
  isFoldableChatsGroup,
  isFoldedChatsGroup,
  limitGroups,
  visibleSessionsForGroup,
  type ChatGroupLabels,
} from "@/lib/chat-groups";
import { deriveTemporaryChatTitle } from "@/lib/temporary-chat";
import { cn } from "@/lib/utils";
import type { ChatSummary, SidebarDensity, SidebarSortMode } from "@/lib/types";

const INITIAL_VISIBLE_SESSIONS = 160;
const VISIBLE_SESSIONS_INCREMENT = 160;
const ACTION_MENU_CONTENT_CLASS = "w-[11rem] min-w-[11rem] whitespace-nowrap";

interface PaneGroupTarget {
  key: string;
  title: string;
  paneCount: number;
  atCapacity: boolean;
}

export interface SidebarPaneGroup {
  tabKey: string;
  title: string;
  activePaneKey: string;
  visible?: boolean;
  panes: Array<{
    key: string;
    chatId: string;
    title: string;
  }>;
}

export interface SidebarDeleteItem {
  key: string;
  label: string;
}

interface ChatListProps {
  sessions: ChatSummary[];
  temporarySessions?: ChatSummary[];
  activeKey: string | null;
  onSelect: (key: string) => void;
  onCloseTemporaryChat?: (key: string) => void;
  onRequestDelete: (key: string, label: string) => void;
  onRequestDeleteMany?: (items: SidebarDeleteItem[]) => void;
  onTogglePin: (key: string) => void;
  onRequestRename: (key: string, label: string) => void;
  onRequestRenameTab?: (key: string, label: string) => void;
  onToggleArchive: (key: string) => void;
  paneGroups?: Record<string, SidebarPaneGroup>;
  onSelectPane?: (tabKey: string, paneKey: string) => void;
  onCreateTab?: (tabKey: string) => void;
  onDetachPane?: (tabKey: string, paneKey: string) => void;
  onDissolveTab?: (tabKey: string) => void;
  onAttachPane?: (
    paneKey: string,
    tabKey: string,
  ) => void;
  onToggleGroup?: (groupId: string) => void;
  onRequestRenameProject?: (projectKey: string, label: string) => void;
  onNewChatInProject?: (projectPath: string, projectName: string) => void;
  pinnedKeys?: string[];
  archivedKeys?: string[];
  pinnedPaneKeys?: string[];
  archivedPaneKeys?: string[];
  sessionOrder?: string[];
  titleOverrides?: Record<string, string>;
  projectNameOverrides?: Record<string, string>;
  collapsedGroups?: Record<string, boolean>;
  runningChatIds?: string[];
  updatedChatIds?: string[];
  density?: SidebarDensity;
  showPreviews?: boolean;
  showTimestamps?: boolean;
  sort?: SidebarSortMode;
  showArchived?: boolean;
  defaultWorkspacePath?: string | null;
  actionMenuPortalContainer?: HTMLElement | null;
  loading?: boolean;
  emptyLabel?: string;
}

export const ChatList = memo(function ChatList({
  sessions,
  temporarySessions = [],
  activeKey,
  onSelect,
  onCloseTemporaryChat,
  onRequestDelete,
  onRequestDeleteMany,
  onTogglePin,
  onRequestRename,
  onRequestRenameTab,
  onToggleArchive,
  paneGroups = {},
  onSelectPane,
  onCreateTab,
  onDetachPane,
  onDissolveTab,
  onAttachPane,
  onToggleGroup,
  onRequestRenameProject,
  onNewChatInProject,
  pinnedKeys = [],
  archivedKeys = [],
  pinnedPaneKeys = [],
  archivedPaneKeys = [],
  sessionOrder = [],
  titleOverrides = {},
  projectNameOverrides = {},
  collapsedGroups = {},
  runningChatIds = [],
  updatedChatIds = [],
  density = "comfortable",
  showPreviews = false,
  showTimestamps = false,
  sort = "updated_desc",
  showArchived = false,
  defaultWorkspacePath,
  actionMenuPortalContainer,
  loading,
  emptyLabel,
}: ChatListProps) {
  const { t } = useTranslation();
  const [visibleLimit, setVisibleLimit] = useState(INITIAL_VISIBLE_SESSIONS);
  const tabRowRefs = useRef(new Map<string, HTMLLIElement>());
  const pendingTabRectsRef = useRef<Map<string, DOMRect> | null>(null);
  const tabLayoutAnimationsRef = useRef(new Map<string, Animation>());
  const [collapsedPaneGroups, setCollapsedPaneGroups] = useState<Set<string>>(
    () => new Set(),
  );
  const [deleteSelectionMode, setDeleteSelectionMode] = useState(false);
  const [selectedDeleteKeys, setSelectedDeleteKeys] = useState<Set<string>>(
    () => new Set(),
  );
  const deleteItemsByKey = useMemo(() => {
    const items = new Map<string, SidebarDeleteItem>();
    for (const group of Object.values(paneGroups)) {
      for (const pane of group.panes) {
        items.set(pane.key, { key: pane.key, label: pane.title });
      }
    }
    for (const session of sessions) {
      if (items.has(session.key)) continue;
      items.set(session.key, {
        key: session.key,
        label: displayTitle(session, titleOverrides, t("chat.newChat")),
      });
    }
    return items;
  }, [paneGroups, sessions, t, titleOverrides]);
  const paneGroupTargets = useMemo(() => Array.from(new Map(
    Object.values(paneGroups)
      .filter((group) => group.visible ?? group.panes.length > 1)
      .map((group) => [group.tabKey, {
        key: group.tabKey,
        title: group.title,
        paneCount: group.panes.length,
        atCapacity: group.panes.length >= MAX_WORKBENCH_PANES,
      }]),
  ).values()), [paneGroups]);
  const labels = useMemo<ChatGroupLabels>(() => ({
    pinned: t("chat.groups.pinned"),
    all: t("chat.groups.all"),
    today: t("chat.groups.today"),
    yesterday: t("chat.groups.yesterday"),
    earlier: t("chat.groups.earlier"),
    archived: t("chat.groups.archived"),
    projects: t("chat.groups.projects"),
    fallbackTitle: t("chat.newChat"),
  }), [t]);
  const groups = useMemo(
    () => groupSessions(sessions, labels, {
      pinnedKeys,
      archivedKeys,
      titleOverrides,
      projectNameOverrides,
      sessionOrder,
      showArchived,
      sort,
      defaultWorkspacePath,
    }),
    [
      archivedKeys,
      labels,
      pinnedKeys,
      sessions,
      showArchived,
      sort,
      titleOverrides,
      projectNameOverrides,
      sessionOrder,
      defaultWorkspacePath,
    ],
  );
  const limitedGroups = useMemo(
    () => limitGroups(groups, visibleLimit, activeKey, collapsedGroups),
    [activeKey, collapsedGroups, groups, visibleLimit],
  );
  const totalSessionCount = useMemo(
    () => groups.reduce(
      (total, group) =>
        total + (isCollapsedProject(group, collapsedGroups) ? 0 : group.sessions.length),
      0,
    ),
    [collapsedGroups, groups],
  );
  const visibleSessionCount = useMemo(
    () => limitedGroups.reduce((total, group) => total + group.sessions.length, 0),
    [limitedGroups],
  );
  const pinned = useMemo(() => new Set(pinnedKeys), [pinnedKeys]);
  const archived = useMemo(() => new Set(archivedKeys), [archivedKeys]);
  const pinnedPanes = useMemo(() => new Set(pinnedPaneKeys), [pinnedPaneKeys]);
  const archivedPanes = useMemo(() => new Set(archivedPaneKeys), [archivedPaneKeys]);
  const hiddenSessionCount = Math.max(0, totalSessionCount - visibleSessionCount);

  useEffect(() => {
    setVisibleLimit(INITIAL_VISIBLE_SESSIONS);
  }, [showArchived, sort]);

  useEffect(() => {
    if (!deleteSelectionMode) return;
    const onKeyDown = (event: KeyboardEvent) => {
      if (event.key !== "Escape") return;
      setDeleteSelectionMode(false);
      setSelectedDeleteKeys(new Set());
    };
    window.addEventListener("keydown", onKeyDown);
    return () => window.removeEventListener("keydown", onKeyDown);
  }, [deleteSelectionMode]);

  useEffect(() => {
    setCollapsedPaneGroups((current) => {
      const next = new Set(Array.from(current).filter((key) => (
        (paneGroups[key]?.panes.length ?? 0) > 1
      )));
      if (next.size === current.size && Array.from(next).every((key) => current.has(key))) {
        return current;
      }
      return next;
    });
  }, [paneGroups]);

  const measureTabRows = useCallback(() => {
    const rects = new Map<string, DOMRect>();
    for (const [key, row] of tabRowRefs.current) {
      rects.set(key, row.getBoundingClientRect());
    }
    return rects;
  }, []);

  const captureTabLayout = useCallback(() => {
    for (const animation of tabLayoutAnimationsRef.current.values()) animation.cancel();
    tabLayoutAnimationsRef.current.clear();
    pendingTabRectsRef.current = measureTabRows();
  }, [measureTabRows]);

  const togglePaneGroup = useCallback((key: string) => {
    captureTabLayout();
    setCollapsedPaneGroups((current) => {
      const next = new Set(current);
      if (next.has(key)) next.delete(key);
      else next.add(key);
      return next;
    });
  }, [captureTabLayout]);

  useLayoutEffect(() => {
    const previousRects = pendingTabRectsRef.current;
    if (!previousRects) return;
    pendingTabRectsRef.current = null;
    const nextRects = measureTabRows();
    const reduceMotion = typeof window.matchMedia === "function"
      && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    if (reduceMotion) return;
    for (const [key, nextRect] of nextRects) {
      const previousRect = previousRects.get(key);
      const row = tabRowRefs.current.get(key);
      if (!previousRect || !row || typeof row.animate !== "function") continue;
      const deltaY = previousRect.top - nextRect.top;
      if (Math.abs(deltaY) < 0.5) continue;
      const animation = row.animate(
        [
          { transform: `translateY(${deltaY}px)` },
          { transform: "translateY(0)" },
        ],
        {
          duration: 180,
          easing: "cubic-bezier(0.2, 0, 0, 1)",
        },
      );
      tabLayoutAnimationsRef.current.set(key, animation);
      animation.addEventListener("finish", () => {
        if (tabLayoutAnimationsRef.current.get(key) === animation) {
          tabLayoutAnimationsRef.current.delete(key);
        }
      }, { once: true });
    }
  }, [collapsedPaneGroups, measureTabRows]);

  useEffect(() => () => {
    for (const animation of tabLayoutAnimationsRef.current.values()) animation.cancel();
  }, []);

  if (loading && sessions.length === 0 && temporarySessions.length === 0) {
    return (
      <div className="px-3 py-6 text-[12px] text-muted-foreground">
        {t("chat.loading")}
      </div>
    );
  }

  if (sessions.length === 0 && temporarySessions.length === 0) {
    return (
      <div className="px-3 py-6 text-[12px] leading-5 text-muted-foreground/80">
        {emptyLabel ?? t("chat.noSessions")}
      </div>
    );
  }

  const running = new Set(runningChatIds);
  const updated = new Set(updatedChatIds);
  const compact = density === "compact";
  const firstProjectGroupIndex = limitedGroups.findIndex((group) => group.kind === "project");

  const beginDeleteSelection = (keys: string[]) => {
    setDeleteSelectionMode(true);
    setSelectedDeleteKeys(new Set(keys.filter((key) => deleteItemsByKey.has(key))));
  };
  const toggleDeleteSelection = (keys: string[]) => {
    setSelectedDeleteKeys((current) => {
      const next = new Set(current);
      const validKeys = keys.filter((key) => deleteItemsByKey.has(key));
      const remove = validKeys.length > 0 && validKeys.every((key) => next.has(key));
      for (const key of validKeys) {
        if (remove) next.delete(key);
        else next.add(key);
      }
      return next;
    });
  };
  const closeDeleteSelection = () => {
    setDeleteSelectionMode(false);
    setSelectedDeleteKeys(new Set());
  };
  const requestDeleteItems = (items: SidebarDeleteItem[]) => {
    if (items.length === 0) return;
    if (onRequestDeleteMany) onRequestDeleteMany(items);
    else if (items.length === 1) onRequestDelete(items[0].key, items[0].label);
  };
  const requestDeleteKeys = (keys: string[]) => {
    requestDeleteItems(keys
      .map((key) => deleteItemsByKey.get(key))
      .filter((item): item is SidebarDeleteItem => item !== undefined));
  };
  const confirmDeleteSelection = () => {
    requestDeleteKeys(Array.from(selectedDeleteKeys));
    closeDeleteSelection();
  };
  return (
    <div className="h-full min-h-0 min-w-0 overflow-x-hidden overflow-y-auto overscroll-contain scrollbar-thin scrollbar-track-transparent">
      <div
        data-chat-list-content
        className="relative min-w-0 space-y-3 px-2 py-1.5"
      >
        {temporarySessions.length > 0 ? (
          <TemporaryChatSection
            sessions={temporarySessions}
            activeKey={activeKey}
            running={running}
            onSelect={onSelect}
            onClose={onCloseTemporaryChat}
          />
        ) : null}
        {limitedGroups.map((group, index) => {
          const foldableChatsGroup = isFoldableChatsGroup(group);
          const foldedChatsGroup = isFoldedChatsGroup(group, collapsedGroups);
          const visibleSessions = visibleSessionsForGroup(
            group,
            activeKey,
            collapsedGroups,
          );
          const hiddenInGroup = Math.max(0, group.sessions.length - visibleSessions.length);
          const canToggleFold = group.sessions.length > COLLAPSED_CHATS_VISIBLE_COUNT;
          return (
            <section key={group.id} aria-label={group.label} className="relative z-[1]">
              {index === firstProjectGroupIndex ? (
                <div className="px-2 pb-1 text-[12px] font-medium text-muted-foreground/65">
                  {labels.projects}
                </div>
              ) : null}
              {group.kind === "project" ? (
                <ProjectGroupHeader
                  label={group.label}
                  path={group.projectPath}
                  collapsed={Boolean(collapsedGroups[group.id])}
                  onToggle={() => onToggleGroup?.(group.id)}
                  onRequestRename={
                    group.projectKey && onRequestRenameProject
                      ? () => onRequestRenameProject(group.projectKey ?? "", group.label)
                      : undefined
                  }
                  onNewChat={
                    group.projectPath && onNewChatInProject
                      ? () => onNewChatInProject(group.projectPath ?? "", group.label)
                      : undefined
                  }
                  actionMenuPortalContainer={actionMenuPortalContainer}
                  updatedAt={showTimestamps ? group.updatedAt : null}
                />
              ) : (
                <ChatsGroupHeader label={group.label} />
              )}
              {group.kind === "project" && collapsedGroups[group.id] ? null : (
                <ul className="space-y-0.5">
                  {visibleSessions.map((s) => {
                    const topicActive = s.key === activeKey;
                    const paneGroup = paneGroups[s.key];
                    const title = displayTitle(s, titleOverrides, t("chat.newChat"));
                    const resolvedPaneGroup = paneGroup ?? {
                      tabKey: s.key,
                      title,
                      activePaneKey: s.key,
                      panes: [{ key: s.key, chatId: s.chatId, title }],
                    };
                    const isWorkbenchTab = paneGroup?.visible
                      ?? ((paneGroup?.panes.length ?? 0) > 1);
                    const paneGroupCollapsed = isWorkbenchTab
                      && collapsedPaneGroups.has(s.key);
                    const paneGroupId = `sidebar-pane-group-${s.key.replace(
                      /[^a-zA-Z0-9_-]/g,
                      "-",
                    )}`;
                    const tabDeleteKeys = resolvedPaneGroup.panes.map((pane) => pane.key);
                    const tabSelected = tabDeleteKeys.every((key) => (
                      selectedDeleteKeys.has(key)
                    ));
                    const tabPartiallySelected = !tabSelected && tabDeleteKeys.some((key) => (
                      selectedDeleteKeys.has(key)
                    ));
                    const projectMode = group.kind === "project";

                    if (isWorkbenchTab) {
                      return (
                        <li
                          key={s.key}
                          ref={(element) => {
                            if (element) tabRowRefs.current.set(s.key, element);
                            else tabRowRefs.current.delete(s.key);
                          }}
                          data-sidebar-tab-group="true"
                          data-pane-group-collapsed={paneGroupCollapsed ? "true" : undefined}
                          className="relative my-1.5 min-w-0"
                        >
                          <div
                            data-workbench-tab-surface
                            className={cn(
                              "-mx-2 min-w-0 bg-sidebar-foreground/[0.045] px-3 dark:bg-white/[0.07]",
                              paneGroupCollapsed ? "py-0" : "py-1",
                              deleteSelectionMode && (tabSelected || tabPartiallySelected)
                                && "ring-1 ring-inset ring-sidebar-foreground/25",
                            )}
                          >
                            <div className={cn("min-w-0", projectMode && "ps-7")}>
                              <WorkbenchTabHeader
                                title={title}
                                controlsId={paneGroupId}
                                collapsed={paneGroupCollapsed}
                                deleteSelectionMode={deleteSelectionMode}
                                selected={tabSelected}
                                partiallySelected={tabPartiallySelected}
                                onToggle={() => togglePaneGroup(s.key)}
                                onToggleSelection={() => toggleDeleteSelection(tabDeleteKeys)}
                                onRequestRename={onRequestRenameTab
                                  ? () => onRequestRenameTab(s.key, title)
                                  : undefined}
                                onDissolve={onDissolveTab
                                  ? () => onDissolveTab(resolvedPaneGroup.tabKey)
                                  : undefined}
                                onRequestDelete={() => requestDeleteKeys(tabDeleteKeys)}
                                actionMenuPortalContainer={actionMenuPortalContainer}
                              />
                              {!paneGroupCollapsed ? (
                                <ActivePaneRows
                                  id={paneGroupId}
                                  group={resolvedPaneGroup}
                                  tabTitle={title}
                                  tabActive={topicActive}
                                  compact={compact}
                                  running={running}
                                  updated={updated}
                                  onSelectPane={onSelectPane}
                                  onRequestDelete={onRequestDelete}
                                  onRequestRename={onRequestRename}
                                  onTogglePin={onTogglePin}
                                  onToggleArchive={onToggleArchive}
                                  pinned={pinnedPanes}
                                  archived={archivedPanes}
                                  onDetachPane={onDetachPane}
                                  moveTargets={paneGroupTargets.filter((target) => (
                                    target.key !== resolvedPaneGroup.tabKey
                                  ))}
                                  onAttachPane={onAttachPane}
                                  deleteSelectionMode={deleteSelectionMode}
                                  selectedDeleteKeys={selectedDeleteKeys}
                                  onToggleDeleteSelection={toggleDeleteSelection}
                                  onBeginDeleteSelection={beginDeleteSelection}
                                  actionMenuPortalContainer={actionMenuPortalContainer}
                                />
                              ) : null}
                            </div>
                          </div>
                        </li>
                      );
                    }

                    const fallbackTitle = t("chat.fallbackTitle", {
                      id: s.chatId.slice(0, 6),
                    });
                    const generatedTitle = s.title?.trim() || "";
                    const tooltipTitle =
                      titleOverrides[s.key]?.trim() ||
                      generatedTitle ||
                      deriveTitle(s.preview, fallbackTitle);
                    const isPinned = pinned.has(s.key);
                    const isArchived = archived.has(s.key);
                    const preview = visibleSessionPreview(s.preview);
                    const showPreview = showPreviews && preview && preview !== title;
                    const timestamp = showTimestamps
                      ? relativeTime(s.updatedAt ?? s.createdAt)
                      : "";
                    const activityState = running.has(s.chatId)
                      ? "running"
                      : updated.has(s.chatId) && !topicActive
                        ? "updated"
                        : null;
                    return (
                      <li
                        key={s.key}
                        ref={(element) => {
                          if (element) tabRowRefs.current.set(s.key, element);
                          else tabRowRefs.current.delete(s.key);
                        }}
                        className="relative min-w-0"
                      >
                        <div
                          data-chat-row={s.key}
                          data-sidebar-tab={s.key}
                          className={cn(
                            "group flex min-w-0 max-w-full items-center gap-1 rounded-[0.65rem] px-2 text-[13px]",
                            SIDEBAR_SELECTION_ITEM_CLASS,
                            compact ? "min-h-7" : "min-h-8",
                            topicActive
                              ? "bg-sidebar-selected text-sidebar-accent-foreground"
                              : "text-sidebar-foreground/82 hover:bg-sidebar-foreground/[0.075] hover:text-sidebar-foreground dark:hover:bg-white/[0.09]",
                            deleteSelectionMode && (tabSelected || tabPartiallySelected)
                              && "bg-sidebar-accent/55 text-sidebar-accent-foreground",
                          )}
                        >
                          <button
                            type="button"
                            onClick={() => {
                              if (deleteSelectionMode) {
                                toggleDeleteSelection(tabDeleteKeys);
                                return;
                              }
                              if (!topicActive) onSelect(s.key);
                            }}
                            draggable={false}
                            aria-current={topicActive ? "page" : undefined}
                            aria-pressed={deleteSelectionMode ? tabSelected : undefined}
                            title={tooltipTitle}
                            className={cn(
                              "flex min-w-0 flex-1 items-center gap-2 overflow-hidden text-left",
                              deleteSelectionMode && "cursor-default",
                              compact ? "py-1" : "py-1.5",
                              projectMode && "pl-7",
                            )}
                          >
                            {deleteSelectionMode ? (
                              <SelectionIndicator
                                checked={tabSelected}
                                partial={tabPartiallySelected}
                              />
                            ) : null}
                            <span className="min-w-0 flex-1 overflow-hidden">
                                {projectMode ? (
                                  <span className="flex w-full min-w-0 items-baseline gap-2">
                                    <span className="min-w-0 flex-1 truncate font-medium leading-5">
                                      {title}
                                    </span>
                                    {isPinned ? <PinnedChatIndicator label={labels.pinned} /> : null}
                                    {timestamp ? (
                                      <span className="shrink-0 text-[11.5px] font-medium text-muted-foreground/58">
                                        {timestamp}
                                      </span>
                                    ) : null}
                                  </span>
                                ) : (
                                  <span className="flex w-full min-w-0 items-center gap-1.5">
                                    <span className="min-w-0 flex-1 truncate font-medium leading-5">
                                      {title}
                                    </span>
                                    {isPinned ? <PinnedChatIndicator label={labels.pinned} /> : null}
                                  </span>
                                )}
                                {showPreview ? (
                                  <span className="block w-full truncate text-[11.5px] leading-4 text-muted-foreground/72">
                                    {preview}
                                  </span>
                                ) : null}
                                {timestamp && !projectMode ? (
                                  <span className="block w-full truncate text-[11px] leading-4 text-muted-foreground/58">
                                    {timestamp}
                                  </span>
                                ) : null}
                            </span>
                          </button>
                          <SessionActivityIndicator state={activityState} />
                          {!deleteSelectionMode ? (
                            <DropdownMenu modal={false}>
                            <DropdownMenuTrigger
                              className={cn(
                                "inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-muted-foreground/75 opacity-0 transition-opacity",
                                "hover:bg-sidebar-accent hover:text-sidebar-foreground group-hover:opacity-100",
                                "focus-visible:opacity-100 data-[state=open]:opacity-100",
                              )}
                              aria-label={t("chat.actions", { title })}
                            >
                              <MoreHorizontal className="h-3.5 w-3.5" />
                            </DropdownMenuTrigger>
                            <DropdownMenuContent
                              align="end"
                              className={ACTION_MENU_CONTENT_CLASS}
                              portalContainer={actionMenuPortalContainer}
                              onCloseAutoFocus={(event) => event.preventDefault()}
                            >
                              <DropdownMenuItem onSelect={() => onTogglePin(s.key)}>
                                {isPinned ? (
                                  <PinOff className="h-4 w-4 shrink-0" />
                                ) : (
                                  <Pin className="h-4 w-4 shrink-0" />
                                )}
                                {isPinned ? t("chat.unpin") : t("chat.pin")}
                              </DropdownMenuItem>
                              <DropdownMenuItem
                                onSelect={() => onRequestRename(s.key, title)}
                              >
                                <Pencil className="h-4 w-4 shrink-0" />
                                {t("chat.rename")}
                              </DropdownMenuItem>
                              <DropdownMenuItem onSelect={() => onToggleArchive(s.key)}>
                                {isArchived ? (
                                  <ArchiveRestore className="h-4 w-4 shrink-0" />
                                ) : (
                                  <Archive className="h-4 w-4 shrink-0" />
                                )}
                                {isArchived ? t("chat.unarchive") : t("chat.archive")}
                              </DropdownMenuItem>
                              {paneGroup && onCreateTab ? (
                                <DropdownMenuItem onSelect={() => onCreateTab(paneGroup.tabKey)}>
                                  <PanelsTopLeft className="h-4 w-4 shrink-0" aria-hidden />
                                  {t("workbench.createGroup", {
                                    defaultValue: "Create group",
                                  })}
                                </DropdownMenuItem>
                              ) : null}
                              {paneGroup && onAttachPane ? (
                                <MoveToGroupSubmenu
                                  targets={paneGroupTargets.filter((target) => (
                                    target.key !== paneGroup.tabKey
                                  ))}
                                  onMove={(targetKey) => onAttachPane(s.key, targetKey)}
                                />
                              ) : null}
                              <DropdownMenuItem
                                onSelect={() => beginDeleteSelection(tabDeleteKeys)}
                              >
                                <ListChecks className="h-4 w-4 shrink-0" />
                                {t("chat.select", { defaultValue: "Select" })}
                              </DropdownMenuItem>
                              <DropdownMenuItem
                                tone="destructive"
                                onSelect={() => {
                                  window.setTimeout(() => requestDeleteKeys(tabDeleteKeys), 0);
                                }}
                              >
                                <Trash2 className="h-4 w-4 shrink-0" />
                                {t("chat.delete")}
                              </DropdownMenuItem>
                            </DropdownMenuContent>
                          </DropdownMenu>
                          ) : null}
                        </div>
                      </li>
                    );
                  })}
                </ul>
              )}
              {foldableChatsGroup && canToggleFold ? (
                <ChatsFoldFooter
                  folded={foldedChatsGroup}
                  hiddenCount={hiddenInGroup}
                  onToggle={() => onToggleGroup?.(group.id)}
                />
              ) : null}
            </section>
          );
        })}
        {hiddenSessionCount > 0 ? (
          <div className="relative z-[1] px-2 pb-2 pt-1">
            <button
              type="button"
              onClick={() =>
                setVisibleLimit((limit) =>
                  Math.min(totalSessionCount, limit + VISIBLE_SESSIONS_INCREMENT),
                )
              }
              className="h-8 w-full rounded-full text-[12px] font-medium text-muted-foreground/65 transition-colors hover:bg-sidebar-accent/65 hover:text-muted-foreground"
            >
              {t("chat.showMore", { count: hiddenSessionCount })}
            </button>
          </div>
        ) : null}
        {deleteSelectionMode ? (
          <div
            data-testid="delete-selection-bar"
            className="sticky bottom-2 z-30 mx-1 mt-3 flex min-h-11 items-center gap-2 rounded-2xl border border-sidebar-border/80 bg-popover/95 p-1.5 pl-2 shadow-[0_10px_30px_rgba(15,23,42,0.14)] backdrop-blur-xl"
          >
            <button
              type="button"
              onClick={closeDeleteSelection}
              aria-label={t("chat.cancelSelection", {
                defaultValue: "Cancel selection",
              })}
              className="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-accent/60 hover:text-foreground"
            >
              <X className="h-4 w-4" aria-hidden />
            </button>
            <span className="min-w-0 flex-1 truncate px-1 text-[12.5px] font-medium text-foreground/85">
              {t("chat.selectedCount", {
                defaultValue: "{{count}} selected",
                count: selectedDeleteKeys.size,
              })}
            </span>
            <button
              type="button"
              disabled={selectedDeleteKeys.size === 0}
              onClick={confirmDeleteSelection}
              className="inline-flex h-8 shrink-0 items-center gap-1.5 rounded-full bg-destructive px-3 text-[12px] font-semibold text-destructive-foreground transition-colors hover:bg-destructive/90 disabled:pointer-events-none disabled:opacity-40"
            >
              <Trash2 className="h-3.5 w-3.5" aria-hidden />
              {t("chat.deleteSelected", { defaultValue: "Delete" })}
            </button>
          </div>
        ) : null}
      </div>
    </div>
  );
});

function WorkbenchTabHeader({
  title,
  controlsId,
  collapsed,
  deleteSelectionMode,
  selected,
  partiallySelected,
  onToggle,
  onToggleSelection,
  onRequestRename,
  onDissolve,
  onRequestDelete,
  actionMenuPortalContainer,
}: {
  title: string;
  controlsId: string;
  collapsed: boolean;
  deleteSelectionMode: boolean;
  selected: boolean;
  partiallySelected: boolean;
  onToggle: () => void;
  onToggleSelection: () => void;
  onRequestRename?: () => void;
  onDissolve?: () => void;
  onRequestDelete: () => void;
  actionMenuPortalContainer?: HTMLElement | null;
}) {
  const { t } = useTranslation();
  const disclosureLabel = t(
    collapsed ? "workbench.expandTabGroup" : "workbench.collapseTabGroup",
    { title },
  );

  return (
    <div
      data-workbench-tab
      className={cn(
        "group/tab flex min-w-0 items-center gap-0.5 rounded-[0.65rem] px-1.5 text-sidebar-foreground/85",
        collapsed ? "min-h-6" : "min-h-7",
      )}
    >
      <button
        type="button"
        onClick={deleteSelectionMode ? onToggleSelection : onToggle}
        draggable={false}
        aria-label={t("workbench.tabAria", { title })}
        aria-expanded={deleteSelectionMode ? undefined : !collapsed}
        aria-controls={deleteSelectionMode ? undefined : controlsId}
        aria-pressed={deleteSelectionMode ? selected : undefined}
        title={title}
        className={cn(
          "flex min-w-0 flex-1 items-center gap-2 overflow-hidden px-0.5 py-1 text-left",
          "text-[12.5px] font-normal leading-5",
          deleteSelectionMode && "cursor-default",
        )}
      >
        {deleteSelectionMode ? (
          <SelectionIndicator checked={selected} partial={partiallySelected} />
        ) : null}
        <span className="min-w-0 flex-1 truncate">{title}</span>
      </button>
      {!deleteSelectionMode ? (
        <>
          <DropdownMenu modal={false}>
            <DropdownMenuTrigger
              className={cn(
                "inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-md",
                "text-muted-foreground/75 opacity-0 transition-opacity",
                "hover:bg-sidebar-accent hover:text-sidebar-foreground group-hover/tab:opacity-100",
                "focus-visible:opacity-100 data-[state=open]:opacity-100",
              )}
              aria-label={t("chat.actions", { title })}
            >
              <MoreHorizontal className="h-3.5 w-3.5" />
            </DropdownMenuTrigger>
            <DropdownMenuContent
              align="end"
              className={ACTION_MENU_CONTENT_CLASS}
              portalContainer={actionMenuPortalContainer}
              onCloseAutoFocus={(event) => event.preventDefault()}
            >
              {onRequestRename ? (
                <DropdownMenuItem onSelect={onRequestRename}>
                  <Pencil className="h-4 w-4 shrink-0" />
                  {t("chat.rename")}
                </DropdownMenuItem>
              ) : null}
              {onDissolve ? (
                <DropdownMenuItem onSelect={onDissolve}>
                  <Ungroup className="h-4 w-4 shrink-0" />
                  {t("workbench.dissolveTab", { defaultValue: "Dissolve group" })}
                </DropdownMenuItem>
              ) : null}
              <DropdownMenuItem
                tone="destructive"
                onSelect={() => window.setTimeout(onRequestDelete, 0)}
              >
                <Trash2 className="h-4 w-4 shrink-0" />
                {t("chat.delete")}
              </DropdownMenuItem>
            </DropdownMenuContent>
          </DropdownMenu>
          <button
            type="button"
            aria-expanded={!collapsed}
            aria-controls={controlsId}
            aria-label={disclosureLabel}
            title={disclosureLabel}
            onClick={onToggle}
            className={cn(
              "inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-md",
              "text-muted-foreground/70 transition-[background-color,color,transform] duration-150 ease-out",
              "hover:bg-sidebar-accent hover:text-sidebar-foreground active:scale-[0.96]",
              "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/60",
              "motion-reduce:transition-none motion-reduce:active:scale-100",
            )}
          >
            <ChevronDown
              aria-hidden
              className={cn(
                "h-3.5 w-3.5 transition-transform duration-200 ease-out motion-reduce:transition-none",
                !collapsed && "rotate-180",
              )}
            />
          </button>
        </>
      ) : null}
    </div>
  );
}

function ActivePaneRows({
  id,
  group,
  tabTitle,
  tabActive,
  compact,
  running,
  updated,
  onSelectPane,
  onRequestDelete,
  onRequestRename,
  onTogglePin,
  onToggleArchive,
  pinned,
  archived,
  onDetachPane,
  moveTargets,
  onAttachPane,
  deleteSelectionMode,
  selectedDeleteKeys,
  onToggleDeleteSelection,
  onBeginDeleteSelection,
  actionMenuPortalContainer,
}: {
  id: string;
  group: SidebarPaneGroup;
  tabTitle: string;
  tabActive: boolean;
  compact: boolean;
  running: ReadonlySet<string>;
  updated: ReadonlySet<string>;
  onSelectPane?: (tabKey: string, paneKey: string) => void;
  onRequestDelete: (key: string, label: string) => void;
  onRequestRename: (key: string, label: string) => void;
  onTogglePin: (key: string) => void;
  onToggleArchive: (key: string) => void;
  pinned: ReadonlySet<string>;
  archived: ReadonlySet<string>;
  onDetachPane?: (tabKey: string, paneKey: string) => void;
  moveTargets: PaneGroupTarget[];
  onAttachPane?: (
    paneKey: string,
    tabKey: string,
  ) => void;
  deleteSelectionMode: boolean;
  selectedDeleteKeys: ReadonlySet<string>;
  onToggleDeleteSelection: (keys: string[]) => void;
  onBeginDeleteSelection: (keys: string[]) => void;
  actionMenuPortalContainer?: HTMLElement | null;
}) {
  const { t } = useTranslation();
  const panes = group.panes;
  return (
    <ul
      id={id}
      aria-label={t("workbench.panesInTab", {
        defaultValue: "Panes in {{title}}",
        title: tabTitle,
      })}
      className="mt-0.5 space-y-0.5"
    >
      {panes.map((pane) => {
        const active = tabActive && pane.key === group.activePaneKey;
        const activityState = running.has(pane.chatId)
          ? "running"
          : updated.has(pane.chatId) && !active
            ? "updated"
            : null;
        const paneActionsLabel = t("workbench.paneActions", {
          defaultValue: "{{title}} pane actions",
          title: pane.title,
        });
        const selected = selectedDeleteKeys.has(pane.key);
        const isPinned = pinned.has(pane.key);
        const isArchived = archived.has(pane.key);

        return (
          <li
            key={pane.key}
            className="relative min-w-0"
          >
            <div
              data-chat-row={pane.key}
              data-sidebar-pane={pane.key}
              className={cn(
                "group/pane flex min-w-0 max-w-full items-center gap-1 rounded-[0.65rem] px-2 text-[13px]",
                SIDEBAR_SELECTION_ITEM_CLASS,
                compact ? "min-h-7" : "min-h-8",
                active
                  ? "bg-sidebar-selected text-sidebar-accent-foreground"
                  : "text-sidebar-foreground/82 hover:bg-sidebar-foreground/[0.075] hover:text-sidebar-foreground dark:hover:bg-white/[0.09]",
                deleteSelectionMode && selected
                  && "bg-sidebar-accent/55 text-sidebar-accent-foreground",
              )}
            >
              <button
                type="button"
                onClick={() => {
                  if (deleteSelectionMode) {
                    onToggleDeleteSelection([pane.key]);
                    return;
                  }
                  onSelectPane?.(group.tabKey, pane.key);
                }}
                draggable={false}
                aria-current={active ? "true" : undefined}
                aria-pressed={deleteSelectionMode ? selected : undefined}
                title={pane.title}
                className={cn(
                  "flex min-w-0 flex-1 items-center gap-2 overflow-hidden text-left font-medium leading-5",
                  compact ? "py-1" : "py-1.5",
                  deleteSelectionMode && "cursor-default",
                )}
              >
                {deleteSelectionMode ? (
                  <SelectionIndicator checked={selected} partial={false} />
                ) : null}
                <span className="min-w-0 flex-1 truncate">{pane.title}</span>
                {isPinned ? <PinnedChatIndicator label={t("chat.groups.pinned")} /> : null}
              </button>
              <SessionActivityIndicator state={activityState} />
              {!deleteSelectionMode ? <DropdownMenu modal={false}>
                <DropdownMenuTrigger
                  className={cn(
                    "inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-muted-foreground/70 opacity-0 transition-opacity",
                    "hover:bg-sidebar-accent hover:text-sidebar-foreground group-hover/pane:opacity-100",
                    "focus-visible:opacity-100 data-[state=open]:opacity-100",
                  )}
                  aria-label={paneActionsLabel}
                >
                  <MoreHorizontal className="h-3.5 w-3.5" />
                </DropdownMenuTrigger>
                <DropdownMenuContent
                  align="end"
                  className={ACTION_MENU_CONTENT_CLASS}
                  portalContainer={actionMenuPortalContainer}
                  onCloseAutoFocus={(event) => event.preventDefault()}
                >
                  <DropdownMenuItem onSelect={() => onTogglePin(pane.key)}>
                    {isPinned ? (
                      <PinOff className="h-4 w-4 shrink-0" />
                    ) : (
                      <Pin className="h-4 w-4 shrink-0" />
                    )}
                    {isPinned ? t("chat.unpin") : t("chat.pin")}
                  </DropdownMenuItem>
                  <DropdownMenuItem
                    onSelect={() => onRequestRename(pane.key, pane.title)}
                  >
                    <Pencil className="h-4 w-4 shrink-0" />
                    {t("chat.rename")}
                  </DropdownMenuItem>
                  <DropdownMenuItem onSelect={() => onToggleArchive(pane.key)}>
                    {isArchived ? (
                      <ArchiveRestore className="h-4 w-4 shrink-0" />
                    ) : (
                      <Archive className="h-4 w-4 shrink-0" />
                    )}
                    {isArchived ? t("chat.unarchive") : t("chat.archive")}
                  </DropdownMenuItem>
                  {onDetachPane ? (
                    <DropdownMenuItem onSelect={() => onDetachPane(group.tabKey, pane.key)}>
                      <Unplug className="h-4 w-4 shrink-0" />
                      {t("workbench.detachPane", {
                        defaultValue: "Remove",
                        title: pane.title,
                      })}
                    </DropdownMenuItem>
                  ) : null}
                  {onAttachPane ? (
                    <MoveToGroupSubmenu
                      targets={moveTargets}
                      onMove={(targetKey) => onAttachPane(pane.key, targetKey)}
                    />
                  ) : null}
                  <DropdownMenuItem
                    onSelect={() => onBeginDeleteSelection([pane.key])}
                  >
                    <ListChecks className="h-4 w-4 shrink-0" />
                    {t("chat.select", { defaultValue: "Select" })}
                  </DropdownMenuItem>
                  <DropdownMenuItem
                    tone="destructive"
                    onSelect={() => {
                      window.setTimeout(() => onRequestDelete(pane.key, pane.title), 0);
                    }}
                  >
                    <Trash2 className="h-4 w-4 shrink-0" />
                    {t("chat.delete")}
                  </DropdownMenuItem>
                </DropdownMenuContent>
              </DropdownMenu> : null}
            </div>
          </li>
        );
      })}
    </ul>
  );
}

function SelectionIndicator({
  checked,
  partial,
}: {
  checked: boolean;
  partial: boolean;
}) {
  const Icon = partial ? SquareMinus : checked ? SquareCheckBig : Square;
  return (
    <Icon
      aria-hidden
      className={cn(
        "h-4 w-4 shrink-0",
        checked || partial ? "text-primary" : "text-muted-foreground/55",
      )}
    />
  );
}

function MoveToGroupSubmenu({
  targets,
  onMove,
}: {
  targets: PaneGroupTarget[];
  onMove: (targetKey: string) => void;
}) {
  const { t } = useTranslation();
  if (targets.length === 0) return null;
  return (
    <DropdownMenuSub>
      <DropdownMenuSubTrigger>
        <MoveRight className="h-4 w-4 shrink-0" aria-hidden />
        {t("workbench.moveTo", { defaultValue: "Move to" })}
      </DropdownMenuSubTrigger>
      <DropdownMenuSubContent>
        {targets.map((target) => (
          <DropdownMenuItem
            key={target.key}
            disabled={target.atCapacity}
            onSelect={() => onMove(target.key)}
          >
            <span className="min-w-0 max-w-56 flex-1 truncate">{target.title}</span>
            <span className="shrink-0 tabular-nums text-muted-foreground/70">
              · {target.paneCount}/{MAX_WORKBENCH_PANES}
            </span>
          </DropdownMenuItem>
        ))}
      </DropdownMenuSubContent>
    </DropdownMenuSub>
  );
}

function TemporaryChatSection({
  sessions,
  activeKey,
  running,
  onSelect,
  onClose,
}: {
  sessions: ChatSummary[];
  activeKey: string | null;
  running: ReadonlySet<string>;
  onSelect: (key: string) => void;
  onClose?: (key: string) => void;
}) {
  const { t } = useTranslation();

  return (
    <section aria-label={t("temporaryChat.sectionTitle")} className="relative z-[1]">
      <ChatsGroupHeader label={t("temporaryChat.sectionTitle")} />
      <ul className="space-y-0.5">
        {sessions.map((session) => {
          const active = session.key === activeKey;
          const title = deriveTemporaryChatTitle(session.preview, t("temporaryChat.title"));
          return (
            <li key={session.key} className="min-w-0">
              <div
                data-temporary-chat-row={session.key}
                className={cn(
                  "group flex min-h-8 min-w-0 max-w-full items-center gap-2 rounded-xl px-2 text-[13px]",
                  SIDEBAR_SELECTION_ITEM_CLASS,
                  active
                    ? "bg-sidebar-selected text-sidebar-accent-foreground"
                    : "text-sidebar-foreground/82 hover:bg-sidebar-foreground/[0.035] hover:text-sidebar-foreground dark:hover:bg-white/[0.05]",
                )}
              >
                <button
                  type="button"
                  onClick={() => onSelect(session.key)}
                  aria-current={active ? "page" : undefined}
                  title={title}
                  className="flex min-w-0 flex-1 items-center gap-2 overflow-hidden py-1.5 text-left"
                >
                  <MessageCircleDashed
                    className="h-3.5 w-3.5 shrink-0 text-[hsl(var(--temporary-foreground))]"
                    aria-hidden
                  />
                  <span className="min-w-0 flex-1 truncate font-medium leading-5">
                    {title}
                  </span>
                </button>
                <SessionActivityIndicator state={running.has(session.chatId) ? "running" : null} />
                {onClose ? (
                  <button
                    type="button"
                    aria-label={t("temporaryChat.closeAction", { title })}
                    onClick={() => onClose(session.key)}
                    className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-muted-foreground/60 transition-colors hover:bg-destructive/10 hover:text-destructive focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/60"
                  >
                    <X className="h-3.5 w-3.5" aria-hidden />
                  </button>
                ) : null}
              </div>
            </li>
          );
        })}
      </ul>
    </section>
  );
}

function ProjectGroupHeader({
  label,
  path,
  collapsed,
  onToggle,
  onRequestRename,
  onNewChat,
  actionMenuPortalContainer,
  updatedAt,
}: {
  label: string;
  path?: string;
  collapsed: boolean;
  onToggle: () => void;
  onRequestRename?: () => void;
  onNewChat?: () => void;
  actionMenuPortalContainer?: HTMLElement | null;
  updatedAt?: string | null;
}) {
  const { t } = useTranslation();

  return (
    <div
      title={path}
      className="group flex min-w-0 items-center gap-1 px-1 pb-1 pt-1 text-[12px] font-medium text-muted-foreground/78"
    >
      <button
        type="button"
        aria-expanded={!collapsed}
        onClick={onToggle}
        className="flex min-w-0 flex-1 items-center gap-2 rounded-lg px-1.5 py-1 text-left transition-colors hover:bg-sidebar-accent/45 hover:text-sidebar-foreground"
      >
        <Folder className="h-3.5 w-3.5 shrink-0" aria-hidden />
        <span className="min-w-0 flex-1 truncate">{label}</span>
      </button>
      {updatedAt ? (
        <span className="shrink-0 text-[11px] text-muted-foreground/55">
          {relativeTime(updatedAt)}
        </span>
      ) : null}
      {onRequestRename ? (
        <DropdownMenu modal={false}>
          <DropdownMenuTrigger
            className={cn(
              "inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-muted-foreground/70 opacity-0 transition-opacity",
              "hover:bg-sidebar-accent hover:text-sidebar-foreground group-hover:opacity-100 focus-visible:opacity-100",
              "data-[state=open]:opacity-100",
            )}
            aria-label={t("chat.actions", { title: label })}
            onClick={(event) => event.stopPropagation()}
          >
            <MoreHorizontal className="h-3.5 w-3.5" />
          </DropdownMenuTrigger>
          <DropdownMenuContent
            align="end"
            className={ACTION_MENU_CONTENT_CLASS}
            portalContainer={actionMenuPortalContainer}
            onCloseAutoFocus={(event) => event.preventDefault()}
          >
            <DropdownMenuItem onSelect={onRequestRename}>
              <Pencil className="h-4 w-4 shrink-0" />
              {t("chat.rename")}
            </DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>
      ) : null}
      {onNewChat ? (
        <button
          type="button"
          aria-label={t("chat.newInProject", { project: label })}
          title={t("chat.newInProject", { project: label })}
          onClick={(event) => {
            event.stopPropagation();
            onNewChat();
          }}
          className={cn(
            "inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-muted-foreground/70 opacity-0 transition-opacity",
            "hover:bg-sidebar-accent hover:text-sidebar-foreground group-hover:opacity-100 focus-visible:opacity-100",
          )}
        >
          <Plus className="h-3.5 w-3.5" />
        </button>
      ) : null}
    </div>
  );
}

function ChatsGroupHeader({ label }: { label: string }) {
  return (
    <div className="px-2 pb-1 text-[12px] font-medium text-muted-foreground/65">
      {label}
    </div>
  );
}

function PinnedChatIndicator({ label }: { label: string }) {
  return (
    <span
      aria-hidden="true"
      title={label}
      className="inline-flex shrink-0 items-center text-muted-foreground/65"
    >
      <Pin className="h-3.5 w-3.5" aria-hidden="true" />
    </span>
  );
}

function ChatsFoldFooter({
  folded,
  hiddenCount,
  onToggle,
}: {
  folded: boolean;
  hiddenCount: number;
  onToggle: () => void;
}) {
  const { t, i18n } = useTranslation();
  const collapsedFallback = i18n.resolvedLanguage?.startsWith("zh")
    ? `已折叠 ${hiddenCount} 个对话`
    : `${hiddenCount} hidden topics`;

  return (
    <div className="px-2 pb-1 pt-1">
      <button
        type="button"
        onClick={onToggle}
        className="h-7 w-full rounded-xl text-left text-[12px] font-medium text-muted-foreground/65 transition-colors hover:bg-sidebar-accent/50 hover:text-muted-foreground"
      >
        <span className="px-2">
          {folded
            ? t("chat.collapsed", {
                count: hiddenCount,
                defaultValue: collapsedFallback,
              })
            : t("chat.showLess")}
        </span>
      </button>
    </div>
  );
}

function SessionActivityIndicator({
  state,
}: {
  state: "running" | "updated" | null;
}) {
  const { t } = useTranslation();

  if (state === "running") {
    const label = t("chat.activity.running");
    return (
      <span
        aria-label={label}
        title={label}
        className="grid h-4 w-4 shrink-0 place-items-center"
      >
        <span className="h-3 w-3 animate-spin rounded-full border border-blue-500/25 border-t-blue-500 [animation-duration:1.4s] motion-reduce:animate-none dark:border-blue-400/25 dark:border-t-blue-400" />
      </span>
    );
  }

  if (state === "updated") {
    const label = t("chat.activity.updated");
    return (
      <span
        aria-label={label}
        title={label}
        className="grid h-4 w-4 shrink-0 place-items-center"
      >
        <span className="h-2 w-2 rounded-full bg-[#ff8a3d] shadow-[0_0_0_2px_rgba(255,138,61,0.16)]" />
      </span>
    );
  }

  return <span className="h-4 w-4 shrink-0" aria-hidden="true" />;
}
