Character schema v2: background, dialogue_style, appearance, skills, gaze_presets with automatic v1→v2 migration. LLM-assisted character creation via Character MCP server. Two-tier memory system (personal per-character + general shared) with budget-based injection into LLM system prompt. Per-character TTS voice routing via state file — Wyoming TTS server reads active config to route between Kokoro (local) and ElevenLabs (cloud PCM 24kHz). Dashboard: memories page, conversation history, character profile on cards, auto-TTS engine selection from character config. Also includes VTube Studio expression bridge and ComfyUI API guide. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
42 lines
1.5 KiB
JavaScript
42 lines
1.5 KiB
JavaScript
import { useEffect, useRef } from 'react'
|
|
import MessageBubble from './MessageBubble'
|
|
import ThinkingIndicator from './ThinkingIndicator'
|
|
|
|
export default function ChatPanel({ messages, isLoading, onReplay, character }) {
|
|
const bottomRef = useRef(null)
|
|
const name = character?.name || 'AI'
|
|
const image = character?.image || null
|
|
|
|
useEffect(() => {
|
|
bottomRef.current?.scrollIntoView({ behavior: 'smooth' })
|
|
}, [messages, isLoading])
|
|
|
|
if (messages.length === 0 && !isLoading) {
|
|
return (
|
|
<div className="flex-1 flex items-center justify-center">
|
|
<div className="text-center">
|
|
{image ? (
|
|
<img src={image} alt={name} className="w-20 h-20 rounded-full object-cover mx-auto mb-4 ring-2 ring-indigo-500/30" />
|
|
) : (
|
|
<div className="w-20 h-20 rounded-full bg-indigo-600/20 flex items-center justify-center mx-auto mb-4">
|
|
<span className="text-indigo-400 text-2xl">{name[0]}</span>
|
|
</div>
|
|
)}
|
|
<h2 className="text-xl font-medium text-gray-200 mb-2">Hi, I'm {name}</h2>
|
|
<p className="text-gray-500 text-sm">Type a message or press the mic to talk</p>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="flex-1 overflow-y-auto py-4">
|
|
{messages.map((msg) => (
|
|
<MessageBubble key={msg.id} message={msg} onReplay={onReplay} character={character} />
|
|
))}
|
|
{isLoading && <ThinkingIndicator character={character} />}
|
|
<div ref={bottomRef} />
|
|
</div>
|
|
)
|
|
}
|