/* 아리 — 라이프 케어(Life Care) 건강·웰니스 / 금융·소비 / 지식·학습(제2의 뇌) 아리가 각 영역의 데이터를 읽고 분석·코칭·리마인더를 미리 준비해 둔다. */ const { useState, useEffect, useRef, useMemo } = React; const L = window.LifeData; /* ---------------- 아이콘 ---------------- */ const P = { spark: "M12 3l1.9 4.6L18.5 9.5l-4.6 1.9L12 16l-1.9-4.6L5.5 9.5l4.6-1.9z|M19 13l.7 1.8 1.8.7-1.8.7-.7 1.8-.7-1.8-1.8-.7 1.8-.7z", grid: "M3 3h7v7H3z|M14 3h7v7h-7z|M14 14h7v7h-7z|M3 14h7v7H3z", inbox: "M22 12h-6l-2 3h-4l-2-3H2|M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z", route: "M6 19a3 3 0 1 0 0-6 3 3 0 0 0 0 6z|M18 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6z|M9 16h6a3 3 0 0 0 3-3V11", cal: "M8 2v4M16 2v4M3 10h18M5 4h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z", check: "M9 11l3 3L22 4|M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11", mail: "M22 7 13.03 12.7a1.94 1.94 0 0 1-2.06 0L2 7|M4 4h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z", brain: "M12 5a3 3 0 1 0-5.9.8A3 3 0 0 0 4 9.5 3 3 0 0 0 6 14a3 3 0 0 0 6 1zM12 5a3 3 0 1 1 5.9.8A3 3 0 0 1 20 9.5 3 3 0 0 1 18 14a3 3 0 0 1-6 1z|M12 5v14", heart: "M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7z", wallet: "M19 7V5a2 2 0 0 0-2-2H5a2 2 0 0 0 0 4h15a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1M3 5v14a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-4|M18 12a1 1 0 0 0 0 2h3v-2z", search: "M21 21l-4.34-4.34|M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16z", bell: "M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9|M10.3 21a1.94 1.94 0 0 0 3.4 0", sun: "M12 17a5 5 0 1 0 0-10 5 5 0 0 0 0 10z|M12 1v2M12 21v2M4.2 4.2l1.4 1.4M18.4 18.4l1.4 1.4M1 12h2M21 12h2M4.2 19.8l1.4-1.4M18.4 5.6l1.4-1.4", moon: "M21 12.8A9 9 0 1 1 11.2 3 7 7 0 0 0 21 12.8z", plus: "M12 5v14M5 12h14", x: "M18 6 6 18M6 6l12 12", chev: "M9 6l6 6-6 6", arrow: "M5 12h14|M13 6l6 6-6 6", more: "M5 12a1 1 0 1 0 2 0 1 1 0 0 0-2 0zM11 12a1 1 0 1 0 2 0 1 1 0 0 0-2 0zM17 12a1 1 0 1 0 2 0 1 1 0 0 0-2 0z", refresh: "M21 12a9 9 0 1 1-3-6.7|M21 3v5h-5", tick: "M20 6 9 17l-5-5", clock: "M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18z|M12 7v5l3 2", zap: "M13 2 3 14h9l-1 8 10-12h-9z", send: "M22 2 11 13|M22 2 15 22l-4-9-9-4z", steps: "M4 16v-2.38C4 11.5 2.97 10.5 3 8c.03-2.72 1.49-6 4.5-6C9.37 2 10 3.8 10 5.5c0 3.11-2 5.66-2 8.68V16a2 2 0 1 1-4 0zM20 20v-2.38c0-2.12 1.03-3.12 1-5.62-.03-2.72-1.49-6-4.5-6C14.63 6 14 7.8 14 9.5c0 3.11 2 5.66 2 8.68V20a2 2 0 1 0 4 0z", flame: "M8.5 14.5A2.5 2.5 0 0 0 11 17a2.5 2.5 0 0 0 2.5-2.5c0-1.38-.5-2-1-3-1.07-2.14-.22-4.05 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.15.43-2.29 1-3a2.5 2.5 0 0 0 2.5 2.5z", water: "M12 2.7S6 9.5 6 14a6 6 0 0 0 12 0c0-4.5-6-11.3-6-11.3z", stretch: "M22 12h-4l-3 9L9 3l-3 9H2", pill: "M10.5 20.5 3.5 13.5a5 5 0 0 1 7-7l7 7a5 5 0 0 1-7 7z|M8.5 8.5l7 7", feather: "M20.24 12.24a6 6 0 0 0-8.49-8.49L5 10.5V19h8.5z|M16 8 2 22|M17.5 15H9", food: "M3 2v7c0 1.1.9 2 2 2a2 2 0 0 0 2-2V2M5 2v20|M21 15V2a5 5 0 0 0-5 5v6c0 1.1.9 2 2 2h3zm0 0v7", bag: "M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z|M3 6h18|M16 10a4 4 0 0 1-8 0", car: "M5 17a2 2 0 1 0 4 0 2 2 0 0 0-4 0zM15 17a2 2 0 1 0 4 0 2 2 0 0 0-4 0z|M5 17H3v-4l2-5h11l3 5v4h-2M5 13h14", coffee: "M17 8h1a4 4 0 1 1 0 8h-1|M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4z|M6 2v2M10 2v2M14 2v2", repeat: "M17 2l4 4-4 4M3 11V9a4 4 0 0 1 4-4h14M7 22l-4-4 4-4M21 13v2a4 4 0 0 1-4 4H3", ticket: "M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2z|M13 5v2M13 17v2M13 11v2", play: "M6 3l15 9-15 9z", music: "M9 18V5l12-2v13|M6 18a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM18 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6z", alert: "M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h16.9a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z|M12 9v4M12 17h.01", trending: "M22 7l-8.5 8.5-5-5L2 17|M16 7h6v6", article: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z|M14 2v6h6|M9 13h6M9 17h5", note: "M12 20h9|M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4z", idea: "M9 18h6|M10 22h4|M12 2a7 7 0 0 0-4 12.7c.6.5 1 1.3 1 2.1V17h6v-.2c0-.8.4-1.6 1-2.1A7 7 0 0 0 12 2z", highlight: "M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z", folder: "M3 7a2 2 0 0 1 2-2h4l2 2.5h8a2 2 0 0 1 2 2V18a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z", link: "M9 17H7A5 5 0 0 1 7 7h2M15 7h2a5 5 0 0 1 0 10h-2M8 12h8", layers: "M12 2 2 7l10 5 10-5z|M2 17l10 5 10-5M2 12l10 5 10-5", }; function I({ d, w }) { return ( {P[d].split("|").map((s, i) => )} ); } const MAIN = [ { id: "dash", label: "대시보드", icon: "grid", href: "대시보드.html" }, { id: "sbox", label: "인박스", icon: "inbox", href: "인박스.html" }, { id: "appr", label: "결재함", icon: "spark", badge: "3", href: "결재함.html" }, { id: "auto", label: "자동화", icon: "zap", href: "자동화.html" }, { id: "journey", label: "여정", icon: "route", href: "여정.html" }, { id: "cal", label: "일정", icon: "cal", href: "일정.html" }, { id: "task", label: "작업", icon: "check", badge: "4", href: "작업.html" }, { id: "mail", label: "메일", icon: "mail", href: "메일.html" }, { id: "noti", label: "알림", icon: "bell", badge: "6", href: "알림.html" }, { id: "research", label: "리서치", icon: "brain", href: "리서치.html" }, { id: "trip", label: "여행", icon: "send", href: "여행.html" }, { id: "life", label: "라이프", icon: "heart", href: "라이프.html" }, { id: "wrap", label: "하루 마감", icon: "moon", href: "하루 마감.html" }, ]; const MODULES = [ { id: "today", label: "오늘의 라이프", sub: "통합 코칭", icon: "spark", tone: "coral" }, { id: "health", label: "건강 & 웰니스", sub: "활동·수면·습관", icon: "heart", tone: "coral" }, { id: "finance", label: "금융 & 소비", sub: "예산·구독·자산", icon: "wallet", tone: "blue" }, { id: "knowledge", label: "지식 & 학습", sub: "제2의 뇌", icon: "brain", tone: "violet" }, ]; function load(k, fb) { try { const v = localStorage.getItem("ariL." + k); return v === null ? fb : JSON.parse(v); } catch (e) { return fb; } } function save(k, v) { try { localStorage.setItem("ariL." + k, JSON.stringify(v)); } catch (e) {} } const won = (n) => "₩" + n.toLocaleString("ko-KR"); /* ===================== 액티비티 링 ===================== */ function Ring({ r }) { return (
{r.val}
{r.label}{r.unit && {r.unit}}
목표 {r.goal}
); } /* ===================== 건강 모듈 ===================== */ function HealthModule({ habits, onHabit, toast }) { const h = L.health; return ( <>

건강 & 웰니스

Apple Health · Apple Watch 연동 · 오늘 {new Date().getHours()}시 기준
toast("건강 리포트를 생성하고 있어요", "coral")}>주간 리포트
아리의 건강 코칭

오늘의 활동목표까지 조금만 더
{h.rings.map((r) => )}
바이탈웨어러블 측정값
{h.vitals.map((v) => (
{v.label}{v.note}
{v.val}{v.unit && {v.unit}}
))}
수면 분석어젯밤 · 7일 추이
{h.sleep.score}점수
{h.sleep.total}
권장보다 18분 부족
{h.sleep.stages.map((s) => ( ))}
{h.sleep.stages.map((s) => ( {s.label} {s.pct}% ))}
{h.sleep.week.map((v, i) => (
= 86 ? " peak" : "")} style={{ height: v + "%" }} title={v + "점"} /> ))}
{["월", "화", "수", "목", "금", "토", "일"].map((d) => {d})}
{h.sleep.note}
건강한 습관리마인더 자동 설정됨
추가
{habits.map((hb) => { const done = hb.done >= hb.total; return (
{hb.title}{hb.goal}
{hb.streak}일
{hb.done}/{hb.total}
{hb.at}
); })}
); } /* ===================== 금융 모듈 ===================== */ function FinanceModule({ toast, paused, onPause }) { const f = L.finance; const b = f.budget; return ( <>

금융 & 소비

우리카드 · 토스 · 카카오뱅크 연동 · 6월 예산
toast("이번 달 가계부를 정리하고 있어요", "blue")}>월간 정산
아리의 소비 코칭

이달 예산{b.daysLeft}일 남음
{won(b.spent)} / {won(b.limit)}
지난달 대비 {Math.abs(b.deltaPct)}% 적게 썼어요
{b.pct}% 사용남은 예산 {won(b.limit - b.spent)}
{f.insights.map((ins, i) => (
{ins.title}

{ins.body}

))}
자동 분류된 지출아리가 거래내역을 카테고리로 정리했어요
{f.cats.map((c) => (
{c.name}{c.over && 예산 빠름}
{won(c.amt)}{c.pct}%
))}
구독 결제 예정결제일 전에 미리 알려드려요
알림 켜짐
{f.subs.map((s) => { const isPaused = paused[s.name]; return (
{s.name}{s.note}{isPaused ? " · 일시정지됨" : ""}
{s.date}
3 ? " far" : "")}>{isPaused ? "정지" : s.inDays + "일 뒤"}
{won(s.amt)}
); })}
); } /* ===================== 지식 모듈 (제2의 뇌) ===================== */ function KnowledgeModule({ toast }) { const k = L.knowledge; const [q, setQ] = useState(""); const [answer, setAnswer] = useState(null); const ask = (text) => { if (!text.trim()) return; setQ(text); setAnswer({ q: text, body: "저장하신 자료를 종합하면 — 검증 가능한 작은 단계로 분해하고, 계획·실행·반성을 분리할 때 에이전트 신뢰성이 가장 크게 올라갑니다. ‘분기 회고’ 메모의 리텐션 +6%p 결과와도 연결돼요. 온보딩 자동화에 오케스트레이터-워커 패턴을 먼저 적용해보길 추천드려요.", sources: ["AI 에이전트 설계 패턴", "분기 회고 메모", "재무 자동화 시스템"], }); }; const filtered = useMemo(() => { const s = q.trim().toLowerCase(); if (!s || answer) return k.items; return k.items.filter((it) => (it.title + it.excerpt + it.tags.join(" ")).toLowerCase().includes(s)); }, [q, answer]); return ( <>

지식 & 학습 · 제2의 뇌

{k.stats.items.toLocaleString()}개 저장 · 이번 주 +{k.stats.thisWeek} · {k.stats.collections}개 컬렉션
toast("새 항목을 스크랩할 준비가 됐어요", "violet")}>스크랩
{ setQ(e.target.value); if (answer) setAnswer(null); }} onKeyDown={(e) => e.key === "Enter" && ask(q)} placeholder="저장한 모든 것에 자연어로 물어보세요…" />
{k.suggested.map((s, i) => ( ))}
{answer && (
"{answer.q}"
{answer.sources.map((s, i) => {s})}
)}

{answer ? "관련 자료" : q.trim() ? "검색 결과" : "최근 스크랩"}

자동 태깅됨
{filtered.map((it) => (
{it.title}
{it.src}{it.time}
{({ article: "아티클", note: "메모", idea: "아이디어", highlight: "하이라이트" })[it.type]}

{it.excerpt}

아리 — " + it.ai }} />

{it.tags.map((t, i) => {t})}
))} {filtered.length === 0 &&
‘{q}’에 해당하는 자료가 없어요. 다르게 물어보거나 새로 스크랩해 보세요.
}
컬렉션주제별 자동 묶음
{k.collections.map((c) => ( ))}
); } /* ===================== 오늘 (통합) ===================== */ function TodayModule({ go, habits, toast }) { const h = L.health, f = L.finance, k = L.knowledge; const nextSub = f.subs[0]; const pendingHabits = habits.filter((x) => x.done < x.total); return ( <>
아리 라이프 코치 · 오늘 아침

오늘의 컨디션은 좋아요. 활동만 조금 더 챙기면 완벽해요 ☀️

어젯밤 7시간 12분 푹 주무셨고 수면 점수는 86점이에요. 다만 어제 물 섭취가 절반에 그쳤으니 오후에 리마인더를 넣어뒀어요. 이번 달 소비는 지난달보다 8% 적고 순항 중이지만, 이틀 뒤 Netflix·Spotify 결제가 함께 빠져나가요. 저장하신 ‘AI 에이전트 설계’ 글은 오후 미팅과 연결돼 요약해뒀어요.

{/* 건강 스냅샷 */}
go("health")}>
건강 & 웰니스활동·수면·습관
{h.rings.map((r) => )}
물 마시기 5/8잔 · 오후 3시 리마인더가 예정돼 있어요.
{/* 금융 스냅샷 */}
go("finance")}>
금융 & 소비6월 예산
{won(f.budget.spent)} / {won(f.budget.limit)}
지난달보다 8% 절약
{nextSub.name}다음 결제
{nextSub.date}
{nextSub.inDays}일 뒤
{won(nextSub.amt)}
{/* 지식 스냅샷 */}
go("knowledge")}>
지식 & 학습제2의 뇌
{ e.stopPropagation(); go("knowledge"); }}> 저장한 것에 물어보기…
{k.items.slice(0, 2).map((it) => (
{it.title} {it.src} · {it.time}
))}
{/* 오늘의 리마인더 */}
오늘의 리마인더아리가 챙기고 있는 것들
{pendingHabits.map((hb) => (
{hb.title}

{hb.at} · {hb.done}/{hb.total} 완료

))}
구독 결제 임박

{nextSub.date} {nextSub.name} {won(nextSub.amt)} 결제 예정

); } /* ===================== 앱 ===================== */ function App() { const [theme, setTheme] = useState(() => load("theme", "light")); const [mod, setMod] = useState(() => load("mod", "today")); const [habits, setHabits] = useState(() => load("habits", L.health.habits)); const [paused, setPaused] = useState(() => load("paused", {})); const [toasts, setToasts] = useState([]); const shellRef = useRef(null); const tid = useRef(0); useEffect(() => { document.documentElement.setAttribute("data-theme", theme); save("theme", theme); }, [theme]); useEffect(() => save("mod", mod), [mod]); useEffect(() => save("habits", habits), [habits]); useEffect(() => save("paused", paused), [paused]); useEffect(() => { const el = shellRef.current; const id = requestAnimationFrame(() => el && el.classList.add("entered")); return () => cancelAnimationFrame(id); }, []); const toast = (msg, tone) => { const id = ++tid.current; setToasts((t) => [...t, { id, msg, tone }]); setTimeout(() => setToasts((t) => t.filter((x) => x.id !== id)), 2500); }; const onHabit = (id) => { setHabits((hs) => hs.map((h) => h.id === id && h.done < h.total ? { ...h, done: h.done + 1 } : h)); toast("습관을 기록했어요", "green"); }; const onPause = (name) => { setPaused((p) => ({ ...p, [name]: !p[name] })); toast(paused[name] ? "구독을 다시 활성화했어요" : "구독을 일시정지했어요", paused[name] ? "green" : "coral"); }; return (
{/* 상단바 */}
아리AI LIFE OS
{/* 좌측: 모듈 + 연결 소스 */} {/* 본문 */}
{mod === "today" && } {mod === "health" && } {mod === "finance" && } {mod === "knowledge" && }
{toasts.map((t) => (
{t.msg}
))}
); } ReactDOM.createRoot(document.getElementById("root")).render();