Renders a single Slack-channel-style message row used across both the OpenMSP community feed and ticket conversation surfaces, ensuring pixel-identical layout on both. ## Key Components ### `ChatMessageRow` The primary message row component with avatar, display name, relative timestamp, body text, and an optional footer slot. Handles avatar proxying via `useProxiedImageUrl` and gracefully falls back to an initials box on image load failure, keyed by URL to allow retry on src change. ### `ChatMessageRowSkeleton` A loading placeholder with 1:1 structural parity to `ChatMessageRow` — matching wrapper, avatar box dimensions, and bar heights to prevent layout reflow on the loading-to-loaded transition. ### `ChatMessageRowProps` ```typescript interface ChatMessageRowProps { displayName: string // Bold name in header avatarUrl?: string | null // Proxied; falls back to initials timeLabel?: string | null // Pre-formatted (e.g. "2h ago"); hidden if empty body: string // whitespace-pre-wrap message text footer?: ReactNode // Surface-specific slot (reply badge / attachments) } ``` ## Usage Example ```typescript import { ChatMessageRow, ChatMessageRowSkeleton } from './chat-message-row' // Slack community feed } /> // Ticket conversation feed } /> // While loading ``` ## Notes - Colors use ODS theme tokens exclusively (`text-ods-text-primary`, `text-ods-text-secondary`, `bg-ods-border`) — no raw color values. - Avatar sizing mirrors Slack's layout: `w-8 h-8` (32px) on mobile, `md:w-10 md:h-10` (40px) on desktop, `rounded-lg`, `object-cover`. - The `footer` prop is the **only** intentional surface-level variation point; all other markup is shared verbatim. **Source:** [`flamingo-stack/openframe-oss-lib`](https://github.com/flamingo-stack/openframe-oss-lib/blob/main/chat-message-row.tsx)