// AIPanel.jsx — 전역 AI 어시스턴트 (우측 사이드 패널). 화면 컨텍스트 기반 채팅.
// ---- 컨텍스트 스토어 ----
let _AICTX = { screen: 'IBK 리스크 인사이트', summary: '', facts: [], suggestions: [] };
function setAIContext(c){ _AICTX = { screen:'IBK 리스크 인사이트', summary:'', facts:[], suggestions:[], ...c }; window.dispatchEvent(new CustomEvent('aictx', { detail:_AICTX })); }
function getAIContext(){ return _AICTX; }
Object.assign(window, { setAIContext, getAIContext });
// ---- 스크립트형 폴백 응답 (LLM 미가용 시) ----
function fallbackAnswer(q, ctx){
const f = (ctx.facts||[]).slice(0,4).map(x=>'· '+x).join('\n');
const lead = ctx.summary ? ctx.summary+' ' : '';
return `현재 보고 계신 **${ctx.screen}** 화면을 기준으로 말씀드릴게요.\n\n${lead}주요 수치는 다음과 같아요.\n${f}\n\n질문하신 내용은 이 데이터를 토대로 살펴봤을 때, 위험액 추이와 한도 소진율을 함께 모니터링하시는 것이 좋아요. 더 구체적으로 알고 싶은 항목을 골라주시면 자세히 설명드릴게요.`;
}
async function askAI(q, ctx, history){
try {
const res = await fetch('/api/ai', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ question: q, context: ctx, history: (history||[]).slice(-6) })
});
if (res.ok) {
const data = await res.json();
if (data.answer && data.answer.trim()) return data.answer.trim();
}
} catch(e){ /* fall through to fallback */ }
await new Promise(r=>setTimeout(r, 650));
return fallbackAnswer(q, ctx);
}
// 간단 마크다운(**굵게**, 줄바꿈) 렌더
function RichText({ text }){
const parts = text.split(/(\*\*[^*]+\*\*)/g);
return {parts.map((p,i)=> p.startsWith('**') && p.endsWith('**')
? {p.slice(2,-2)}
: {p})};
}
function Bubble({ m }){
const me = m.role==='user';
return (