import { useAIChat, useAILookup } from '@deplixo/sdk'; import Setup from './components/Setup.jsx'; import Conversation from './components/Conversation.jsx'; import Portrait from './components/Portrait.jsx'; import { GUIDES, DEPTHS } from './components/config.js'; function App() { const [stage, setStage] = useState('setup'); // 'setup' | 'conversation' | 'generating' | 'portrait' const [guide, setGuide] = useState(null); const [depth, setDepth] = useState(null); const [transcript, setTranscript] = useState([]); // captured conversation const [portrait, setPortrait] = useState(null); const [genError, setGenError] = useState(null); const lookup = useAILookup({ example: { portrait: 'A few paragraphs of prose...', values: [ { name: 'Fidelity', description: 'How this value showed up in the user’s specific answers.', tradition: 'Stoic', philosophical_note: 'A brief explanation of how this tradition thinks about this value.', }, ], closing_line: 'A final sentence from the guide.', }, }); const beginConversation = () => { if (guide && depth) setStage('conversation'); }; const handleConcluded = async (messages) => { setTranscript(messages); setStage('generating'); setGenError(null); const guideObj = GUIDES.find((g) => g.id === guide); const depthObj = DEPTHS.find((d) => d.id === depth); const transcriptText = messages .map((m) => `${m.role === 'user' ? 'Seeker' : guideObj.name}: ${m.content}`) .join('\n\n'); const prompt = `You are ${guideObj.name}, the philosophical guide. You have just completed a ${depthObj.label} conversation with a seeker about their values and inner life. Here is the full conversation: ${transcriptText} Now, write a Values Portrait in your distinctive voice (${guideObj.voiceShort}). Requirements: - portrait: 2–4 paragraphs of prose in your voice (${guideObj.name}'s voice — ${guideObj.voiceShort}). Reflect back what emerged. Reference SPECIFIC details, people, situations, or phrases the seeker actually mentioned. No generic platitudes. - values: A list of values that genuinely emerged from THIS conversation. You decide how many fit (could be 3, could be 7). For each value: - name: A single word or short phrase - description: 1–2 sentences explaining how THIS value showed up in THIS seeker's specific answers — quote or paraphrase what they said - tradition: One of: Stoic, Socratic, Aristotelian, Jungian, Confucian, Existentialist, Epicurean, or another classical tradition that fits - philosophical_note: A brief explanation (2–3 sentences) of how that tradition thinks about this value - closing_line: A final sentence, in your voice, to send the seeker on their way. Return only the structured object.`; try { const result = await lookup.run(prompt); setPortrait(result); setStage('portrait'); } catch (err) { setGenError(err?.message || 'Something went wrong generating the portrait.'); setStage('conversation'); } }; const startOver = () => { setStage('setup'); setGuide(null); setDepth(null); setTranscript([]); setPortrait(null); setGenError(null); }; const guideObj = guide ? GUIDES.find((g) => g.id === guide) : null; const depthObj = depth ? DEPTHS.find((d) => d.id === depth) : null; // Apply guide accent as a CSS variable on the root container const accentStyle = guideObj ? { '--guide-accent': guideObj.accent, '--guide-accent-tint': guideObj.accentTint } : {}; return (
{stage === 'setup' && ( )} {(stage === 'conversation' || stage === 'generating') && guideObj && depthObj && ( )} {stage === 'portrait' && portrait && guideObj && ( )}
); } ReactDOM.createRoot(document.getElementById('root')).render();