Files
Kokorone/apps/web/components/ChatBubble.tsx
jiantw83andClaude Sonnet 5 1bea06e3cd feat(遊戲化): 新增大廳、附身、時間流、後日談進度等遊戲化系統功能
新增角色大廳(lobby)邀約與通話、附身(possession)、時鐘(clock)抽象、
時間流與睡眠負債、後日談進度、行為選擇與旁觀反應等模組,並補齊對應冒煙
測試(R~V)與單元測試;同步調整 web/mobile 對應頁面與元件。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 09:10:26 +08:00

53 lines
1.7 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { VoicePlaybackButton } from "./VoicePlaybackButton";
interface ChatBubbleProps {
role: "user" | "character";
text: string;
characterId?: string;
userId?: string;
}
// H-3 動作描寫標記渲染:把 *…* 標記以暮空紫小字呈現,與台詞區分。
function renderContent(text: string) {
const parts = text.split(/(*[^*]+*)/g).filter(Boolean);
return parts.map((part, index) => {
const match = part.match(/^*([^*]+)*$/);
if (match) {
return (
<span key={index} className="text-xs" style={{ color: "var(--violet)" }}>
{match[1]}
</span>
);
}
return <span key={index}>{part}</span>;
});
}
export function ChatBubble({ role, text, characterId, userId }: ChatBubbleProps) {
const isUser = role === "user";
return (
<div className="flex max-w-[78%] flex-col" style={{ alignSelf: isUser ? "flex-end" : "flex-start", alignItems: isUser ? "flex-end" : "flex-start" }}>
<div
className="rounded-2xl px-3.5 py-2 text-sm leading-relaxed"
style={
isUser
? {
background: "var(--accent-bright)",
color: "#fff",
borderBottomRightRadius: 4,
}
: {
background: "var(--bg-soft)",
border: "1px solid var(--line)",
borderBottomLeftRadius: 4,
}
}
>
{renderContent(text)}
</div>
{/* P-6:語音播放介面只在角色訊息旁提供,使用者自己的訊息不需要播放。 */}
{!isUser && characterId && userId && <VoicePlaybackButton characterId={characterId} userId={userId} text={text} />}
</div>
);
}