mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-04-29 08:27:30 +08:00
webui: add sessions/memory management and show full hot-reload field details
This commit is contained in:
@@ -19,7 +19,7 @@ function setPath(obj: any, path: string, value: any) {
|
||||
|
||||
const Config: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const { cfg, setCfg, cfgRaw, setCfgRaw, loadConfig, q } = useAppContext();
|
||||
const { cfg, setCfg, cfgRaw, setCfgRaw, loadConfig, hotReloadFieldDetails, q } = useAppContext();
|
||||
const [showRaw, setShowRaw] = useState(false);
|
||||
|
||||
async function saveConfig() {
|
||||
@@ -53,6 +53,18 @@ const Config: React.FC = () => {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="bg-zinc-900/40 border border-zinc-800/80 rounded-2xl p-4">
|
||||
<div className="text-sm font-semibold text-zinc-300 mb-2">热更新字段(完整)</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-xs">
|
||||
{hotReloadFieldDetails.map((it) => (
|
||||
<div key={it.path} className="p-2 rounded bg-zinc-950 border border-zinc-800">
|
||||
<div className="font-mono text-zinc-200">{it.path}</div>
|
||||
<div className="text-zinc-400">{it.name || ''}{it.description ? ` · ${it.description}` : ''}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 bg-zinc-900/40 border border-zinc-800/80 rounded-2xl overflow-hidden flex flex-col shadow-sm">
|
||||
{!showRaw ? (
|
||||
<div className="p-8 overflow-y-auto">
|
||||
|
||||
85
webui/src/pages/Memory.tsx
Normal file
85
webui/src/pages/Memory.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useAppContext } from '../context/AppContext';
|
||||
|
||||
const Memory: React.FC = () => {
|
||||
const { q } = useAppContext();
|
||||
const [files, setFiles] = useState<string[]>([]);
|
||||
const [active, setActive] = useState('');
|
||||
const [content, setContent] = useState('');
|
||||
|
||||
async function loadFiles() {
|
||||
const r = await fetch(`/webui/api/memory${q}`);
|
||||
const j = await r.json();
|
||||
setFiles(Array.isArray(j.files) ? j.files : []);
|
||||
}
|
||||
|
||||
const qp = (k: string, v: string) => `${q}${q ? '&' : '?'}${k}=${encodeURIComponent(v)}`;
|
||||
|
||||
async function openFile(path: string) {
|
||||
const r = await fetch(`/webui/api/memory${qp('path', path)}`);
|
||||
const j = await r.json();
|
||||
setActive(path);
|
||||
setContent(j.content || '');
|
||||
}
|
||||
|
||||
async function saveFile() {
|
||||
if (!active) return;
|
||||
await fetch(`/webui/api/memory${q}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ path: active, content }),
|
||||
});
|
||||
await loadFiles();
|
||||
}
|
||||
|
||||
async function removeFile(path: string) {
|
||||
await fetch(`/webui/api/memory${qp('path', path)}`, { method: 'DELETE' });
|
||||
if (active === path) {
|
||||
setActive('');
|
||||
setContent('');
|
||||
}
|
||||
await loadFiles();
|
||||
}
|
||||
|
||||
async function createFile() {
|
||||
const name = prompt('memory file name', `note-${Date.now()}.md`);
|
||||
if (!name) return;
|
||||
await fetch(`/webui/api/memory${q}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ path: name, content: '' }),
|
||||
});
|
||||
await loadFiles();
|
||||
await openFile(name);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadFiles().catch(() => {});
|
||||
}, [q]);
|
||||
|
||||
return (
|
||||
<div className="flex h-full">
|
||||
<aside className="w-72 border-r border-zinc-800 p-4 space-y-2 overflow-y-auto">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="font-semibold">Memory Files</h2>
|
||||
<button onClick={createFile} className="px-2 py-1 rounded bg-zinc-800">+</button>
|
||||
</div>
|
||||
{files.map((f) => (
|
||||
<div key={f} className={`flex items-center justify-between p-2 rounded ${active === f ? 'bg-zinc-800' : 'hover:bg-zinc-900'}`}>
|
||||
<button className="text-left flex-1" onClick={() => openFile(f)}>{f}</button>
|
||||
<button className="text-red-400" onClick={() => removeFile(f)}>x</button>
|
||||
</div>
|
||||
))}
|
||||
</aside>
|
||||
<main className="flex-1 p-4 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="font-semibold">{active || 'No file selected'}</h2>
|
||||
<button onClick={saveFile} className="px-3 py-1 rounded bg-indigo-600">Save</button>
|
||||
</div>
|
||||
<textarea value={content} onChange={(e) => setContent(e.target.value)} className="w-full h-[80vh] bg-zinc-900 border border-zinc-800 rounded p-3" />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Memory;
|
||||
Reference in New Issue
Block a user