feat(assets): add DeepSeek assistant with streaming results and voice input

This commit is contained in:
shishengliang
2026-09-23 14:16:36 +08:00
parent 822aabf2ca
commit 0c4a9bcd05
14 changed files with 3355 additions and 3 deletions
@@ -0,0 +1,79 @@
// Adapted from Beautiful UI's MIT-licensed ThinkingState (Steps).
// Uses real server progress rather than the reference's timed demo sequence.
import { useEffect, useState } from "react";
import { Check, ChevronDown, LoaderCircle, Sparkles } from "lucide-react";
export function ThinkingState({
steps,
working,
started,
failed,
}: {
steps: string[];
working: boolean;
started: number;
failed?: boolean;
}) {
const [manualExpanded, setExpanded] = useState<boolean | null>(null);
const [elapsed, setElapsed] = useState(0);
useEffect(() => {
if (!working) return;
const update = () => setElapsed((Date.now() - started) / 1000);
update();
const timer = window.setInterval(update, 100);
return () => window.clearInterval(timer);
}, [working, started]);
const expanded = manualExpanded ?? working;
return (
<div className="aq-thinking">
<button
type="button"
aria-expanded={expanded}
onClick={() => setExpanded(!expanded)}
>
<Sparkles size={15} aria-hidden="true" />
<span className={working ? "aq-shimmer" : ""}>
{working ? "正在分析" : failed ? "查询已停止" : "分析完成"}
</span>
<span className="aq-time">{elapsed.toFixed(1)}s</span>
<ChevronDown
size={14}
className={expanded ? "aq-rotated" : ""}
aria-hidden="true"
/>
</button>
<div
className="aq-trace-collapse"
aria-hidden={!expanded}
style={{
gridTemplateRows: expanded ? "1fr" : "0fr",
opacity: expanded ? 1 : 0,
}}
>
<div className="aq-trace-clip">
<ol className="aq-trace">
{steps.map((step, index) => (
<li key={step}>
{working && index === steps.length - 1 ? (
<LoaderCircle
className="aq-spin"
size={14}
aria-hidden="true"
/>
) : (
<Check size={14} aria-hidden="true" />
)}
<span>{step}</span>
</li>
))}
</ol>
</div>
</div>
{working && (
<span className="sr-only" role="status">
{steps.at(-1) || "正在连接分析服务"}
</span>
)}
</div>
);
}