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)