@dickpy/dsh-imagegen 1.0.7 → 1.0.19
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 +28 -9
- package/docs/images/prompt-template-library.png +0 -0
- package/lib/client.js +1739 -408
- package/lib/client.js.map +1 -1
- package/lib/index.js +797 -38
- package/package.json +5 -4
- package/src/client/ImageGenPanel.tsx +476 -61
- package/src/client/TemplateLibrary.tsx +336 -0
- package/src/client/api.ts +58 -1
- package/src/client/locales.ts +127 -22
- package/src/client/mount.tsx +11 -6
- package/src/client/panel.module.css +245 -8
- package/src/client/templates.module.css +453 -0
- package/src/engine.ts +78 -11
- package/src/gallery-store.ts +266 -0
- package/src/index.ts +3 -1
- package/src/protocol.ts +79 -5
- package/src/routes.ts +191 -1
- package/src/templates/cases.json +10196 -0
- package/src/templates-store.ts +278 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt-template library styles. Same conventions as panel.module.css:
|
|
3
|
+
* scoped box-sizing, dsh --dsw-* tokens, no leakage outside the overlay
|
|
4
|
+
* subtree.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
.overlay,
|
|
8
|
+
.overlay *,
|
|
9
|
+
.overlay *::before,
|
|
10
|
+
.overlay *::after {
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.overlay {
|
|
15
|
+
position: fixed;
|
|
16
|
+
inset: 0;
|
|
17
|
+
z-index: 130;
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
padding: 28px;
|
|
22
|
+
background: var(--dsw-alias-bg-mask-1);
|
|
23
|
+
color: var(--dsw-alias-label-primary);
|
|
24
|
+
font-family: var(--dsw-font-family);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.shell {
|
|
28
|
+
display: flex;
|
|
29
|
+
flex-direction: column;
|
|
30
|
+
width: min(1180px, 100%);
|
|
31
|
+
height: 100%;
|
|
32
|
+
max-height: 100%;
|
|
33
|
+
overflow: hidden;
|
|
34
|
+
border: 1px solid var(--dsw-alias-border-l1);
|
|
35
|
+
border-radius: 14px;
|
|
36
|
+
background: var(--dsw-alias-bg-base);
|
|
37
|
+
box-shadow: 0 18px 60px rgba(0, 0, 0, 0.28);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* --- header ------------------------------------------------------------ */
|
|
41
|
+
|
|
42
|
+
.header {
|
|
43
|
+
display: flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
justify-content: space-between;
|
|
46
|
+
gap: 12px;
|
|
47
|
+
padding: 14px 18px 10px;
|
|
48
|
+
flex: none;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.heading {
|
|
52
|
+
display: flex;
|
|
53
|
+
align-items: baseline;
|
|
54
|
+
gap: 10px;
|
|
55
|
+
min-width: 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.title {
|
|
59
|
+
margin: 0;
|
|
60
|
+
font-size: 15px;
|
|
61
|
+
font-weight: 650;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.meta {
|
|
65
|
+
font-size: 12px;
|
|
66
|
+
color: var(--dsw-alias-label-tertiary);
|
|
67
|
+
white-space: nowrap;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.headerActions {
|
|
71
|
+
display: inline-flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
gap: 8px;
|
|
74
|
+
flex: none;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.close {
|
|
78
|
+
display: inline-flex;
|
|
79
|
+
align-items: center;
|
|
80
|
+
justify-content: center;
|
|
81
|
+
width: 28px;
|
|
82
|
+
height: 28px;
|
|
83
|
+
border: none;
|
|
84
|
+
border-radius: 8px;
|
|
85
|
+
background: transparent;
|
|
86
|
+
color: var(--dsw-alias-label-secondary);
|
|
87
|
+
cursor: pointer;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.close:hover {
|
|
91
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
92
|
+
color: var(--dsw-alias-label-primary);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* --- toolbar ----------------------------------------------------------- */
|
|
96
|
+
|
|
97
|
+
.toolbar {
|
|
98
|
+
display: flex;
|
|
99
|
+
flex-direction: column;
|
|
100
|
+
gap: 10px;
|
|
101
|
+
padding: 0 18px 12px;
|
|
102
|
+
flex: none;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.search {
|
|
106
|
+
width: 100%;
|
|
107
|
+
height: 34px;
|
|
108
|
+
padding: 0 12px;
|
|
109
|
+
border: 1px solid var(--dsw-alias-border-l1);
|
|
110
|
+
border-radius: 9px;
|
|
111
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
112
|
+
color: var(--dsw-alias-label-primary);
|
|
113
|
+
font-size: 13px;
|
|
114
|
+
font-family: inherit;
|
|
115
|
+
outline: none;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.search:focus {
|
|
119
|
+
border-color: var(--dsw-alias-brand-primary);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.search::placeholder {
|
|
123
|
+
color: var(--dsw-alias-label-dimmed);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.categoryRow {
|
|
127
|
+
display: flex;
|
|
128
|
+
flex-wrap: wrap;
|
|
129
|
+
gap: 6px;
|
|
130
|
+
max-height: 64px;
|
|
131
|
+
overflow-y: auto;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.categoryPill {
|
|
135
|
+
height: 26px;
|
|
136
|
+
padding: 0 11px;
|
|
137
|
+
border: 1px solid var(--dsw-alias-border-l1);
|
|
138
|
+
border-radius: 999px;
|
|
139
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
140
|
+
color: var(--dsw-alias-label-secondary);
|
|
141
|
+
font-size: 12px;
|
|
142
|
+
font-family: inherit;
|
|
143
|
+
cursor: pointer;
|
|
144
|
+
white-space: nowrap;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.categoryPill:hover {
|
|
148
|
+
color: var(--dsw-alias-label-primary);
|
|
149
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.categoryPill[data-active] {
|
|
153
|
+
border-color: var(--dsw-alias-brand-primary);
|
|
154
|
+
background: var(--dsw-alias-brand-primary);
|
|
155
|
+
color: var(--dsw-alias-bg-base);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.notice {
|
|
159
|
+
margin: 0 18px 10px;
|
|
160
|
+
padding: 8px 12px;
|
|
161
|
+
border: 1px solid var(--dsw-alias-border-l1);
|
|
162
|
+
border-radius: 9px;
|
|
163
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
164
|
+
color: var(--dsw-alias-label-secondary);
|
|
165
|
+
font-size: 12px;
|
|
166
|
+
flex: none;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* --- body / grid -------------------------------------------------------- */
|
|
170
|
+
|
|
171
|
+
.body {
|
|
172
|
+
flex: 1;
|
|
173
|
+
min-height: 0;
|
|
174
|
+
overflow-y: auto;
|
|
175
|
+
padding: 2px 18px 14px;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.state {
|
|
179
|
+
display: flex;
|
|
180
|
+
flex-direction: column;
|
|
181
|
+
align-items: center;
|
|
182
|
+
justify-content: center;
|
|
183
|
+
gap: 12px;
|
|
184
|
+
height: 100%;
|
|
185
|
+
min-height: 220px;
|
|
186
|
+
color: var(--dsw-alias-label-tertiary);
|
|
187
|
+
font-size: 13px;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.spinner {
|
|
191
|
+
width: 26px;
|
|
192
|
+
height: 26px;
|
|
193
|
+
border: 2px solid var(--dsw-alias-border-l2);
|
|
194
|
+
border-top-color: var(--dsw-alias-brand-primary);
|
|
195
|
+
border-radius: 50%;
|
|
196
|
+
animation: dsh-imagegen-templates-spin 0.9s linear infinite;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
@keyframes dsh-imagegen-templates-spin {
|
|
200
|
+
to { transform: rotate(360deg); }
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.grid {
|
|
204
|
+
display: grid;
|
|
205
|
+
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
|
206
|
+
gap: 12px;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.card {
|
|
210
|
+
display: flex;
|
|
211
|
+
flex-direction: column;
|
|
212
|
+
gap: 0;
|
|
213
|
+
padding: 0;
|
|
214
|
+
overflow: hidden;
|
|
215
|
+
border: 1px solid var(--dsw-alias-border-l1);
|
|
216
|
+
border-radius: 11px;
|
|
217
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
218
|
+
cursor: pointer;
|
|
219
|
+
text-align: left;
|
|
220
|
+
font-family: inherit;
|
|
221
|
+
color: var(--dsw-alias-label-primary);
|
|
222
|
+
transition: border-color 0.15s ease, transform 0.15s ease;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.card:hover {
|
|
226
|
+
border-color: var(--dsw-alias-brand-primary);
|
|
227
|
+
transform: translateY(-1px);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.thumbWrap {
|
|
231
|
+
position: relative;
|
|
232
|
+
display: block;
|
|
233
|
+
width: 100%;
|
|
234
|
+
aspect-ratio: 1 / 1;
|
|
235
|
+
background: var(--dsw-alias-bg-layer-2);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.thumb {
|
|
239
|
+
display: block;
|
|
240
|
+
width: 100%;
|
|
241
|
+
height: 100%;
|
|
242
|
+
object-fit: cover;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.thumbPlaceholder {
|
|
246
|
+
display: flex;
|
|
247
|
+
align-items: center;
|
|
248
|
+
justify-content: center;
|
|
249
|
+
width: 100%;
|
|
250
|
+
height: 100%;
|
|
251
|
+
color: var(--dsw-alias-label-dimmed);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.featuredBadge {
|
|
255
|
+
position: absolute;
|
|
256
|
+
top: 8px;
|
|
257
|
+
left: 8px;
|
|
258
|
+
padding: 2px 8px;
|
|
259
|
+
border-radius: 999px;
|
|
260
|
+
background: var(--dsw-alias-brand-primary);
|
|
261
|
+
color: var(--dsw-alias-bg-base);
|
|
262
|
+
font-size: 11px;
|
|
263
|
+
font-weight: 600;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.cardBody {
|
|
267
|
+
display: flex;
|
|
268
|
+
flex-direction: column;
|
|
269
|
+
gap: 4px;
|
|
270
|
+
padding: 9px 11px 10px;
|
|
271
|
+
min-width: 0;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.cardTitle {
|
|
275
|
+
font-size: 12.5px;
|
|
276
|
+
font-weight: 600;
|
|
277
|
+
line-height: 1.35;
|
|
278
|
+
display: -webkit-box;
|
|
279
|
+
-webkit-line-clamp: 2;
|
|
280
|
+
-webkit-box-orient: vertical;
|
|
281
|
+
overflow: hidden;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.cardMeta {
|
|
285
|
+
display: flex;
|
|
286
|
+
align-items: center;
|
|
287
|
+
gap: 6px;
|
|
288
|
+
min-width: 0;
|
|
289
|
+
font-size: 11px;
|
|
290
|
+
color: var(--dsw-alias-label-tertiary);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.cardCategory {
|
|
294
|
+
flex: none;
|
|
295
|
+
padding: 1px 7px;
|
|
296
|
+
border-radius: 999px;
|
|
297
|
+
background: var(--dsw-alias-bg-layer-2);
|
|
298
|
+
color: var(--dsw-alias-label-secondary);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.cardSource {
|
|
302
|
+
overflow: hidden;
|
|
303
|
+
text-overflow: ellipsis;
|
|
304
|
+
white-space: nowrap;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/* --- footer ------------------------------------------------------------- */
|
|
308
|
+
|
|
309
|
+
.footer {
|
|
310
|
+
flex: none;
|
|
311
|
+
display: flex;
|
|
312
|
+
align-items: center;
|
|
313
|
+
justify-content: space-between;
|
|
314
|
+
gap: 10px;
|
|
315
|
+
padding: 9px 18px;
|
|
316
|
+
border-top: 1px solid var(--dsw-alias-border-l1);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.attribution {
|
|
320
|
+
font-size: 11.5px;
|
|
321
|
+
color: var(--dsw-alias-label-dimmed);
|
|
322
|
+
min-width: 0;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.sourceLink {
|
|
326
|
+
flex: none;
|
|
327
|
+
font-size: 11.5px;
|
|
328
|
+
font-weight: 600;
|
|
329
|
+
color: var(--dsw-alias-brand-primary);
|
|
330
|
+
text-decoration: none;
|
|
331
|
+
white-space: nowrap;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.sourceLink:hover {
|
|
335
|
+
text-decoration: underline;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/* --- detail overlay ----------------------------------------------------- */
|
|
339
|
+
|
|
340
|
+
.detailOverlay {
|
|
341
|
+
position: absolute;
|
|
342
|
+
inset: 0;
|
|
343
|
+
display: flex;
|
|
344
|
+
align-items: center;
|
|
345
|
+
justify-content: center;
|
|
346
|
+
padding: 34px;
|
|
347
|
+
background: var(--dsw-alias-bg-mask-1);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.detail {
|
|
351
|
+
display: grid;
|
|
352
|
+
grid-template-columns: minmax(0, 5fr) minmax(0, 4fr);
|
|
353
|
+
grid-template-rows: minmax(0, 1fr);
|
|
354
|
+
gap: 0;
|
|
355
|
+
width: min(980px, 100%);
|
|
356
|
+
max-height: 100%;
|
|
357
|
+
overflow: hidden;
|
|
358
|
+
border: 1px solid var(--dsw-alias-border-l1);
|
|
359
|
+
border-radius: 14px;
|
|
360
|
+
background: var(--dsw-alias-bg-base);
|
|
361
|
+
box-shadow: 0 18px 60px rgba(0, 0, 0, 0.32);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.detailMedia {
|
|
365
|
+
display: flex;
|
|
366
|
+
align-items: center;
|
|
367
|
+
justify-content: center;
|
|
368
|
+
min-height: 0;
|
|
369
|
+
overflow: hidden;
|
|
370
|
+
background: var(--dsw-alias-bg-layer-2);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.detailImage {
|
|
374
|
+
display: block;
|
|
375
|
+
width: 100%;
|
|
376
|
+
height: 100%;
|
|
377
|
+
object-fit: contain;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.detailInfo {
|
|
381
|
+
display: flex;
|
|
382
|
+
flex-direction: column;
|
|
383
|
+
gap: 10px;
|
|
384
|
+
min-height: 0;
|
|
385
|
+
overflow-y: auto;
|
|
386
|
+
padding: 18px;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.detailTitle {
|
|
390
|
+
margin: 0;
|
|
391
|
+
font-size: 15px;
|
|
392
|
+
font-weight: 650;
|
|
393
|
+
line-height: 1.4;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.detailMeta {
|
|
397
|
+
display: flex;
|
|
398
|
+
align-items: center;
|
|
399
|
+
flex-wrap: wrap;
|
|
400
|
+
gap: 8px;
|
|
401
|
+
font-size: 12px;
|
|
402
|
+
color: var(--dsw-alias-label-tertiary);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.detailLink {
|
|
406
|
+
color: var(--dsw-alias-brand-primary);
|
|
407
|
+
text-decoration: none;
|
|
408
|
+
overflow: hidden;
|
|
409
|
+
text-overflow: ellipsis;
|
|
410
|
+
white-space: nowrap;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.detailLink:hover {
|
|
414
|
+
text-decoration: underline;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
.detailPrompt {
|
|
418
|
+
flex: 1;
|
|
419
|
+
min-height: 0;
|
|
420
|
+
margin: 0;
|
|
421
|
+
padding: 12px;
|
|
422
|
+
overflow-y: auto;
|
|
423
|
+
border: 1px solid var(--dsw-alias-border-l1);
|
|
424
|
+
border-radius: 10px;
|
|
425
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
426
|
+
color: var(--dsw-alias-label-primary);
|
|
427
|
+
font-size: 12.5px;
|
|
428
|
+
line-height: 1.6;
|
|
429
|
+
font-family: inherit;
|
|
430
|
+
white-space: pre-wrap;
|
|
431
|
+
word-break: break-word;
|
|
432
|
+
user-select: text;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.detailActions {
|
|
436
|
+
display: flex;
|
|
437
|
+
gap: 8px;
|
|
438
|
+
flex: none;
|
|
439
|
+
position: sticky;
|
|
440
|
+
bottom: 0;
|
|
441
|
+
margin: 0 -18px -18px;
|
|
442
|
+
padding: 12px 18px;
|
|
443
|
+
background: var(--dsw-alias-bg-base);
|
|
444
|
+
border-top: 1px solid var(--dsw-alias-border-l1);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/* Narrow shells: stack the detail pane. */
|
|
448
|
+
@media (max-width: 760px) {
|
|
449
|
+
.detail {
|
|
450
|
+
grid-template-columns: 1fr;
|
|
451
|
+
grid-template-rows: minmax(0, 3fr) minmax(0, 4fr);
|
|
452
|
+
}
|
|
453
|
+
}
|
package/src/engine.ts
CHANGED
|
@@ -42,6 +42,33 @@ const MAX_EDIT_IMAGE_BYTES = 10 * 1024 * 1024
|
|
|
42
42
|
/** Sizes dall-e-3 accepts; anything else falls back to its square default. */
|
|
43
43
|
const DALLE3_SIZES = new Set(['1024x1024', '1792x1024', '1024x1792'])
|
|
44
44
|
|
|
45
|
+
/** Whether the model is an xAI Grok Imagine model (grok-imagine-image,
|
|
46
|
+
* grok-imagine-image-2.0, …). Grok Imagine speaks JSON on both endpoints
|
|
47
|
+
* and exposes its own aspect-ratio / response-format knobs instead of the
|
|
48
|
+
* OpenAI size/quality/detail passthrough. */
|
|
49
|
+
function isGrokImagine(model: string): boolean {
|
|
50
|
+
return /^grok-imagine(?:-|$)/.test(model)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** The panel's aspect ratios mapped to the closest OpenAI pixel size
|
|
54
|
+
* (gpt-image-2 / generic OpenAI-compatible endpoints). */
|
|
55
|
+
const OPENAI_SIZE_BY_RATIO: Readonly<Record<string, string>> = {
|
|
56
|
+
'1:1': '1024x1024',
|
|
57
|
+
'3:4': '1024x1536',
|
|
58
|
+
'4:3': '1536x1024',
|
|
59
|
+
'9:16': '1024x1792',
|
|
60
|
+
'2:3': '1024x1536',
|
|
61
|
+
'3:2': '1536x1024',
|
|
62
|
+
'16:9': '1792x1024',
|
|
63
|
+
'21:9': '1792x1024',
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Panel ratios that need renaming for a model's vocabulary. Grok documents
|
|
67
|
+
* 20:9 as its ultra-wide ratio, so the panel's 21:9 label is sent as 20:9. */
|
|
68
|
+
const GROK_ASPECT_ALIASES: Readonly<Record<string, string>> = {
|
|
69
|
+
'21:9': '20:9',
|
|
70
|
+
}
|
|
71
|
+
|
|
45
72
|
/** Content-type extension hints for URL-fetched images. */
|
|
46
73
|
function mimeOfExtension(path: string): string | undefined {
|
|
47
74
|
const match = /\.([a-z0-9]+)$/i.exec(path)
|
|
@@ -87,17 +114,44 @@ function effectiveParams(request: GenerateRequest): {
|
|
|
87
114
|
size?: string
|
|
88
115
|
quality?: string
|
|
89
116
|
detail?: string
|
|
117
|
+
aspect_ratio?: string
|
|
118
|
+
resolution?: string
|
|
119
|
+
response_format?: string
|
|
90
120
|
} {
|
|
91
121
|
const model = request.model.trim() === '' ? 'gpt-image-2' : request.model.trim()
|
|
92
122
|
// dall-e-3 has no quality/detail knobs and only produces one image.
|
|
93
123
|
if (model === 'dall-e-3') {
|
|
94
|
-
const
|
|
124
|
+
const pixel = OPENAI_SIZE_BY_RATIO[request.size]
|
|
125
|
+
const size = (pixel !== undefined && DALLE3_SIZES.has(pixel)) ? pixel : '1024x1024'
|
|
95
126
|
return { model, size }
|
|
96
127
|
}
|
|
128
|
+
// Grok Imagine: the panel's aspect ratios are sent as-is (21:9 aliased to
|
|
129
|
+
// the documented 20:9), the clarity tiers become the resolution parameter
|
|
130
|
+
// (the API documents 1k / 2k only, so 4k falls back to 2k), and base64
|
|
131
|
+
// output keeps the temporary signed result URLs from expiring before the
|
|
132
|
+
// host downloads them.
|
|
133
|
+
if (isGrokImagine(model)) {
|
|
134
|
+
return {
|
|
135
|
+
model,
|
|
136
|
+
...request.size !== '' && request.size !== 'auto'
|
|
137
|
+
? { aspect_ratio: GROK_ASPECT_ALIASES[request.size] ?? request.size }
|
|
138
|
+
: {},
|
|
139
|
+
...request.quality !== '' && request.quality !== 'auto'
|
|
140
|
+
? { resolution: request.quality === '4k' ? '2k' : request.quality }
|
|
141
|
+
: {},
|
|
142
|
+
response_format: 'b64_json',
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// OpenAI-compatible endpoints: nearest pixel size, clarity tiers mapped to
|
|
146
|
+
// the quality levels (1k→low / 2k→medium / 4k→high), detail passthrough.
|
|
97
147
|
return {
|
|
98
148
|
model,
|
|
99
|
-
...request.size !== '' && request.size !== 'auto'
|
|
100
|
-
|
|
149
|
+
...request.size !== '' && request.size !== 'auto' && OPENAI_SIZE_BY_RATIO[request.size] !== undefined
|
|
150
|
+
? { size: OPENAI_SIZE_BY_RATIO[request.size] }
|
|
151
|
+
: {},
|
|
152
|
+
...request.quality === '1k' ? { quality: 'low' } : {},
|
|
153
|
+
...request.quality === '2k' ? { quality: 'medium' } : {},
|
|
154
|
+
...request.quality === '4k' ? { quality: 'high' } : {},
|
|
101
155
|
...request.detail !== '' ? { detail: request.detail } : {},
|
|
102
156
|
}
|
|
103
157
|
}
|
|
@@ -178,14 +232,27 @@ async function requestOneImage(
|
|
|
178
232
|
if (bytes.byteLength > MAX_EDIT_IMAGE_BYTES) {
|
|
179
233
|
throw new ImageGenError('参考图片超过 10MB 上限', 'edit-image-too-large')
|
|
180
234
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
235
|
+
// Grok Imagine /images/edits takes a JSON image_url object (a base64 data
|
|
236
|
+
// URI is accepted) instead of OpenAI's multipart form-data upload.
|
|
237
|
+
if (isGrokImagine(params.model)) {
|
|
238
|
+
headers['content-type'] = 'application/json'
|
|
239
|
+
body = JSON.stringify({
|
|
240
|
+
model: params.model,
|
|
241
|
+
prompt: request.prompt,
|
|
242
|
+
image: { url: request.image, type: 'image_url' },
|
|
243
|
+
...params.aspect_ratio !== undefined ? { aspect_ratio: params.aspect_ratio } : {},
|
|
244
|
+
response_format: 'b64_json',
|
|
245
|
+
})
|
|
246
|
+
} else {
|
|
247
|
+
const form = new FormData()
|
|
248
|
+
form.append('image', new Blob([bytes], { type: parsed.mime }), `reference.${extensionOf(parsed.mime)}`)
|
|
249
|
+
form.append('prompt', request.prompt)
|
|
250
|
+
form.append('model', params.model)
|
|
251
|
+
if (params.size !== undefined) form.append('size', params.size)
|
|
252
|
+
if (params.quality !== undefined) form.append('quality', params.quality)
|
|
253
|
+
if (params.detail !== undefined) form.append('detail', params.detail)
|
|
254
|
+
body = form
|
|
255
|
+
}
|
|
189
256
|
} else {
|
|
190
257
|
headers['content-type'] = 'application/json'
|
|
191
258
|
body = JSON.stringify({ prompt: request.prompt, ...params } as Record<string, unknown>)
|