feat(frontend): M9 模态 UI(mode 系统+共享 store+WS 客户端 + chat(思维链/工具回显/情绪徽标)/code/Agent 监控/后台思考页 + Miuix 设计 token + PWA);tsc+vite build 通过
This commit is contained in:
1 parent
b3ffd51736
commit
4c48e94659
40 files changed
+5321
No files matched your search
@@ -0,0 +1,102 @@
|
||||
import { useState } from "react";
|
||||
import type { ChatMessage, ToolUsed } from "../lib/types";
|
||||
import { Tag } from "./primitives";
|
||||
|
||||
/** Collapsible inline chain-of-thought block. */
|
||||
export function ThinkingBlock({
|
||||
text,
|
||||
streaming,
|
||||
}: {
|
||||
text?: string;
|
||||
streaming?: boolean;
|
||||
}): React.ReactNode | null {
|
||||
const [open, setOpen] = useState(false);
|
||||
if (!text && !streaming) return null;
|
||||
|
||||
return (
|
||||
<div className="mb-2 overflow-hidden rounded-[var(--mu-radius-sm)] border border-[var(--mu-glass-border)] bg-[var(--mu-surface-2)]/60">
|
||||
<button
|
||||
onClick={() => setOpen((v) => !v)}
|
||||
className="flex w-full items-center gap-1.5 px-3 py-1.5 text-xs text-[var(--mu-text-lo)]"
|
||||
>
|
||||
<span
|
||||
className={`inline-block transition-transform duration-200 ${open ? "rotate-90" : ""}`}
|
||||
>
|
||||
▸
|
||||
</span>
|
||||
<span>思维链</span>
|
||||
{streaming && !text ? <span className="mu-blink">思考中…</span> : null}
|
||||
</button>
|
||||
{open && text ? (
|
||||
<pre className="max-h-40 overflow-auto whitespace-pre-wrap px-3 pb-2 text-xs leading-relaxed text-[var(--mu-text-mid)]">
|
||||
{text}
|
||||
</pre>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/** Tool echo card (工具回显). */
|
||||
export function ToolCard({ tool }: { tool: ToolUsed }): React.ReactNode {
|
||||
const [open, setOpen] = useState(false);
|
||||
const status = tool.ok === undefined ? "neutral" : tool.ok ? "ok" : "err";
|
||||
return (
|
||||
<div className="mb-2 overflow-hidden rounded-[var(--mu-radius-sm)] border border-[var(--mu-glass-border)] bg-[var(--mu-surface-2)]/60">
|
||||
<button
|
||||
onClick={() => setOpen((v) => !v)}
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-xs"
|
||||
>
|
||||
<span
|
||||
className={`inline-block transition-transform duration-200 ${open ? "rotate-90" : ""}`}
|
||||
>
|
||||
▸
|
||||
</span>
|
||||
<Tag tone={status}>
|
||||
{tool.ok === false ? "✗" : tool.ok === true ? "✓" : "⚙"} {tool.name}
|
||||
</Tag>
|
||||
{tool.summary ? (
|
||||
<span className="truncate text-[var(--mu-text-lo)]">{tool.summary}</span>
|
||||
) : null}
|
||||
</button>
|
||||
{(open || tool.detail) && tool.detail ? (
|
||||
<pre className="max-h-40 overflow-auto whitespace-pre-wrap px-3 pb-2 text-xs text-[var(--mu-text-mid)]">
|
||||
{tool.detail}
|
||||
</pre>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/** A single chat message bubble. */
|
||||
export function MessageBubble({ message }: { message: ChatMessage }): React.ReactNode {
|
||||
const isUser = message.role === "user";
|
||||
if (isUser) {
|
||||
return (
|
||||
<div className="mu-rise-in flex justify-end">
|
||||
<div className="max-w-[80%] whitespace-pre-wrap rounded-[var(--mu-radius-lg)] bg-[var(--mu-accent)] px-4 py-2.5 text-sm text-white shadow-[var(--mu-shadow-1)]">
|
||||
{message.content}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mu-rise-in flex gap-3">
|
||||
<img src="/icon.svg" alt="" className="mt-1 h-7 w-7 shrink-0" />
|
||||
<div className="min-w-0 max-w-[85%]">
|
||||
{message.mood ? (
|
||||
<div className="mb-1 text-xs text-[var(--mu-text-lo)]">心情 · {message.mood}</div>
|
||||
) : null}
|
||||
<ThinkingBlock text={message.thinking} streaming={message.streaming} />
|
||||
{message.tools?.map((t, i) => <ToolCard key={i} tool={t} />)}
|
||||
<div className="rounded-[var(--mu-radius-lg)] border border-[var(--mu-glass-border)] bg-[var(--mu-surface-3)] px-4 py-2.5 text-sm text-[var(--mu-text-hi)]">
|
||||
{message.content ? (
|
||||
<span className="whitespace-pre-wrap">{message.content}</span>
|
||||
) : message.streaming ? (
|
||||
<span className="mu-blink text-[var(--mu-text-lo)]">生成中…</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user