feat: unify reference image data structure, add Nano Banana 2 model

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>
This commit is contained in:
7418andClaude Opus 4.6 committed 2026-02-27 00:55:24 +08:00
1 parent 00d3fef5a4
commit d5dd9cfe34
37 files changed
+3508 -343

No files matched your search

+149
View File
@@ -406,6 +406,16 @@ export const SETTING_KEYS = {
MAX_THINKING_TOKENS: 'max_thinking_tokens',
} as const;
// ==========================================
// Reference Image Types (for image generation)
// ==========================================
export interface ReferenceImage {
mimeType: string;
data?: string; // base64 (user upload)
localPath?: string; // file path (generated result)
}
// ==========================================
// File Attachment Types
// ==========================================
@@ -435,6 +445,145 @@ export function formatFileSize(bytes: number): string {
// Claude Client Types
// ==========================================
// ==========================================
// Batch Image Generation Types
// ==========================================
export type MediaJobStatus = 'draft' | 'planning' | 'planned' | 'running' | 'paused' | 'completed' | 'cancelled' | 'failed';
export type MediaJobItemStatus = 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled';
export interface MediaJob {
id: string;
session_id: string | null;
status: MediaJobStatus;
doc_paths: string; // JSON array of file paths
style_prompt: string;
batch_config: string; // JSON of BatchConfig
total_items: number;
completed_items: number;
failed_items: number;
created_at: string;
updated_at: string;
completed_at: string | null;
}
export interface MediaJobItem {
id: string;
job_id: string;
idx: number;
prompt: string;
aspect_ratio: string;
image_size: string;
model: string;
tags: string; // JSON array of strings
source_refs: string; // JSON array of strings
status: MediaJobItemStatus;
retry_count: number;
result_media_generation_id: string | null;
error: string | null;
created_at: string;
updated_at: string;
}
export interface MediaContextEvent {
id: string;
session_id: string;
job_id: string;
payload: string; // JSON object
sync_mode: 'manual' | 'auto_batch';
synced_at: string | null;
created_at: string;
}
export interface BatchConfig {
concurrency: number; // max parallel image generations (default: 2)
maxRetries: number; // max retry attempts per item (default: 2)
retryDelayMs: number; // base delay for exponential backoff (default: 2000)
}
export interface PlannerItem {
prompt: string;
aspectRatio: string;
resolution: string;
tags: string[];
sourceRefs: string[];
}
export interface PlannerOutput {
summary: string;
items: PlannerItem[];
}
export type JobProgressEventType =
| 'item_started'
| 'item_completed'
| 'item_failed'
| 'item_retry'
| 'job_completed'
| 'job_paused'
| 'job_cancelled';
export interface JobProgressEvent {
type: JobProgressEventType;
jobId: string;
itemId?: string;
itemIdx?: number;
progress: {
total: number;
completed: number;
failed: number;
processing: number;
};
error?: string;
retryCount?: number;
mediaGenerationId?: string;
timestamp: string;
}
// --- Batch Image Gen API Types ---
export interface CreateMediaJobRequest {
sessionId?: string;
items: Array<{
prompt: string;
aspectRatio?: string;
imageSize?: string;
model?: string;
tags?: string[];
sourceRefs?: string[];
}>;
batchConfig?: Partial<BatchConfig>;
stylePrompt?: string;
docPaths?: string[];
}
export interface PlanMediaJobRequest {
docPaths?: string[];
docContent?: string;
stylePrompt: string;
sessionId?: string;
count?: number;
}
export interface UpdateMediaJobItemsRequest {
items: Array<{
id: string;
prompt?: string;
aspectRatio?: string;
imageSize?: string;
tags?: string[];
}>;
}
export interface MediaJobResponse {
job: MediaJob;
items: MediaJobItem[];
}
export interface MediaJobListResponse {
jobs: MediaJob[];
}
export interface ClaudeStreamOptions {
prompt: string;
sessionId: string;