/* 아리 — 자동화 규칙 빌더 페이지
좌측 레일로 하위 화면 전환: 내 규칙 · 새 자동화 · 아리 제안 · 실행 기록 */
const { useState } = React;
const I = window.AriShell.Icon;
const Topbar = window.AriShell.Topbar;
const SubRail = window.AriShell.SubRail;
const AU = window.AriAuto;
const AU_TONE = { mail: "var(--blue)", cal: "var(--violet)", focus: "var(--coral)", life: "var(--green)" };
const AU_CAT = { mail: "메일", cal: "일정", focus: "집중", life: "라이프" };
function Flow({ p, small }) {
return (
트리거{p.trigger}
{p.cond && (
조건{p.cond}
)}
동작{p.action}
);
}
/* ===== 내 규칙 ===== */
function RulesView({ rules, toggle }) {
const activeN = rules.filter((r) => r.on).length;
return (
{activeN}/{rules.length} 켜짐
{rules.map((r) => (
{r.name}
{AU_CAT[r.cat]}
마지막 실행 {r.last}
·
이번 주 {r.runs}회
))}
되돌릴 수 있어요모든 자동 실행은 결재함에 기록되고 한 번에 되돌릴 수 있어요.
위험한 건 물어봐요보내기·결제·삭제는 규칙이 있어도 실행 전에 확인받아요.
스스로 배워요반복되는 손길을 발견하면 새 규칙을 먼저 제안해요.
);
}
/* ===== 새 자동화 (작성 플로우) ===== */
function NewView({ onCreate }) {
const [text, setText] = useState("");
const match = AU.examples.find((e) => e.text === text.trim());
return (
새 자동화 만들기
하고 싶은 일을 한 문장으로 적으세요. 트리거·조건·동작은 아리가 찾아요.
setText(e.target.value)}
onKeyDown={(e) => { if (e.key === "Enter" && match) onCreate(match); }}
placeholder="예: 출장 전날엔 저녁 일정 비워줘"
aria-label="새 규칙 문장"
autoFocus
/>
{AU.examples.map((e) => (
))}
{/* 미리보기 */}
미리보기
{match ? "아리가 이렇게 이해했어요" : "문장을 적으면 여기에 규칙이 그려져요"}
{match &&
{AU_CAT[match.cat]}}
{match ? (
{match.name}
만든 규칙은 ‘내 규칙’에서 언제든 끄거나 지울 수 있어요.
) : (
예시 문장을 누르거나 직접 적어보세요. 아리가 트리거 → 조건 → 동작으로 나눠 보여드려요.
)}
);
}
/* ===== 아리 제안 ===== */
function SuggestView({ suggests, accept, dismiss }) {
return (
{suggests.map((s) => (
{s.pattern}
))}
{suggests.length === 0 && (
지금은 새 제안이 없어요. 패턴이 보이면 알려드릴게요.
)}
);
}
/* ===== 실행 기록 ===== */
function LogView() {
return (
오늘 {AU.log.filter((l) => !l.time.startsWith("어제")).length}건
{AU.log.map((l, i) => (
))}
);
}
const AU_RAIL = (suggestN) => [
{ id: "rules", icon: "zap", label: "내 규칙" },
{ id: "new", icon: "plus", label: "새 자동화" },
{ sep: true },
{ id: "suggest", icon: "brain", label: "아리 제안", dot: suggestN > 0 },
{ id: "log", icon: "clock", label: "실행 기록" },
];
const AU_HEAD = {
rules: { eye: ["규칙으로 굴러가는 하루"], title: "내 규칙", em: "켜두면 알아서 굴러가요" },
new: { eye: ["한 문장이면 충분해요"], title: "새 자동화", em: "말하듯 적으면 규칙이 돼요" },
suggest: { eye: ["아리가 지켜보다 발견한 것"], title: "아리 제안", em: "반복되는 일을 자동으로" },
log: { eye: ["조용히 일한 기록"], title: "실행 기록", em: "규칙이 움직인 순간들" },
};
function App() {
const [view, setView] = useState("rules");
const [rules, setRules] = useState(AU.rules);
const [suggests, setSuggests] = useState(AU.suggests);
const [toast, setToast] = useState(null);
const activeN = rules.filter((r) => r.on).length;
const toggle = (id) => setRules(rules.map((r) => (r.id === id ? { ...r, on: !r.on } : r)));
const addRule = (cat, name, parse) => {
setRules((rs) => [
{ id: "new-" + Date.now(), on: true, cat, name, trigger: parse.trigger, cond: parse.cond, action: parse.action, last: "방금 만듦", runs: 0, fresh: true },
...rs,
]);
setToast("‘" + name + "’ 규칙을 켰어요.");
setView("rules");
setTimeout(() => setToast(null), 3200);
};
const createFromExample = (m) => addRule(m.cat, m.name, m.parse);
const accept = (s) => { addRule(s.offer.cat, s.offer.name, s.offer); setSuggests((x) => x.filter((y) => y.id !== s.id)); };
const dismiss = (s) => setSuggests((x) => x.filter((y) => y.id !== s.id));
const h = AU_HEAD[view];
return (
활성 규칙 {activeN}개
이번 주 {AU.stats.runsWeek}회 실행
{AU.stats.saved} 아꼈어요
{h.title} {h.em}
{view === "rules" && }
{view === "new" && }
{view === "suggest" && }
{view === "log" && }
{toast &&
{toast}
}
);
}
ReactDOM.createRoot(document.getElementById("root")).render();