// ====== Agent editor ======
//
// PR 18: sticky bottom SaveBar gives the user explicit feedback that their
// edits were saved. The auto-save path (parent's onUpdate on every keystroke)
// stays for keystroke-level safety; the bar shows status + offers a manual
// "Save" click so users who expect a button see one. Closes bug.md P1
// "/manage Agent 管理页没有底部 Save 按钮 / 用户无法判断修改是否已经保存".
function relTime(ts) {
if (!ts) return "";
const s = Math.max(0, Math.floor((Date.now() - ts) / 1000));
if (s < 5) return "just now";
if (s < 60) return `${s}s ago`;
if (s < 3600) return `${Math.floor(s / 60)}m ago`;
return `${Math.floor(s / 3600)}h ago`;
}
function SaveBar({ saveState, onSave, extraButtons }) {
// tick once per second so the "Saved Xs ago" pill stays fresh without
// requiring the parent to re-render on a timer.
const [, setTick] = React.useState(0);
React.useEffect(() => {
const id = setInterval(() => setTick((n) => n + 1), 1000);
return () => clearInterval(id);
}, []);
const st = saveState || { status: "idle" };
const status = st.status;
let pill;
if (status === "saving") {
pill = Saving…;
} else if (status === "dirty") {
pill = Unsaved changes;
} else if (status === "saved") {
pill =