feat(web): D 消息编辑/删除前端入口 + 日志聚合前端(Observability后端tail) + 修api-client契约
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- D前端: lib/api/messages.ts(patchMessage/deleteMessage,兼容裸DTO与{message}) + MessageActions.tsx(仅user消息; 单击/右键/450ms长按开Miuix玻璃菜单; 编辑弹窗预填+字数→乐观updateMessageById→patchMessage→失败回滚; 删除确认→deleteMessage→removeMessageById; 乐观气泡禁用) + chat.tsx挂载 + Icon加edit/trash/more
- 日志前端: types(AdminLogLine time:string|number) + adminSystem.adminObservabilityLogs + Observability.tsx 后端分级tail(level筛选chip注明'该级及以上'+等宽行HH:mm:ss.SSS·level着色·source·msg+30s刷新) 下半保留WS回显
- Docs/api-client §6a/§4.2 修正: PATCH /messages/{id} 实际返回 {data:<DTO>} 非 {data:{message}}(前端已双向兼容,避免误导Android)
- 前端 build 绿(167 modules); SSR冒烟11 checks
- 遗留: 未实机登录; 菜单absolute理论上边缘可裁切(可改portal)
This commit is contained in:
AskaEth committed 2026-10-03 15:23:27 +08:00
1 parent 025dd1796b
commit 1ac126fc9e
9 files changed
+611 -12

No files matched your search

+7 -2
View File
@@ -6,6 +6,7 @@ import { Tag } from "./primitives";
import { Icon, type IconName } from "./Icon";
import { WakePath } from "./status";
import { modeBody } from "./messages";
import { UserMessageActions } from "./messages/MessageActions";
import { renderContent } from "../lib/renderContent";
import { splitBubble, type ContentPart } from "../lib/parseContent";
@@ -332,7 +333,9 @@ export function SplitChatBubble({
// bubble and below the last — never inside a bubble's body.
const align = message.role === "user" ? "right" : "left";
return (
<div className={`flex flex-col gap-2.5 ${message.role === "user" ? "items-end" : ""}`}>
<div
className={`flex flex-col gap-2.5 ${message.role === "user" ? "group relative items-end" : ""}`}
>
<BubbleNotes items={head} align={align} />
{segments.map((seg, i) =>
message.role === "user" ? (
@@ -348,6 +351,7 @@ export function SplitChatBubble({
),
)}
<BubbleNotes items={tail} align={align} />
{message.role === "user" ? <UserMessageActions message={message} /> : null}
</div>
);
}
@@ -362,7 +366,7 @@ function UserBubble({ message, mode }: { message: ChatMessage; mode: Mode }): Re
<span className="whitespace-pre-wrap">{renderContent(message.content)}</span>
) : null;
return (
<div className="mu-rise-in flex flex-col items-end gap-0.5">
<div className="mu-rise-in group relative flex flex-col items-end gap-0.5">
<div className="liquid-glass-accent max-w-[80%] rounded-[var(--mu-radius-lg)] px-4 py-2.5 text-sm text-white shadow-[var(--mu-shadow-1)]">
{message.image ? (
<img
@@ -375,6 +379,7 @@ function UserBubble({ message, mode }: { message: ChatMessage; mode: Mode }): Re
</div>
<SourceLabel deviceId={message.deviceId} role={message.role} />
<SenderLabel name={message.senderName} />
<UserMessageActions message={message} />
</div>
);
}