/** * StreamMessage — single message card in the stream */ import { Component, Show, createMemo } from "solid-js"; import type { StreamMessage as StreamMessageType } from "./registry"; import { getMessageType } from "./registry"; import { revertLatest, useJournalStream } from "../stores/journalStream"; export const StreamMessageCard: Component<{ message: StreamMessageType; }> = (props) => { const stream = useJournalStream(); const def = createMemo(() => getMessageType(props.message.type)); const rendered = createMemo(() => def()?.render?.(props.message.payload, props.message as any), ); const time = createMemo(() => { const d = new Date(props.message.timestamp); return d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }); }); const isMine = () => props.message.sender === stream.myName; const latestSeq = () => stream.senderSeq[stream.myName] ?? 0; const canRevertThis = () => isMine() && props.message.seq === latestSeq() && !props.message.reverted; const senderClass = () => props.message.sender === "gm" ? "text-purple-600 font-semibold" : "text-blue-600 font-semibold"; return ( } >
{/* Header */}
{props.message.sender} {time()} {props.message.type}
{/* Body */}
} > {rendered()}
); }; /** Fallback: render payload as formatted JSON */ const JsonBody: Component<{ payload: unknown }> = (props) => { const text = () => JSON.stringify(props.payload, null, 2); return (
      {text()}
    
); }; /** Reverted card: struck-through, grey */ const RevealedCard: Component<{ msg: StreamMessageType }> = (props) => { return (
{props.msg.sender} — 已撤回
); };