Documentation Index
Fetch the complete documentation index at: https://tech.illasoft.com/llms.txt
Use this file to discover all available pages before exploring further.
使用 Zustand 进行轻量级状态管理。
Store 结构
Thread 数据和用户 profile 数据通过 TanStack Query 从服务端获取,不使用 Zustand store。
Poisson Store
usePoissonStore(来自 store/poisson.ts)管理 Generator 相关状态,使用嵌套 map 模式 poissonMap[threadId] 存储每个 thread 的独立状态。
// store/poisson.ts:8-29 (权威)
type EditMode =
| "chat" | "upscale" | "filter" | "adjust" | "crop" | "chooseFilter"
| "inpaint" | "expand" | "replaceBg" | "eraser" | "mask"
| "generateVideo" | "generateMusic" | "generateInstrumental" | "addVocals"
| "generateImage"
// 2026-03+ 新增 5 个
| "trimVideo" | "trimAudio" | "motionControl" | "videoExtend" | "videoEdit";
type KiraModel = "lite" | "nova" | "crazy" | "ultra";
interface PoissonState {
// 模型选择(顶层,通过 persist 中间件持久化到 localStorage)
model: KiraModel;
// 嵌套 map,按 threadId 隔离状态
poissonMap: Record<string, {
currentImage: Image;
currentVersionId: string;
versions: Version[];
messages: UIMessage[];
tempMessages: UIMessage[];
executingTool: { toolName: string; args: object } | null;
chatStatus: ChatStatus;
inputState: InputState;
errorInfo: ToolError[] | null;
editMode: EditMode;
editModeData?: any;
isReceivingMessage: boolean;
isReceivingVersions: boolean;
currentSelectVideo?: Video;
currentSelectAudio?: Audio;
currentSelectMediaType: "image" | "video" | "audio";
}>;
// Actions(均需传入 threadId)
setCurrentImage: (threadId: string, image: Image) => void;
setEditMode: (threadId: string, mode: EditMode, data?: any) => void;
getEditMode: (threadId: string) => EditMode; // 默认 "chat"
getEditModeData: (threadId: string) => any;
setCurrentSelectVideo: (threadId: string, video: Video) => void;
setCurrentSelectAudio: (threadId: string, audio: Audio) => void;
getCurrentSelectMediaType: (threadId: string) => "image" | "video" | "audio" | null;
setCurrentVersionId: (threadId: string, versionId: string) => void; // 自动选首媒体
appendThreadVersion: (threadId: string, version: Version) => void;
updateVersions: (threadId: string) => void; // 异步重新 fetch versions
}
export const usePoissonStore = create<PoissonState>(
persist(/* ... */, { name: 'poisson-store', partialize: (state) => ({ model: state.model }) })
);
Store 使用 Zustand 的 persist 中间件,通过 localStorage 持久化 model 字段。
Auth Store
useAuthStore(来自 store/auth.ts)仅管理认证状态。用户 profile 数据通过 TanStack Query 获取。
// store/auth.ts
interface AuthState {
isAuth: boolean;
login: () => void;
logout: () => void;
}
Feed State Store
useFeedStateStore(来自 store/feed.ts)管理 Feed 相关状态。
// store/feed.ts
interface FeedState {
deletedFeed: string[]; // 已删除的 feed ID 列表
localDeleteFeed: (feedId: string) => void; // 本地标记删除
localRecoverFeed: (feedId: string) => void; // 本地恢复
getDeletedFeed: (feedId: string) => boolean; // 判断是否已删除
}
使用方式
// 组件中使用
function ImagePanel({ threadId }: { threadId: string }) {
const currentImage = usePoissonStore(
(state) => state.poissonMap[threadId]?.currentImage
);
return <img src={currentImage?.url} />;
}
自定义 Hooks
仅 4 个 hook,集中在 Centrifugo 订阅和任务重提交:
| Hook | 用途 | 文件 |
|---|
useMusicNotificationHandler | 订阅 Centrifugo audio_status 推送,同步 store (versions / messages / currentSelectAudio) | hooks/use-music-notification-handler.ts |
useVideoNotificationHandler | 同上,video_status | hooks/use-video-notification-handler.ts |
useSubmitMusicTask | POST /music/submit/:taskId(insufficient_credits 恢复);失败触发 billing dialog | hooks/use-submit-music-task.ts |
useSubmitVideoTask | 同上视频版 | hooks/use-submit-video-task.ts |
不存在 useKiraChat hook —— 它实际上是 components/chat/kirachat.tsx 的 React component。不存在 ImageUploaderProvider / ImageUploaderContext —— 图片上传走 lib/utils.ts 的 uploadImage() 工具函数(内含 CSAM 451 错误识别)。
CSAM 错误处理(前端)
// lib/utils.ts:12
export const CSAM_BLOCKED_ERROR = "csam_blocked";
// lib/utils.ts:44-87 uploadImage()
const res = await fetch(`/image/check/${imageId}`, {...});
if (res.status === 451) {
throw new Error(CSAM_BLOCKED_ERROR);
}
上传组件捕获 CSAM_BLOCKED_ERROR 后 toast i18n key error.csam_not_allowed。
最佳实践
- 每个功能模块一个 Store
- 状态和 Actions 定义在同一个 Store 中
- 使用选择器避免不必要的重渲染