9994bbeaac
- All pages use new design system (card, section-label, btn-accent) - No more glassmorphism — clean solid surfaces - Cron, Soul, Config, Skills detail all restyled - Consistent typography and spacing throughout
83 lines
2.7 KiB
TypeScript
83 lines
2.7 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect } from "react";
|
|
import { Save, RotateCcw } from "lucide-react";
|
|
|
|
export default function SoulPage() {
|
|
const [content, setContent] = useState("");
|
|
const [original, setOriginal] = useState("");
|
|
const [loading, setLoading] = useState(true);
|
|
const [saving, setSaving] = useState(false);
|
|
const [saved, setSaved] = useState(false);
|
|
|
|
useEffect(() => {
|
|
fetch("/soul/api")
|
|
.then((r) => r.json())
|
|
.then((d) => { setContent(d.content || ""); setOriginal(d.content || ""); })
|
|
.catch(() => {})
|
|
.finally(() => setLoading(false));
|
|
}, []);
|
|
|
|
const dirty = content !== original;
|
|
|
|
const save = async () => {
|
|
setSaving(true);
|
|
await fetch("/soul/api", {
|
|
method: "PUT",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ content }),
|
|
});
|
|
setOriginal(content);
|
|
setSaving(false);
|
|
setSaved(true);
|
|
setTimeout(() => setSaved(false), 2000);
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<div className="flex items-center justify-between">
|
|
<h1 className="text-xl font-semibold" style={{ color: "var(--text-1)" }}>SOUL.md</h1>
|
|
<div className="flex items-center gap-2">
|
|
{dirty && (
|
|
<button onClick={() => setContent(original)} className="p-2 rounded-xl active:scale-95" style={{ color: "var(--text-3)" }}>
|
|
<RotateCcw size={16} />
|
|
</button>
|
|
)}
|
|
<button
|
|
onClick={save}
|
|
disabled={!dirty || saving}
|
|
className="flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-xs font-semibold transition-all disabled:opacity-20"
|
|
style={{ background: "var(--accent-dim)", color: "var(--accent)" }}
|
|
>
|
|
<Save size={14} />
|
|
{saving ? "Saving..." : saved ? "Saved" : "Save"}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<p className="text-[12px]" style={{ color: "var(--text-3)" }}>
|
|
Persona file. Loaded fresh each message.
|
|
</p>
|
|
|
|
{loading ? (
|
|
<div className="card p-4 animate-pulse space-y-2">
|
|
<div className="h-4 rounded w-full" style={{ background: "var(--bg-hover)" }} />
|
|
<div className="h-4 rounded w-3/4" style={{ background: "var(--bg-hover)" }} />
|
|
</div>
|
|
) : (
|
|
<textarea
|
|
value={content}
|
|
onChange={(e) => setContent(e.target.value)}
|
|
className="w-full min-h-[60vh] p-4 rounded-2xl text-[13px] resize-none font-mono leading-relaxed outline-none transition-colors"
|
|
style={{
|
|
background: "var(--bg-raised)",
|
|
border: "1px solid var(--border)",
|
|
color: "var(--text-1)",
|
|
}}
|
|
spellCheck={false}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|