feat(assets): add chat typing indicator lottie animation

This commit is contained in:
2026-06-08 17:26:09 +08:00
parent f4e1c30051
commit 90fe892995
22 changed files with 988 additions and 3 deletions
+77
View File
@@ -0,0 +1,77 @@
"use client";
/**
* LocalMessage 模型(存储层副本)
*
* 与 `src/data/models/chat/chat_message.ts` 的 `ChatMessage` 解耦:
* 存储层维护自己的数据形态(独立 id/role/content/createdAt + 内部 sessionId 分组),
* 不耦合 wire 模型,方便后续 schema 独立演进。
*
* 对齐项目内其他 Zod 模型的范式:
* - `*Schema` 是验证与默认值的单一来源
* - `*Input` 是构造输入(字段可缺),`*Data` 是解析后形态
* - 不可变(private constructor + Object.freeze
* - 桥接方法 `toRow` / `fromRow` 处理 Dexie 行 ↔ 类实例的转换
*/
import { z } from "zod";
import type { LocalMessageRow } from "./local_chat_db";
export const LocalMessageSchema = z.object({
id: z.string(),
role: z.string(),
content: z.string(),
createdAt: z.string().default(""),
sessionId: z.string().default(""),
});
export type LocalMessageInput = z.input<typeof LocalMessageSchema>;
export type LocalMessageData = z.output<typeof LocalMessageSchema>;
export class LocalMessage {
declare readonly id: string;
declare readonly role: string;
declare readonly content: string;
declare readonly createdAt: string;
declare readonly sessionId: string;
private constructor(input: LocalMessageInput) {
const data = LocalMessageSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: LocalMessageInput): LocalMessage {
return new LocalMessage(input);
}
static fromJson(json: unknown): LocalMessage {
return LocalMessage.from(json as LocalMessageInput);
}
toJson(): LocalMessageData {
return LocalMessageSchema.parse(this);
}
/** 转成 Dexie 行(不含 dbId,让 Dexie 自增)。 */
toRow(): Omit<LocalMessageRow, "dbId"> {
return {
id: this.id,
role: this.role,
content: this.content,
createdAt: this.createdAt,
sessionId: this.sessionId,
};
}
/** 从 Dexie 行还原(忽略 dbId 内部主键)。 */
static fromRow(row: LocalMessageRow): LocalMessage {
return LocalMessage.from({
id: row.id,
role: row.role,
content: row.content,
createdAt: row.createdAt,
sessionId: row.sessionId,
});
}
}