Add login gate, calculation history, and AI markdown download.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-13 09:39:38 +08:00
parent abf78cbbb5
commit 462bec2739
23 changed files with 878 additions and 74 deletions
+124
View File
@@ -0,0 +1,124 @@
"use client";
import { useEffect, useState } from "react";
import Link from "next/link";
import { Download, Trash2 } from "lucide-react";
import PageShell from "@/components/page-shell";
import { Button } from "@/components/ui/button";
import { ZenCard } from "@/components/ui/zen-card";
import Markdown from "react-markdown";
import {
buildHistoryMarkdown,
deleteHistoryEntry,
downloadMarkdown,
loadHistory,
} from "@/lib/history/storage";
import { MODE_LABELS, type CalcHistoryEntry } from "@/lib/history/types";
export default function HistoryPageClient() {
const [items, setItems] = useState<CalcHistoryEntry[]>([]);
const [activeId, setActiveId] = useState<string | null>(null);
useEffect(() => {
const list = loadHistory();
setItems(list);
setActiveId(list[0]?.id ?? null);
}, []);
const active = items.find((e) => e.id === activeId) ?? null;
function handleDelete(id: string) {
deleteHistoryEntry(id);
const next = loadHistory();
setItems(next);
if (activeId === id) {
setActiveId(next[0]?.id ?? null);
}
}
return (
<PageShell className="py-8">
<div className="mb-6 text-center">
<h1 className="text-2xl font-bold tracking-wide"></h1>
<p className="mt-2 text-sm text-muted-foreground">
</p>
</div>
{items.length === 0 ? (
<ZenCard className="text-center text-sm text-muted-foreground">
<p></p>
<Link href="/liuyao" className="mt-3 inline-block text-primary underline">
</Link>
</ZenCard>
) : (
<div className="grid gap-6 lg:grid-cols-[minmax(0,280px)_1fr]">
<div className="space-y-2">
{items.map((item) => (
<button
key={item.id}
type="button"
onClick={() => setActiveId(item.id)}
className={`w-full rounded-xl border px-4 py-3 text-left text-sm transition ${
activeId === item.id
? "border-primary/40 bg-primary/5"
: "border-border/60 bg-card/80 hover:border-primary/20"
}`}
>
<p className="font-medium">{item.title}</p>
<p className="mt-1 line-clamp-2 text-xs text-muted-foreground">
{item.question}
</p>
<p className="mt-1 text-xs text-muted-foreground">
{MODE_LABELS[item.mode]} ·{" "}
{new Date(item.createdAt).toLocaleString("zh-CN")}
</p>
</button>
))}
</div>
{active && (
<ZenCard
title={active.title}
subtitle={`${MODE_LABELS[active.mode]} · ${new Date(active.createdAt).toLocaleString("zh-CN")}`}
>
<p className="text-sm text-muted-foreground">{active.question}</p>
{active.summary && (
<p className="text-sm text-muted-foreground">{active.summary}</p>
)}
<div className="flex flex-wrap gap-2">
<Button
size="sm"
variant="outline"
onClick={() =>
downloadMarkdown(
buildHistoryMarkdown(active),
`${active.title}.md`,
)
}
>
<Download size={14} className="mr-1" />
Markdown
</Button>
<Button
size="sm"
variant="outline"
onClick={() => handleDelete(active.id)}
>
<Trash2 size={14} className="mr-1" />
</Button>
</div>
<div className="max-h-[60vh] overflow-y-auto rounded-md border bg-background/50 p-4">
<Markdown className="prose max-w-none text-sm dark:prose-invert">
{active.completion}
</Markdown>
</div>
</ZenCard>
)}
</div>
)}
</PageShell>
);
}