Reference Image 统一重构: - 新增 ReferenceImage 类型 (src/types/index.ts),统一承载 base64 和 localPath 两种来源 - 重写 image-ref-store.ts:单一 Map<string, ReferenceImage[]> 替代原来的 imageGenRefStore + lastGeneratedImagePaths 双通路 - sessionStorage 持久化 last-generated 路径,解决页面刷新后数据丢失 - buildReferenceImages() 作为唯一的 merge 入口,消除各组件手写拼接逻辑 - ImageGenConfirmation props 从两个 (referenceImagePaths + referenceImagesData) 合并为单一 referenceImages?: ReferenceImage[] - 预览渲染改为单循环,根据 data/localPath 决定 img src,上传图和结果图同时显示 - API 调用时再拆分回 base64 和路径两个字段,后端无需改动 - StreamingMessage / MessageItem 各自 5 行手动 merge 逻辑替换为一行 buildReferenceImages() 调用 - ChatView 改用 setLastGeneratedImages() 和 transferPendingToMessage() 替代直接操作 Map - MessageInput 改用 setRefImages() / deleteRefImages() 替代直接操作 Map 新增 Gemini 3.1 Flash Image 模型: - 新增 Nano Banana 2 (gemini-3.1-flash-image-preview) 作为默认图片生成模型 - 模型列表: Nano Banana 2 > Nano Banana Pro > Nano Banana - 更新 ProviderManager、image-generator.ts、media/generate route 的默认值 其他已有改动一并提交: - batch image generation 功能文件 - 依赖更新 (package.json / package-lock.json) - i18n、db schema、AppShell、uploads route 等相关改动 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
124 lines
3.4 KiB
TypeScript
124 lines
3.4 KiB
TypeScript
/**
|
||
* Unified reference-image store for image generation.
|
||
*
|
||
* Keys:
|
||
* PENDING_KEY – images uploaded via the input bar, not yet bound to a message
|
||
* LAST_GENERATED_KEY – file paths of the most recently generated images (sessionStorage-backed)
|
||
* <message-id> – images bound to a specific assistant message
|
||
*/
|
||
import type { ReferenceImage } from '@/types';
|
||
|
||
export const PENDING_KEY = '__pending__';
|
||
export const LAST_GENERATED_KEY = '__last_generated__';
|
||
|
||
const store = new Map<string, ReferenceImage[]>();
|
||
|
||
// ── sessionStorage persistence for last-generated paths ──
|
||
|
||
const SS_KEY = 'imgref:last_generated';
|
||
|
||
function loadLastGenerated(): void {
|
||
if (typeof window === 'undefined') return;
|
||
try {
|
||
const raw = sessionStorage.getItem(SS_KEY);
|
||
if (raw) {
|
||
const arr: ReferenceImage[] = JSON.parse(raw);
|
||
if (Array.isArray(arr) && arr.length > 0) {
|
||
store.set(LAST_GENERATED_KEY, arr);
|
||
}
|
||
}
|
||
} catch {
|
||
// ignore
|
||
}
|
||
}
|
||
|
||
// Auto-restore on module load (client only)
|
||
loadLastGenerated();
|
||
|
||
// ── Public helpers ──
|
||
|
||
/** Set reference images for a given key. */
|
||
export function setRefImages(key: string, images: ReferenceImage[]): void {
|
||
if (images.length === 0) {
|
||
store.delete(key);
|
||
} else {
|
||
store.set(key, images);
|
||
}
|
||
}
|
||
|
||
/** Delete reference images for a given key. */
|
||
export function deleteRefImages(key: string): void {
|
||
store.delete(key);
|
||
}
|
||
|
||
/** Get reference images for a given key (or undefined). */
|
||
export function getRefImages(key: string): ReferenceImage[] | undefined {
|
||
return store.get(key);
|
||
}
|
||
|
||
/**
|
||
* Transfer pending reference images to a specific message ID.
|
||
* Called when a streaming response transitions to a persisted MessageItem.
|
||
*/
|
||
export function transferPendingToMessage(messageId: string): void {
|
||
const pending = store.get(PENDING_KEY);
|
||
if (pending) {
|
||
store.set(messageId, pending);
|
||
store.delete(PENDING_KEY);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Store generated image paths and persist to sessionStorage.
|
||
* Called when image generation completes.
|
||
*/
|
||
export function setLastGeneratedImages(paths: string[]): void {
|
||
const images: ReferenceImage[] = paths.map(p => ({ mimeType: 'image/png', localPath: p }));
|
||
store.set(LAST_GENERATED_KEY, images);
|
||
if (typeof window !== 'undefined') {
|
||
try {
|
||
sessionStorage.setItem(SS_KEY, JSON.stringify(images));
|
||
} catch {
|
||
// storage full
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Single merge entry-point: build a unified ReferenceImage[] for a given context.
|
||
*
|
||
* @param key Store key (PENDING_KEY or message.id)
|
||
* @param useLastGenerated Whether the LLM requested editing last-generated images
|
||
* @param extraPaths Additional file paths from the parsed request (referenceImages field)
|
||
*/
|
||
export function buildReferenceImages(
|
||
key: string,
|
||
useLastGenerated: boolean,
|
||
extraPaths?: string[],
|
||
): ReferenceImage[] {
|
||
const result: ReferenceImage[] = [];
|
||
|
||
// 1. User-uploaded base64 images
|
||
const uploaded = store.get(key);
|
||
if (uploaded) {
|
||
result.push(...uploaded);
|
||
}
|
||
|
||
// 2. Last-generated images (if LLM requested)
|
||
if (useLastGenerated) {
|
||
const lastGen = store.get(LAST_GENERATED_KEY);
|
||
if (lastGen) {
|
||
result.push(...lastGen);
|
||
}
|
||
}
|
||
|
||
// 3. Extra paths from the parsed request JSON
|
||
if (extraPaths && extraPaths.length > 0) {
|
||
for (const p of extraPaths) {
|
||
result.push({ mimeType: 'image/png', localPath: p });
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|