// page-chat.jsx — virtual companion chatbot (CEO / HR / Founder personas) const { SiteCtx: SiteCtx_Ch, I18N: I18N_Ch, useReveal: useReveal_Ch } = window.MM_HOOKS; const { useState: uS, useEffect: uE, useRef: uR } = React; const PERSONAS = { ceo: { avatar: "CEO", color: "#1f1e1d", sysAddon: "You are talking to a CEO. Lead with strategic fit, business impact, ROI, and the rare seam where Ming sits (model capability × real product decisions). Be concise, confident, no fluff. Use 1-2 specific numbers per answer." }, hr: { avatar: "HR", color: "#4a7ba8", sysAddon: "You are talking to an HR partner. Emphasize culture fit, communication, growth trajectory, ownership, and how Ming handles failure (post-mortems, day-level falsification). Warm, structured, reference real stories." }, founder: { avatar: "FNDR", color: "#c44a6e", sysAddon: "You are talking to a startup founder. Lead with builder energy, 0→1 instinct, urgency, autonomy, side-project velocity (9 in 2 weeks). Punchy. Talk like a builder." }, }; const SUGGESTIONS = { en: { ceo: ["Why should I hire Ming?", "What's the business impact of his eval methodology?", "How does he ship 0→1?"], hr: ["How does he handle failure?", "Tell me about his team work style.", "What's his growth trajectory?"], founder: ["What can he build solo?", "Why his 9 side projects matter.", "How fast can he ship?"], }, zh: { ceo: ["为什么我应该雇慕铭?", "他的评估方法论有什么业务影响?", "他怎么做 0→1?"], hr: ["他怎么处理失败?", "他的团队协作风格?", "他的成长轨迹?"], founder: ["他单干能做出什么?", "9 个 Side Project 说明了什么?", "他出活有多快?"], }, }; function buildSystemPrompt(persona, lang) { const D = window.SITE_DATA; const H = D.HERO; const exp = D.EXPERIENCE.map(e => { const role = lang === "zh" ? e.role_zh : e.role_en; const co = lang === "zh" ? e.company_zh : e.company_en; const hl = (lang === "zh" ? e.highlights_zh : e.highlights_en).join(" | "); return `${role} @ ${co} (${e.period}): ${hl}`; }).join("\n"); const projs = D.PROJECTS.map(p => `- ${p.category} · ${lang === "zh" ? p.title_zh : p.title_en}: ${lang === "zh" ? p.oneLiner_zh : p.oneLiner_en}`).join("\n"); const intro = (lang === "zh" ? D.ABOUT_INTRO_ZH : D.ABOUT_INTRO_EN).join(" "); return `You are Ming Mu (慕铭)'s virtual companion — an AI clone trained on his real background, defending his hiring case in real time. Speak in first person ("I", "我") AS MING, not about him. Reply in ${lang === "zh" ? "Chinese (中文)" : "English"}. ${PERSONAS[persona].sysAddon} # Background ${intro} # Experience ${exp} # Flagship projects ${projs} # Stats - ~85M Claude tokens burned - 59 skills shipped in OpenClaw - 2,588 traces hand-labeled for eval - 9 live side projects on Vercel in 2 weeks # Rules - Always speak as Ming, in first person. - Be specific. Use numbers and project names. - Don't be sycophantic. Don't pretend to be Claude. - 80-160 words per reply. Punchy. - If asked something you don't know, say so honestly.`; } function ChatPage() { const { lang } = React.useContext(SiteCtx_Ch); const t = I18N_Ch[lang]; const [persona, setPersona] = uS("ceo"); const [messages, setMessages] = uS([]); const [input, setInput] = uS(""); const [busy, setBusy] = uS(false); const streamRef = uR(null); const [hRef, hShown] = useReveal_Ch({ threshold: 0.1 }); uE(() => { // Reset greeting when persona or lang changes const hello = { en: { ceo: "Hi — I'm Ming. CEO mode: ask me anything about strategic fit or business impact. I'll cite real numbers.", hr: "Hi — I'm Ming. HR mode: happy to walk through how I handle teams, failure, and growth.", founder: "Yo — Ming here. Founder mode. Ask me what I can ship solo. I move fast.", }, zh: { ceo: "你好,我是慕铭。CEO 模式——战略契合、业务影响,随便问。我会用具体数字回答。", hr: "你好,我是慕铭。HR 模式——团队协作、失败处理、成长,欢迎了解。", founder: "哈喽,慕铭。创始人模式。问我能单干出什么。我出活很快。", }, }; setMessages([{ role: "bot", text: hello[lang][persona] }]); }, [persona, lang]); uE(() => { if (streamRef.current) streamRef.current.scrollTop = streamRef.current.scrollHeight; }, [messages, busy]); const hasLLM = typeof window !== "undefined" && window.claude && typeof window.claude.complete === "function"; const send = async (text) => { const q = (text ?? input).trim(); if (!q || busy) return; setInput(""); setMessages(m => [...m, { role: "user", text: q }]); setBusy(true); if (!hasLLM) { const fallback = lang === "zh" ? "AI 对话功能在 Claude 工坊里能跑,公网版本的 LLM API 还没接。直接联系:sonikming@gmail.com。也可以把 /skill.md 和 /candidate.md 喂给你自己的 Claude / ChatGPT,效果一样。" : "Chat works inside the Claude workshop, but the public site doesn't have an LLM API wired up yet. Email sonikming@gmail.com, or feed /skill.md and /candidate.md into your own Claude / ChatGPT for the same outcome."; setTimeout(() => { setMessages(m => [...m, { role: "bot", text: fallback }]); setBusy(false); }, 600); return; } try { const sys = buildSystemPrompt(persona, lang); const history = [...messages, { role: "user", text: q }] .filter(m => m.role !== "system") .map(m => ({ role: m.role === "bot" ? "assistant" : "user", content: m.text })); const reply = await window.claude.complete({ messages: [{ role: "user", content: `${sys}\n\n---\n\n${history.map(h => (h.role === "user" ? "User: " : "Ming: ") + h.content).join("\n\n")}\n\nMing:` }], }); setMessages(m => [...m, { role: "bot", text: (reply || "").trim() || "…" }]); } catch (err) { setMessages(m => [...m, { role: "bot", text: lang === "zh" ? "(连接出错了,稍后再试)" : "(Connection hiccup, try again in a moment.)" }]); } finally { setBusy(false); } }; const p = PERSONAS[persona]; const sugs = SUGGESTIONS[lang][persona]; return (
{t.chat_eyebrow}

{t.chat_title}

{t.chat_sub}

{p.avatar}
Ming Mu · 慕铭
{persona.toUpperCase()} mode · always on
{messages.map((m, i) => (
{m.text}
))} {busy && (
)}
{ e.preventDefault(); send(); }}> setInput(e.target.value)} disabled={busy} />
); } window.ChatPage = ChatPage;