初始化提交
This commit is contained in:
commit
887c1f449e
963 files changed
+166869
No files matched your search
@@ -0,0 +1,169 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
import { Button } from "@/components/ui/Button";
|
||||
import { SensitivityBadge } from "@/components/ui/StatusBadge";
|
||||
import { describeError } from "@/lib/utils/error";
|
||||
import { MEMORY_SOURCE_LABEL, MEMORY_TYPE_LABEL } from "@/lib/constants/labels";
|
||||
import { memoryApi } from "@/lib/api";
|
||||
import { formatDateTime, formatPercent } from "@/lib/utils/format";
|
||||
import { cn } from "@/lib/utils/cn";
|
||||
import type { CreateMemoryRequest, Memory } from "@/types/api";
|
||||
import { MemoryEditor } from "./MemoryEditor";
|
||||
|
||||
/** 单条记忆卡片(设计稿 memory-preview.html §mem-card)
|
||||
* 类型色标(.mem-type)、置信度/重要性进度条(.meta-bar .fill)、
|
||||
* 敏感度徽章、证据来源(.mem-foot)*/
|
||||
export function MemoryCard({
|
||||
memory,
|
||||
characterId,
|
||||
onChanged,
|
||||
onError,
|
||||
}: {
|
||||
memory: Memory;
|
||||
characterId?: string;
|
||||
onChanged: (kind: "update" | "delete", payload: Memory | string) => void;
|
||||
onError: (message: string) => void;
|
||||
}) {
|
||||
const [editing, setEditing] = useState(false);
|
||||
const [confirming, setConfirming] = useState(false);
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
const handleUpdate = async (body: CreateMemoryRequest) => {
|
||||
setBusy(true);
|
||||
try {
|
||||
const updated = await memoryApi.updateMemory(memory.id, body);
|
||||
onChanged("update", updated);
|
||||
setEditing(false);
|
||||
} catch (err) {
|
||||
onError(describeError(err));
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
setBusy(true);
|
||||
try {
|
||||
await memoryApi.deleteMemory(memory.id);
|
||||
onChanged("delete", memory.id);
|
||||
} catch (err) {
|
||||
onError(describeError(err));
|
||||
setConfirming(false);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (editing) {
|
||||
return (
|
||||
<MemoryEditor
|
||||
characterId={characterId}
|
||||
initial={memory}
|
||||
onSubmit={handleUpdate}
|
||||
onCancel={() => setEditing(false)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const sensClass = memory.sensitivity === "high" ? "high-sens" : "";
|
||||
|
||||
return (
|
||||
<div className={cn("mem-card", sensClass)}>
|
||||
{/* 标题行(设计稿 .mem-head) */}
|
||||
<div className="mem-head">
|
||||
<span className="mem-title">{memory.title || "无标题记忆"}</span>
|
||||
<span className={cn("mem-type", memory.type)}>
|
||||
{MEMORY_TYPE_LABEL[memory.type] ?? memory.type}
|
||||
</span>
|
||||
<SensitivityBadge level={memory.sensitivity} />
|
||||
</div>
|
||||
|
||||
{/* 记忆内容(设计稿 .mem-content) */}
|
||||
<p className="mem-content">{memory.content}</p>
|
||||
|
||||
{/* 元数据行:置信度 + 重要性进度条(设计稿 .mem-meta) */}
|
||||
<div className="mem-meta">
|
||||
<span className="meta-item conf">
|
||||
<span className="lab">置信</span>
|
||||
<span className="meta-bar">
|
||||
<span
|
||||
className="fill"
|
||||
style={{
|
||||
width: `${Math.round((memory.confidence ?? 0) * 100)}%`,
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
<span className="vv">{formatPercent(memory.confidence)}</span>
|
||||
</span>
|
||||
<span className="meta-item imp">
|
||||
<span className="lab">重要</span>
|
||||
<span className="meta-bar">
|
||||
<span
|
||||
className="fill"
|
||||
style={{
|
||||
width: `${Math.round((memory.importance ?? 0) * 100)}%`,
|
||||
background: "var(--gilt)",
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
<span className="vv">{formatPercent(memory.importance)}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* 脚注(设计稿 .mem-foot) */}
|
||||
<div className="mem-foot">
|
||||
<span className="evi">
|
||||
来源:{MEMORY_SOURCE_LABEL[memory.source] ?? memory.source}
|
||||
</span>
|
||||
<span className="evi">
|
||||
{formatDateTime(memory.updated_at ?? memory.created_at)}
|
||||
</span>
|
||||
|
||||
{memory.evidence_ids && memory.evidence_ids.length > 0 && (
|
||||
<span className="evi">证据·{memory.evidence_ids.length} 条</span>
|
||||
)}
|
||||
|
||||
<span className="act">
|
||||
<button type="button" onClick={() => setEditing(true)}>
|
||||
编辑
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="primary"
|
||||
onClick={() => setConfirming(true)}
|
||||
disabled={busy}
|
||||
>
|
||||
删除
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* 删除确认 */}
|
||||
{confirming && (
|
||||
<div className="mt-3 flex flex-wrap items-center justify-between gap-2 rounded-lg border border-cinnabar/30 bg-cinnabar/10 px-3 py-2 text-xs text-cinnabar backdrop-blur-sm">
|
||||
<span>删除后此记忆将不再注入对话上下文,确认删除?</span>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={() => setConfirming(false)}
|
||||
disabled={busy}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="danger"
|
||||
onClick={handleDelete}
|
||||
loading={busy}
|
||||
>
|
||||
确认删除
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user