@dickpy/dsh-imagegen 1.5.2 → 1.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -2
- package/lib/client.js +5803 -1942
- package/lib/client.js.map +1 -1
- package/lib/index.js +1151 -33
- package/package.json +1 -1
- package/src/canvas-store.ts +376 -0
- package/src/client/CanvasWorkspace.tsx +1569 -0
- package/src/client/ImageGenPanel.tsx +164 -96
- package/src/client/SettingsCard.tsx +182 -4
- package/src/client/api.ts +48 -1
- package/src/client/canvas-workspace.module.css +929 -0
- package/src/client/helpers.ts +71 -33
- package/src/client/index.ts +59 -7
- package/src/client/locales.ts +1452 -772
- package/src/client/panel.module.css +83 -33
- package/src/client/use-language.ts +14 -0
- package/src/engine.ts +302 -12
- package/src/gallery-store.ts +5 -0
- package/src/generation-runtime.ts +1 -0
- package/src/history-store.ts +5 -0
- package/src/index.ts +71 -4
- package/src/model-catalog.ts +10 -1
- package/src/presets.ts +15 -0
- package/src/protocol.ts +121 -1
- package/src/routes.ts +231 -1
- package/src/storage-sync.ts +105 -0
package/src/index.ts
CHANGED
|
@@ -8,9 +8,10 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { Context } from '@deepseek-ai/cordis'
|
|
11
|
+
import { readFileSync } from 'node:fs'
|
|
12
|
+
import path from 'node:path'
|
|
11
13
|
import { installSettingsSectionCompat, settingsNamespaceCompat } from './settings-compat.ts'
|
|
12
|
-
import z from 'schemastery'
|
|
13
|
-
// Type-only: pulls the webServer Context merge (route registration).
|
|
14
|
+
import z from 'schemastery'// Type-only: pulls the webServer Context merge (route registration).
|
|
14
15
|
import type {} from '@deepseek-ai/dsh-host-webserver'
|
|
15
16
|
// Type-only: pulls the systemPrompt Context merge (announcement section).
|
|
16
17
|
import type {} from '@deepseek-ai/dsh-system-prompt'
|
|
@@ -21,6 +22,18 @@ import type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment'
|
|
|
21
22
|
import { IMAGEGEN_SETTINGS_NAMESPACE, type ChannelConfig, type ModelMapping } from './protocol.ts'
|
|
22
23
|
import { makeRoutes, type SettingsSeam } from './routes.ts'
|
|
23
24
|
import { syncAllTemplates } from './templates-store.ts'
|
|
25
|
+
import { setStorageSyncHandler, putObject, type StorageSyncConfig } from './storage-sync.ts'
|
|
26
|
+
|
|
27
|
+
/** Content type for a saved image file name (object uploads). */
|
|
28
|
+
function mimeOfPath(filePath: string): string {
|
|
29
|
+
switch (path.extname(filePath).toLowerCase()) {
|
|
30
|
+
case '.jpg':
|
|
31
|
+
case '.jpeg': return 'image/jpeg'
|
|
32
|
+
case '.webp': return 'image/webp'
|
|
33
|
+
case '.gif': return 'image/gif'
|
|
34
|
+
default: return 'image/png'
|
|
35
|
+
}
|
|
36
|
+
}
|
|
24
37
|
import { ImageGenerationRuntime, type ChannelsView, type RuntimeChannel } from './generation-runtime.ts'
|
|
25
38
|
import { registerAgentImageTools } from './agent-image-tools.ts'
|
|
26
39
|
import { registerEditImageCommand } from './edit-image-command.ts'
|
|
@@ -42,6 +55,7 @@ export { latestSessionImage, registerEditImageCommand } from './edit-image-comma
|
|
|
42
55
|
export { appendGallery, clearGallery, listGallery, readGalleryImage, removeGallery, updateGalleryTags } from './gallery-store.ts'
|
|
43
56
|
export { listTemplates, readTemplateImage, refreshTemplates, sampleTemplates, syncAllTemplates, clearTemplateMemo } from './templates-store.ts'
|
|
44
57
|
export { addTemplateFavorite, clearTemplateFavoritesMemo, listTemplateFavorites, removeTemplateFavorite } from './template-favorites.ts'
|
|
58
|
+
export { putObject, setStorageSyncHandler, testStorage, type StorageSyncConfig } from './storage-sync.ts'
|
|
45
59
|
export { checkForUpdate, clearUpdateCache, compareVersions, CURRENT_VERSION, installUpdate, profileFromProcess } from './updater.ts'
|
|
46
60
|
|
|
47
61
|
/** The branded settings namespace of this plugin (the card edits it). */
|
|
@@ -75,6 +89,22 @@ export interface Config {
|
|
|
75
89
|
promptApiKey?: string
|
|
76
90
|
/** Chat model used to expand short image prompts. */
|
|
77
91
|
promptModel?: string
|
|
92
|
+
/** Sync saved images to an S3-compatible object store (COS / OSS / Qiniu S3 …). */
|
|
93
|
+
storageEnabled?: boolean
|
|
94
|
+
/** S3-compatible endpoint URL including the bucket (virtual-hosted or path style). */
|
|
95
|
+
storageEndpoint?: string
|
|
96
|
+
/** Provider region for SigV4 scope, e.g. ap-guangzhou / oss-cn-hangzhou. */
|
|
97
|
+
storageRegion?: string
|
|
98
|
+
/** Object key prefix, default 'dsh-imagegen'. */
|
|
99
|
+
storagePrefix?: string
|
|
100
|
+
/** S3 access key id. */
|
|
101
|
+
storageAccessKey?: string
|
|
102
|
+
/** S3 secret access key (stored redacted). */
|
|
103
|
+
storageSecretKey?: string
|
|
104
|
+
/** Upload gallery additions (default on when storage is enabled). */
|
|
105
|
+
storageSyncGallery?: boolean
|
|
106
|
+
/** Also upload history images. */
|
|
107
|
+
storageSyncHistory?: boolean
|
|
78
108
|
/* ----- deprecated legacy single-endpoint fields (migrated to channels) ----- */
|
|
79
109
|
/** Legacy base URL; synthesized into the default channel on upgrade. */
|
|
80
110
|
apiUrl?: string
|
|
@@ -103,6 +133,14 @@ export const Config: z<Config> = z.object({
|
|
|
103
133
|
promptApiUrl: z.string().default(''),
|
|
104
134
|
promptApiKey: z.string().role('secret').default(''),
|
|
105
135
|
promptModel: z.string().default(''),
|
|
136
|
+
storageEnabled: z.boolean().default(false),
|
|
137
|
+
storageEndpoint: z.string().default(''),
|
|
138
|
+
storageRegion: z.string().default(''),
|
|
139
|
+
storagePrefix: z.string().default('dsh-imagegen'),
|
|
140
|
+
storageAccessKey: z.string().default(''),
|
|
141
|
+
storageSecretKey: z.string().role('secret').default(''),
|
|
142
|
+
storageSyncGallery: z.boolean().default(true),
|
|
143
|
+
storageSyncHistory: z.boolean().default(false),
|
|
106
144
|
apiUrl: z.string().default(''),
|
|
107
145
|
apiKey: z.string().role('secret').default(''),
|
|
108
146
|
imageModels: z.array(z.string()).default([]),
|
|
@@ -117,7 +155,7 @@ const DEFAULT_ALLOW_AGENT_IMAGE_GENERATION = true
|
|
|
117
155
|
const SECTION_ORDER = 150
|
|
118
156
|
|
|
119
157
|
/** Model-facing announcement: plugin presence, capabilities, and limits. */
|
|
120
|
-
export const IMAGEGEN_GUIDANCE = '本机已安装 dsh-imagegen 插件(DSH AI 生图):侧边栏「AI 生图」入口。能力:通过「渠道」对接 OpenAI 兼容图像生成 API(每个渠道 = 一个 API 端点 + 各自的模型目录),支持文生图(/images/generations)与图生图(/images/edits,上传参考图,grok-imagine 模型按官方 JSON image_url 协议发送,nanobanana 系列按 aspect_ratio / image_size 参数协议发送;seedream 系列统一走 /images/generations,参考图以 JSON image 数组发送;智谱 `glm-image` 使用官方 `/api/paas/v4/images/generations
|
|
158
|
+
export const IMAGEGEN_GUIDANCE = '本机已安装 dsh-imagegen 插件(DSH AI 生图):侧边栏「AI 生图」入口。能力:通过「渠道」对接 OpenAI 兼容图像生成 API(每个渠道 = 一个 API 端点 + 各自的模型目录),支持文生图(/images/generations)与图生图(/images/edits,上传参考图,grok-imagine 模型按官方 JSON image_url 协议发送,nanobanana 系列按 aspect_ratio / image_size 参数协议发送;seedream 系列统一走 /images/generations,参考图以 JSON image 数组发送;智谱 `glm-image` 使用官方 `/api/paas/v4/images/generations`,当前仅支持文生图;qwen-image 系列使用阿里云 DashScope 原生接口(api_url 填 https://dashscope.aliyuncs.com/api/v1,不支持 OpenAI 兼容模式,该渠道不可复用于提示词增强,尺寸自动映射为宽*高)。API 地址与密钥在 GUI 设置中按渠道配置,密钥仅存于本机设置文档;生成请求由本地宿主代理转发,结果以 base64 返回面板,可预览与下载。模型只能使用用户在各渠道配置目录中的模型;检测模型时会过滤聊天、Embedding 等非图片模型,但模型出现在 /models 中仍不等于其网关原生支持生图协议,遇到 Qwen、Gemini 等非 OpenAI 生图协议时应如实说明上游兼容性。可一键把满意的图片加入「画廊」。内置「提示词模板库」(面板提示词框左下角「模板库」按钮):多来源标签页(精选案例库 / 沧河案例库,后续可扩展),打包 awesome-gpt-image-2 的数百条提示词案例,可搜索、筛选、收藏(星标,宿主持久化)与复用;各来源列表独立刷新,宿主每 12 小时后台自动同步一次。Agent 可直接调用 `generate_image` 提交文生图,也可用 `edit_image` 图生图;默认保持工具调用等待直到任务完成,完成图片显示在工具调用对应的左侧结果区域,模型收到状态和附件引用,不会额外伪造用户消息。用户也可以使用 `/edit_image <修改描述>`,命令会直接读取当前对话最近图片并调用插件图片模型,不经过对话模型的图片能力检查。若明确需要后台执行,可传 `wait_for_completion: false`,之后再用 `get_image_generation_task` 查询;不要反复轮询。限制:生成消耗上游 API 额度;图片内容由上游模型生成,可能不符合预期或包含不适宜内容;api_key 以明文存储在设置文档中;参考图会发送至所配置的 API 服务;模板库在线刷新与参考图首次加载需要访问对应来源站点(vibeui.top / gpt-image2.canghe.ai)。用户提到「生图 / 绘画 / 生成图片 / 文生图 / 图生图 / 画廊 / 提示词模板」时即指本插件,请据此协作。'
|
|
121
159
|
|
|
122
160
|
/** Append the live channel × model table so an Agent can honor user choices. */
|
|
123
161
|
function guidanceFor(channels: RuntimeChannel[], defaultChannelId: string): string {
|
|
@@ -175,6 +213,7 @@ export interface EffectiveConfig {
|
|
|
175
213
|
promptApiUrl: string
|
|
176
214
|
promptApiKey: string
|
|
177
215
|
promptModel: string
|
|
216
|
+
storage: StorageSyncConfig & { enabled: boolean; syncGallery: boolean; syncHistory: boolean }
|
|
178
217
|
}
|
|
179
218
|
|
|
180
219
|
/**
|
|
@@ -182,7 +221,7 @@ export interface EffectiveConfig {
|
|
|
182
221
|
* @param ctx - host plugin context carrying webServer/systemPrompt.
|
|
183
222
|
* @param config - resolved plugin config (schema defaults applied by the loader).
|
|
184
223
|
*/
|
|
185
|
-
export function apply(ctx: Context, config?: Config): void {
|
|
224
|
+
export function apply(ctx: Context, config?: Config): (() => void) | void {
|
|
186
225
|
// The live source the surfaces read: the settings section once the settings
|
|
187
226
|
// service is attached, the composition entry otherwise.
|
|
188
227
|
let current: () => Config = () => config ?? {}
|
|
@@ -226,6 +265,16 @@ export function apply(ctx: Context, config?: Config): void {
|
|
|
226
265
|
promptApiUrl: typeof value.promptApiUrl === 'string' ? value.promptApiUrl.trim() : '',
|
|
227
266
|
promptApiKey: typeof value.promptApiKey === 'string' ? value.promptApiKey.trim() : '',
|
|
228
267
|
promptModel: typeof value.promptModel === 'string' ? value.promptModel.trim() : '',
|
|
268
|
+
storage: {
|
|
269
|
+
enabled: value.storageEnabled ?? false,
|
|
270
|
+
endpoint: typeof value.storageEndpoint === 'string' ? value.storageEndpoint.trim() : '',
|
|
271
|
+
region: typeof value.storageRegion === 'string' ? value.storageRegion.trim() : '',
|
|
272
|
+
accessKey: typeof value.storageAccessKey === 'string' ? value.storageAccessKey.trim() : '',
|
|
273
|
+
secretKey: typeof value.storageSecretKey === 'string' ? value.storageSecretKey.trim() : '',
|
|
274
|
+
prefix: typeof value.storagePrefix === 'string' && value.storagePrefix.trim() !== '' ? value.storagePrefix.trim() : 'dsh-imagegen',
|
|
275
|
+
syncGallery: value.storageSyncGallery ?? true,
|
|
276
|
+
syncHistory: value.storageSyncHistory ?? false,
|
|
277
|
+
},
|
|
229
278
|
}
|
|
230
279
|
}
|
|
231
280
|
|
|
@@ -236,6 +285,21 @@ export function apply(ctx: Context, config?: Config): void {
|
|
|
236
285
|
return { channels: value.channels, defaultChannelId: value.defaultChannelId }
|
|
237
286
|
}
|
|
238
287
|
|
|
288
|
+
// Object-storage sync: the image stores announce every file they write; the
|
|
289
|
+
// handler resolves the live settings and uploads when enabled. Fire and
|
|
290
|
+
// forget — a sync failure never blocks the save path.
|
|
291
|
+
setStorageSyncHandler((kind, filePath) => {
|
|
292
|
+
const storage = resolve().storage
|
|
293
|
+
if (!storage.enabled || !storage.endpoint.trim() || storage.secretKey.trim() === '') return
|
|
294
|
+
if (kind === 'gallery' && !storage.syncGallery) return
|
|
295
|
+
if (kind === 'history' && !storage.syncHistory) return
|
|
296
|
+
const key = `${storage.prefix}/${kind === 'gallery' ? 'gallery' : 'images'}/${path.basename(filePath)}`
|
|
297
|
+
const data = readFileSync(filePath)
|
|
298
|
+
void putObject(storage, key, data, mimeOfPath(filePath)).catch(() => {
|
|
299
|
+
// Best-effort sync: surfaced through the settings test, never fatal here.
|
|
300
|
+
})
|
|
301
|
+
})
|
|
302
|
+
|
|
239
303
|
// Browser endpoints and Agent tools share the exact same serial queue. This
|
|
240
304
|
// keeps image persistence, cancellation, and retries coherent across both
|
|
241
305
|
// entry points; Agent tools wait for their task result by default and render
|
|
@@ -276,6 +340,7 @@ export function apply(ctx: Context, config?: Config): void {
|
|
|
276
340
|
attachments: sctx.attachments,
|
|
277
341
|
pendingConversationImages,
|
|
278
342
|
runtime,
|
|
343
|
+
resolveStorage: () => resolve().storage,
|
|
279
344
|
})
|
|
280
345
|
const disposers = routes.map(route => ctx.webServer.register(route))
|
|
281
346
|
// Background template sync: the upstream sources update on their own
|
|
@@ -354,4 +419,6 @@ export function apply(ctx: Context, config?: Config): void {
|
|
|
354
419
|
// Initial registration from the composition entry (covers deployments with
|
|
355
420
|
// no settings service, whose installSettingsSection never fires its hooks).
|
|
356
421
|
sync()
|
|
422
|
+
|
|
423
|
+
return () => { setStorageSyncHandler(undefined) }
|
|
357
424
|
}
|
package/src/model-catalog.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Framework-free (pure data + regex), safe for the client bundle to inline.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
export type ModelFamily = 'gpt-image' | 'dall-e' | 'grok' | 'nanobanana' | 'seedream' | 'zhipu' | 'unknown'
|
|
11
|
+
export type ModelFamily = 'gpt-image' | 'dall-e' | 'grok' | 'nanobanana' | 'seedream' | 'zhipu' | 'qwen' | 'unknown'
|
|
12
12
|
|
|
13
13
|
/** Capability/identity annotation for one model id. */
|
|
14
14
|
export interface ModelCatalogEntry {
|
|
@@ -77,6 +77,14 @@ const ENTRIES: Record<Exclude<ModelFamily, 'unknown'>, Omit<ModelCatalogEntry, '
|
|
|
77
77
|
supportsAspectRatio: false,
|
|
78
78
|
qualityTiers: ['HD'],
|
|
79
79
|
},
|
|
80
|
+
qwen: {
|
|
81
|
+
label: 'qwen-image',
|
|
82
|
+
labelZh: '千问图像',
|
|
83
|
+
known: true,
|
|
84
|
+
supportsEdit: true,
|
|
85
|
+
supportsAspectRatio: true,
|
|
86
|
+
qualityTiers: ['auto'],
|
|
87
|
+
},
|
|
80
88
|
}
|
|
81
89
|
|
|
82
90
|
/** Official Gemini image ids served by Nano Banana gateways. */
|
|
@@ -98,6 +106,7 @@ export function describeModel(model: string): ModelCatalogEntry {
|
|
|
98
106
|
if (/^nanobanana/i.test(id) || NANOBANANA_GEMINI_IDS.has(id)) return { family: 'nanobanana', ...ENTRIES.nanobanana }
|
|
99
107
|
if (/^(?:doubao-)?seedream/i.test(id)) return { family: 'seedream', ...ENTRIES.seedream }
|
|
100
108
|
if (/^(?:glm-image|cogview(?:-|$))/i.test(id)) return { family: 'zhipu', ...ENTRIES.zhipu }
|
|
109
|
+
if (/^qwen-image(?:[-_.]|$)/i.test(id)) return { family: 'qwen', ...ENTRIES.qwen }
|
|
101
110
|
return { family: 'unknown', label: 'unknown', labelZh: '未知协议', known: false, supportsEdit: true, supportsAspectRatio: false, qualityTiers: [] }
|
|
102
111
|
}
|
|
103
112
|
|
package/src/presets.ts
CHANGED
|
@@ -54,6 +54,21 @@ export const IMAGE_PRESETS: PresetProvider[] = [
|
|
|
54
54
|
{ alias: 'glm-image', id: 'glm-image' },
|
|
55
55
|
],
|
|
56
56
|
},
|
|
57
|
+
{
|
|
58
|
+
id: 'aliyun-dashscope-qwen',
|
|
59
|
+
name: '阿里云百炼(Qwen-Image)',
|
|
60
|
+
apiUrl: 'https://dashscope.aliyuncs.com/api/v1',
|
|
61
|
+
hint: '阿里云百炼 DashScope 原生接口:通义千问 Qwen-Image 系列(该渠道不可复用于提示词增强)',
|
|
62
|
+
models: [
|
|
63
|
+
{ alias: 'qwen-image-3.0-pro', id: 'qwen-image-3.0-pro' },
|
|
64
|
+
{ alias: 'qwen-image-3.0', id: 'qwen-image-3.0' },
|
|
65
|
+
{ alias: 'qwen-image-2.0-pro', id: 'qwen-image-2.0-pro' },
|
|
66
|
+
{ alias: 'qwen-image-2.0', id: 'qwen-image-2.0' },
|
|
67
|
+
{ alias: 'qwen-image-max', id: 'qwen-image-max' },
|
|
68
|
+
{ alias: 'qwen-image-plus', id: 'qwen-image-plus' },
|
|
69
|
+
{ alias: 'qwen-image', id: 'qwen-image' },
|
|
70
|
+
],
|
|
71
|
+
},
|
|
57
72
|
{
|
|
58
73
|
id: 'xai-grok',
|
|
59
74
|
name: 'xAI(Grok)',
|
package/src/protocol.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
export const IMAGEGEN_SETTINGS_NAMESPACE = 'dsh-imagegen'
|
|
9
9
|
|
|
10
10
|
/** Published package version shared by the host updater and the client UI. */
|
|
11
|
-
export const PLUGIN_VERSION = '1.5.
|
|
11
|
+
export const PLUGIN_VERSION = '1.5.4'
|
|
12
12
|
|
|
13
13
|
/** Same-origin route family (loopback-only, mirroring the dsh-ssh fence). */
|
|
14
14
|
export const SETTINGS_API = {
|
|
@@ -54,6 +54,14 @@ export const TASK_API = {
|
|
|
54
54
|
retry: '/api/dsh-imagegen/tasks/retry',
|
|
55
55
|
} as const
|
|
56
56
|
|
|
57
|
+
/** Reveal the host data directory (saved images) in the OS file manager. */
|
|
58
|
+
export const DATA_FOLDER_API = '/api/dsh-imagegen/data-folder/open' as const
|
|
59
|
+
|
|
60
|
+
/** Probe the configured S3-compatible object storage. */
|
|
61
|
+
export const STORAGE_API = {
|
|
62
|
+
test: '/api/dsh-imagegen/storage/test',
|
|
63
|
+
} as const
|
|
64
|
+
|
|
57
65
|
/** Host-mediated GitHub Release update routes. */
|
|
58
66
|
export const UPDATE_API = {
|
|
59
67
|
check: '/api/dsh-imagegen/update/check',
|
|
@@ -88,6 +96,18 @@ export const GALLERY_API = {
|
|
|
88
96
|
image: '/api/dsh-imagegen/gallery/image',
|
|
89
97
|
} as const
|
|
90
98
|
|
|
99
|
+
/** Host-persisted infinite canvas projects and their content-addressed assets. */
|
|
100
|
+
export const CANVAS_API = {
|
|
101
|
+
list: '/api/dsh-imagegen/canvas/list',
|
|
102
|
+
create: '/api/dsh-imagegen/canvas/create',
|
|
103
|
+
read: '/api/dsh-imagegen/canvas/read',
|
|
104
|
+
save: '/api/dsh-imagegen/canvas/save',
|
|
105
|
+
remove: '/api/dsh-imagegen/canvas/remove',
|
|
106
|
+
assetUpload: '/api/dsh-imagegen/canvas/asset/upload',
|
|
107
|
+
assetImport: '/api/dsh-imagegen/canvas/asset/import',
|
|
108
|
+
asset: '/api/dsh-imagegen/canvas/asset',
|
|
109
|
+
} as const
|
|
110
|
+
|
|
91
111
|
/** Maximum number of history entries retained host-side (oldest evicted). */
|
|
92
112
|
export const HISTORY_MAX = 50
|
|
93
113
|
|
|
@@ -226,6 +246,100 @@ export interface TemplateFavorite {
|
|
|
226
246
|
/** Generation modes. */
|
|
227
247
|
export type GenerateMode = 'text' | 'edit'
|
|
228
248
|
|
|
249
|
+
/** Origin information carried by a generation started from the canvas. */
|
|
250
|
+
export interface CanvasTaskMeta {
|
|
251
|
+
canvasId: string
|
|
252
|
+
sourceNodeId?: string
|
|
253
|
+
/** Legacy v1 annotation workflow; kept so old history entries still parse. */
|
|
254
|
+
annotationNodeId?: string
|
|
255
|
+
parentNodeId?: string
|
|
256
|
+
placement?: 'right' | 'below'
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** One image asset referenced by a canvas node. */
|
|
260
|
+
export interface CanvasAssetRef {
|
|
261
|
+
assetId: string
|
|
262
|
+
url: string
|
|
263
|
+
mime: string
|
|
264
|
+
bytes: number
|
|
265
|
+
width: number
|
|
266
|
+
height: number
|
|
267
|
+
origin: 'upload' | 'history' | 'gallery' | 'generated'
|
|
268
|
+
originId?: string
|
|
269
|
+
entryId?: string
|
|
270
|
+
imageIndex?: number
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export type CanvasNodeType = 'image' | 'text' | 'config'
|
|
274
|
+
|
|
275
|
+
export interface CanvasViewport {
|
|
276
|
+
x: number
|
|
277
|
+
y: number
|
|
278
|
+
/** Zoom factor. */
|
|
279
|
+
k: number
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** Free-form per-node state, mirroring the node-graph canvas model. */
|
|
283
|
+
export interface CanvasNodeMetadata {
|
|
284
|
+
/** Image nodes: the rendered asset. */
|
|
285
|
+
asset?: CanvasAssetRef
|
|
286
|
+
status?: 'idle' | 'generating' | 'success' | 'error'
|
|
287
|
+
error?: string
|
|
288
|
+
/** Config/image nodes: generation settings. */
|
|
289
|
+
prompt?: string
|
|
290
|
+
model?: string
|
|
291
|
+
size?: string
|
|
292
|
+
quality?: string
|
|
293
|
+
/** Config nodes: how many images to generate (1-4). */
|
|
294
|
+
count?: number
|
|
295
|
+
/** Config nodes: generation mode (image) or plain writing (text). */
|
|
296
|
+
mode?: 'image' | 'text'
|
|
297
|
+
taskId?: string
|
|
298
|
+
sourceNodeId?: string
|
|
299
|
+
/** Text nodes. */
|
|
300
|
+
text?: string
|
|
301
|
+
fontSize?: number
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export interface CanvasNode {
|
|
305
|
+
id: string
|
|
306
|
+
type: CanvasNodeType
|
|
307
|
+
title: string
|
|
308
|
+
x: number
|
|
309
|
+
y: number
|
|
310
|
+
width: number
|
|
311
|
+
height: number
|
|
312
|
+
metadata?: CanvasNodeMetadata
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export interface CanvasConnection {
|
|
316
|
+
id: string
|
|
317
|
+
fromNodeId: string
|
|
318
|
+
toNodeId: string
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export interface CanvasDocument {
|
|
322
|
+
version: 2
|
|
323
|
+
id: string
|
|
324
|
+
title: string
|
|
325
|
+
revision: number
|
|
326
|
+
viewport: CanvasViewport
|
|
327
|
+
background: 'dots' | 'lines' | 'blank'
|
|
328
|
+
nodes: CanvasNode[]
|
|
329
|
+
connections: CanvasConnection[]
|
|
330
|
+
createdAt: number
|
|
331
|
+
updatedAt: number
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export interface CanvasSummary {
|
|
335
|
+
id: string
|
|
336
|
+
title: string
|
|
337
|
+
revision: number
|
|
338
|
+
nodeCount: number
|
|
339
|
+
createdAt: number
|
|
340
|
+
updatedAt: number
|
|
341
|
+
}
|
|
342
|
+
|
|
229
343
|
/** Metadata shared by the ecommerce product-set workflow. */
|
|
230
344
|
export interface EcommerceTaskMeta {
|
|
231
345
|
workflow?: 'ecommerce'
|
|
@@ -257,6 +371,8 @@ export interface ProductSetDraft {
|
|
|
257
371
|
category: string
|
|
258
372
|
platform: string
|
|
259
373
|
language: string
|
|
374
|
+
/** Custom copy language when language is 'custom'. */
|
|
375
|
+
customLanguage?: string
|
|
260
376
|
size: string
|
|
261
377
|
productName: string
|
|
262
378
|
sellingPoints: string
|
|
@@ -311,6 +427,8 @@ export interface GenerateRequest extends EcommerceTaskMeta {
|
|
|
311
427
|
comparisonId?: string
|
|
312
428
|
/** All model aliases selected for one comparison run. */
|
|
313
429
|
comparisonModels?: string[]
|
|
430
|
+
/** Optional canvas lineage metadata. */
|
|
431
|
+
canvas?: CanvasTaskMeta
|
|
314
432
|
}
|
|
315
433
|
|
|
316
434
|
/** One generated image, normalized host-side to base64 so the browser never
|
|
@@ -428,6 +546,7 @@ export interface HistoryEntry extends EcommerceTaskMeta {
|
|
|
428
546
|
comparisonId?: string
|
|
429
547
|
/** Model aliases included in the comparison run. */
|
|
430
548
|
comparisonModels?: string[]
|
|
549
|
+
canvas?: CanvasTaskMeta
|
|
431
550
|
}
|
|
432
551
|
|
|
433
552
|
/** A history entry the client submits for persistence (images still carry base64). */
|
|
@@ -451,4 +570,5 @@ export interface HistoryEntryInput extends EcommerceTaskMeta {
|
|
|
451
570
|
comparisonId?: string
|
|
452
571
|
/** Model aliases included in the comparison run. */
|
|
453
572
|
comparisonModels?: string[]
|
|
573
|
+
canvas?: CanvasTaskMeta
|
|
454
574
|
}
|