mirror of
https://github.com/yusufipk/YTChatHub.git
synced 2026-09-11 10:56:17 +00:00
Backend: one SSE stream for messages, selection, poll and connection status; reconnect with backoff; no on-disk Innertube session cache; mock only with MOCK_CHAT=1; binds to 127.0.0.1; serves client/out so production runs on one port. Full URLs for links YouTube truncates in chat. Client: components split out, Lucide icons, stream title and status, on-air strip, search, pause, keyboard shortcuts, overlay settings dialog. Overlay themes, position, size, animation and auto-hide via URL, sized for 1080p and scaled with the source. New blueprint theme for the brand background. Removed Tailwind, better-sqlite3, zod, the timezone helpers, Cline memory bank and AGENTS.md. Added ESLint, node:test tests and CI.
37 lines
1.4 KiB
TypeScript
37 lines
1.4 KiB
TypeScript
'use client';
|
|
|
|
import { X } from 'lucide-react';
|
|
import type { ChatMessage } from '@shared/chat';
|
|
import { proxyImageUrl } from '../lib/imageProxy';
|
|
import { formatAmount } from '../lib/format';
|
|
import { MessageBody } from './MessageBody';
|
|
|
|
type Props = { selection: ChatMessage | null; onClear: () => void };
|
|
|
|
/** Mirrors what the OBS overlay is showing right now, so the operator never has to guess. */
|
|
export function OnAir({ selection, onClear }: Props) {
|
|
return (
|
|
<section className={`onair ${selection ? 'onair--live' : ''}`}>
|
|
<span className="onair__label">On air</span>
|
|
{selection ? (
|
|
<>
|
|
{selection.authorPhoto && <img src={proxyImageUrl(selection.authorPhoto)} alt="" className="avatar avatar--sm" />}
|
|
<span className="onair__author">{selection.author}</span>
|
|
{selection.superChat && (
|
|
<span className="card__amount" style={{ background: selection.superChat.color }}>
|
|
{formatAmount(selection.superChat.amount, selection.superChat.currency)}
|
|
</span>
|
|
)}
|
|
<MessageBody message={selection} className="onair__text" />
|
|
<button type="button" className="btn btn--sm" onClick={onClear} title="Clear the overlay (Esc)">
|
|
<X size={13} />
|
|
Clear
|
|
</button>
|
|
</>
|
|
) : (
|
|
<span className="onair__hint">Nothing on the overlay. Click a message to show it, click again to hide it.</span>
|
|
)}
|
|
</section>
|
|
);
|
|
}
|