Files
llink/js/src/components/composing-indicator.tsx
T
Arjun PatelandGitHub 3d8fa79657 add real-time infrastructure (#137)
* setup infra for pusher service

* setup client sdk for pusher service

* fix: ping parse failure

* fix: send pong back to client

avoid disconnections every 2.5 minutes

* increase replicas

* feat: show presence and compose indicator
2026-04-09 12:08:18 -07:00

48 lines
1.6 KiB
TypeScript

import type { Human } from "@/api/types";
import type { ComposingUser } from "@/features/particles/stream-presence-context";
interface ComposingIndicatorProps {
users: ComposingUser[];
networkHumans?: Human[];
}
/**
* Composing indicators pinned to the left edge, text running bottom-to-top
* via writing-mode so it hugs the edge without transform math issues.
*/
export function ComposingIndicator({
users,
networkHumans,
}: ComposingIndicatorProps) {
if (users.length === 0) return null;
return (
<div
className="z-100 absolute left-2 top-1/2 z-20 flex -translate-y-1/2 flex-col gap-1.5 animate-in fade-in duration-200"
style={{ writingMode: "vertical-rl" }}
>
{users.map((u) => {
const human = networkHumans?.find((h) => h.id === u.humanId);
const name = human?.email_prefix ?? u.humanId;
const modeLabel = u.mode === "typing" ? "typing" : "recording";
return (
<div
key={u.humanId}
className="flex rotate-180 items-center gap-1.5 rounded-full bg-white/10 px-2 py-1 backdrop-blur-sm"
>
<span className="flex gap-0.5">
<span className="size-1 rounded-full bg-white/70 animate-bounce [animation-delay:0ms]" />
<span className="size-1 rounded-full bg-white/70 animate-bounce [animation-delay:150ms]" />
<span className="size-1 rounded-full bg-white/70 animate-bounce [animation-delay:300ms]" />
</span>
<span className="whitespace-nowrap text-[10px] text-white/50">
{name} {modeLabel}
</span>
</div>
);
})}
</div>
);
}