"use client"; import * as React from "react"; import { RefreshCw } from "lucide-react"; import { cn } from "@/lib/utils"; import { Button } from "@/components/ui/button"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; import { colorFromUserName } from "@/lib/yjs-provider"; export type PresenceUser = { clientId: number; name: string; color: string; imageUrl?: string; }; function initials(name: string): string { const parts = name.trim().split(/\s+/).filter(Boolean); if (parts.length === 0) return "?"; if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase(); return `${parts[0][0] ?? ""}${parts[1][0] ?? ""}`.toUpperCase(); } export type PresenceAvatarsProps = { users: PresenceUser[]; /** @default 5 */ maxVisible?: number; className?: string; }; /** * Horizontal stack of collaborator avatars (initials or image) with cursor-colored borders. */ export function PresenceAvatars({ users, maxVisible = 5, className, }: PresenceAvatarsProps) { const visible = users.slice(0, maxVisible); const overflow = Math.max(0, users.length - maxVisible); if (users.length === 0) { return null; } return (
{visible.map((u) => { const border = u.color || colorFromUserName(u.name); return (
{u.imageUrl ? ( // eslint-disable-next-line @next/next/no-img-element ) : ( {initials(u.name)} )}
{u.name}
); })} {overflow > 0 ? (
+{overflow}
) : null}
); } export type ConnectionUiStatus = "connected" | "connecting" | "disconnected"; export type ConnectionStatusProps = { status: ConnectionUiStatus; onRetry?: () => void; className?: string; }; const statusConfig: Record< ConnectionUiStatus, { label: string; dot: string; pulse?: boolean } > = { connected: { label: "Connected", dot: "bg-emerald-500 shadow-[0_0_0_3px_rgba(16,185,129,0.25)]", }, connecting: { label: "Connecting…", dot: "bg-amber-400 shadow-[0_0_0_3px_rgba(251,191,36,0.3)]", pulse: true, }, disconnected: { label: "Disconnected", dot: "bg-red-500 shadow-[0_0_0_3px_rgba(239,68,68,0.25)]", }, }; /** * Compact live connection indicator with optional retry when offline. */ export function ConnectionStatus({ status, onRetry, className, }: ConnectionStatusProps) { const cfg = statusConfig[status]; return (
{cfg.label} {status === "disconnected" && onRetry ? ( ) : null}
); }