kingkont 0.7.69 → 0.7.70
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/index.html +8 -0
- package/lib/cli.js +3 -2
- package/lib/providers.js +10 -6
- package/package.json +1 -1
- package/renderer/generate.js +29 -0
- package/renderer/state.js +2 -1
- package/server.js +2 -2
package/index.html
CHANGED
|
@@ -343,6 +343,7 @@
|
|
|
343
343
|
<label id="imageModelRow">Модель для картинки
|
|
344
344
|
<div class="seg-control">
|
|
345
345
|
<button class="seg active" data-img-model="nano-banana-2" type="button" title="Высокое качество, медленно">Nano Banana 2</button>
|
|
346
|
+
<button class="seg" data-img-model="nano-banana-pro" type="button" title="Pro-версия nano-banana, выше качество">Nano Banana Pro</button>
|
|
346
347
|
<button class="seg" data-img-model="grok" type="button">Grok</button>
|
|
347
348
|
<button class="seg" data-img-model="seedream" type="button">Seedream 4.5</button>
|
|
348
349
|
<button class="seg" data-img-model="seedream-5-lite" type="button">Seedream 5 Lite</button>
|
|
@@ -350,6 +351,13 @@
|
|
|
350
351
|
<button class="seg" data-img-model="sdxl-lightning" type="button" title="Очень быстрая (3-5с)">⚡ SDXL Lightning</button>
|
|
351
352
|
</div>
|
|
352
353
|
</label>
|
|
354
|
+
<label id="imageQualityRow">Качество
|
|
355
|
+
<div class="seg-control" id="imageQualityCtl">
|
|
356
|
+
<button class="seg" data-img-quality="low" type="button" title="Быстрее, дешевле">low</button>
|
|
357
|
+
<button class="seg active" data-img-quality="medium" type="button">medium</button>
|
|
358
|
+
<button class="seg" data-img-quality="high" type="button" title="Лучше детализация, медленнее">high</button>
|
|
359
|
+
</div>
|
|
360
|
+
</label>
|
|
353
361
|
<label id="imageOptionsRow">Соотношение сторон
|
|
354
362
|
<div class="seg-control" id="imageAspectCtl">
|
|
355
363
|
<button class="seg" data-img-asp="source" type="button" id="imgAspSource"
|
package/lib/cli.js
CHANGED
|
@@ -248,6 +248,7 @@ async function cmdGen({ positional, flags }) {
|
|
|
248
248
|
aspectRatio: finalAspect,
|
|
249
249
|
resolution: flags.resolution,
|
|
250
250
|
duration: flags.duration,
|
|
251
|
+
quality: flags.quality,
|
|
251
252
|
firstFrame: flags['first-frame'] || flags.firstFrame,
|
|
252
253
|
lastFrame: flags['last-frame'] || flags.lastFrame,
|
|
253
254
|
settings, flags,
|
|
@@ -370,9 +371,9 @@ async function cmdUpload({ positional, flags }) {
|
|
|
370
371
|
// =============================================================================
|
|
371
372
|
|
|
372
373
|
async function runMediaGeneration(root, ref, node, args) {
|
|
373
|
-
const { kind, prompt, modelKey, imageInputs, videoInputs, aspectRatio, resolution, duration, firstFrame, lastFrame, settings, flags } = args;
|
|
374
|
+
const { kind, prompt, modelKey, imageInputs, videoInputs, aspectRatio, resolution, duration, quality, firstFrame, lastFrame, settings, flags } = args;
|
|
374
375
|
const start = await providers.startGeneration({
|
|
375
|
-
kind, prompt, modelKey, imageInputs, videoInputs, aspectRatio, resolution, duration, firstFrame, lastFrame, settings,
|
|
376
|
+
kind, prompt, modelKey, imageInputs, videoInputs, aspectRatio, resolution, duration, quality, firstFrame, lastFrame, settings,
|
|
376
377
|
});
|
|
377
378
|
console.error(`[task] ${start.taskId} provider=${start.provider}`);
|
|
378
379
|
await updateNode(root, ref, node.id, n => {
|
package/lib/providers.js
CHANGED
|
@@ -52,6 +52,7 @@ function summarizeBody(body) {
|
|
|
52
52
|
|
|
53
53
|
const KIE_IMAGE_MODELS = {
|
|
54
54
|
'nano-banana-2': 'nano-banana-2',
|
|
55
|
+
'nano-banana-pro': 'nano-banana-pro',
|
|
55
56
|
'grok': 'grok-imagine/text-to-image',
|
|
56
57
|
'seedream': 'seedream/4.5-text-to-image',
|
|
57
58
|
'seedream-5-lite': 'seedream/5-lite-text-to-image',
|
|
@@ -212,7 +213,7 @@ async function kiePoll(taskId) {
|
|
|
212
213
|
*/
|
|
213
214
|
async function startGeneration(args) {
|
|
214
215
|
const { kind, prompt, modelKey, imageInputs, videoInputs, firstFrame, lastFrame,
|
|
215
|
-
aspectRatio, resolution, duration, settings: s } = args;
|
|
216
|
+
aspectRatio, resolution, duration, quality, settings: s } = args;
|
|
216
217
|
if (kind !== 'image' && kind !== 'video') throw new Error(`unknown kind: ${kind}`);
|
|
217
218
|
if (!prompt && kind !== 'video') throw new Error('prompt обязателен');
|
|
218
219
|
|
|
@@ -229,7 +230,7 @@ async function startGeneration(args) {
|
|
|
229
230
|
const kieSupportsModel = !!kieMap[kieKey];
|
|
230
231
|
|
|
231
232
|
if (kieAvailable && kieSupportsModel) {
|
|
232
|
-
return await _startGenerationViaKie({ kind, prompt, key: kieKey, imageInputs, videoInputs, aspectRatio, resolution, duration });
|
|
233
|
+
return await _startGenerationViaKie({ kind, prompt, key: kieKey, imageInputs, videoInputs, aspectRatio, resolution, duration, quality });
|
|
233
234
|
}
|
|
234
235
|
|
|
235
236
|
// Chatium-путь.
|
|
@@ -252,6 +253,7 @@ async function startGeneration(args) {
|
|
|
252
253
|
prompt, model: fullModel, aspectRatio, resolution,
|
|
253
254
|
outputFormat: isSeedream ? 'jpeg' : 'jpg',
|
|
254
255
|
};
|
|
256
|
+
if (quality) body.quality = quality; // 'low' | 'medium' | 'high' — модель сама решит как мапить
|
|
255
257
|
if (!isSeedream && Array.isArray(imageInputs) && imageInputs.length) {
|
|
256
258
|
body.imageInputs = imageInputs;
|
|
257
259
|
}
|
|
@@ -271,29 +273,31 @@ async function startGeneration(args) {
|
|
|
271
273
|
|
|
272
274
|
// Внутренний helper: KIE-путь startGeneration. Вынесен чтобы не дублировать
|
|
273
275
|
// логику между «KIE первичный» и старым «KIE fallback».
|
|
274
|
-
async function _startGenerationViaKie({ kind, prompt, key, imageInputs, videoInputs, aspectRatio, resolution, duration }) {
|
|
276
|
+
async function _startGenerationViaKie({ kind, prompt, key, imageInputs, videoInputs, aspectRatio, resolution, duration, quality }) {
|
|
275
277
|
const map = kind === 'video' ? KIE_VIDEO_MODELS : KIE_IMAGE_MODELS;
|
|
276
278
|
const fullModel = map[key];
|
|
277
279
|
if (!fullModel) throw new Error(`unknown ${kind} model: ${key}`);
|
|
278
280
|
|
|
279
281
|
const input = { prompt };
|
|
280
282
|
if (kind === 'image') {
|
|
281
|
-
if (key === 'nano-banana-2') {
|
|
283
|
+
if (key === 'nano-banana-2' || key === 'nano-banana-pro') {
|
|
282
284
|
if (imageInputs?.length) input.image_input = imageInputs;
|
|
283
285
|
if (aspectRatio) input.aspect_ratio = aspectRatio;
|
|
284
286
|
input.resolution = resolution || '1K';
|
|
285
287
|
input.output_format = 'jpg';
|
|
288
|
+
if (quality) input.quality = quality;
|
|
286
289
|
} else if (key === 'grok') {
|
|
287
290
|
if (aspectRatio) input.aspect_ratio = aspectRatio;
|
|
288
291
|
input.nsfw_checker = false;
|
|
289
|
-
input.enable_pro =
|
|
292
|
+
input.enable_pro = quality === 'high'; // pro-mode для high
|
|
290
293
|
} else if (key === 'seedream' || key === 'seedream-5-lite') {
|
|
291
294
|
input.aspect_ratio = aspectRatio || '16:9';
|
|
292
|
-
input.quality = 'high';
|
|
295
|
+
input.quality = quality || 'high';
|
|
293
296
|
input.nsfw_checker = false;
|
|
294
297
|
} else if (key === 'flux-schnell' || key === 'sdxl-lightning') {
|
|
295
298
|
if (aspectRatio) input.aspect_ratio = aspectRatio;
|
|
296
299
|
input.output_format = 'jpg';
|
|
300
|
+
// flux/sdxl быстрые, quality для них N/A — пропускаем.
|
|
297
301
|
}
|
|
298
302
|
} else {
|
|
299
303
|
if (imageInputs?.length) input.reference_image_urls = imageInputs;
|
package/package.json
CHANGED
package/renderer/generate.js
CHANGED
|
@@ -104,6 +104,7 @@ function openPhraseFor(charInfo) {
|
|
|
104
104
|
$('imageModelRow').style.display = 'none';
|
|
105
105
|
|
|
106
106
|
$('imageOptionsRow').style.display = 'none';
|
|
107
|
+
$('imageQualityRow').style.display = 'none';
|
|
107
108
|
$('videoOptionsRow').style.display = 'none';
|
|
108
109
|
|
|
109
110
|
$('videoModelRow').style.display = 'none';
|
|
@@ -301,6 +302,7 @@ async function openGenModal(kind) {
|
|
|
301
302
|
$('imageModelRow').style.display = kind === 'image' ? '' : 'none';
|
|
302
303
|
|
|
303
304
|
$('imageOptionsRow').style.display = kind === 'image' ? '' : 'none';
|
|
305
|
+
$('imageQualityRow').style.display = kind === 'image' ? '' : 'none';
|
|
304
306
|
$('videoOptionsRow').style.display = kind === 'video' ? '' : 'none';
|
|
305
307
|
|
|
306
308
|
$('videoModelRow').style.display = kind === 'video' ? '' : 'none';
|
|
@@ -742,6 +744,7 @@ document.querySelectorAll('#genModal [data-kind]').forEach(b => {
|
|
|
742
744
|
$('imageModelRow').style.display = state.genKind === 'image' ? '' : 'none';
|
|
743
745
|
|
|
744
746
|
$('imageOptionsRow').style.display = state.genKind === 'image' ? '' : 'none';
|
|
747
|
+
$('imageQualityRow').style.display = state.genKind === 'image' ? '' : 'none';
|
|
745
748
|
$('videoOptionsRow').style.display = state.genKind === 'video' ? '' : 'none';
|
|
746
749
|
|
|
747
750
|
$('videoModelRow').style.display = state.genKind === 'video' ? '' : 'none';
|
|
@@ -1161,8 +1164,30 @@ document.querySelectorAll('#genModal [data-img-model]').forEach(b => {
|
|
|
1161
1164
|
document.querySelectorAll('#genModal [data-img-model]').forEach(x => x.classList.remove('active'));
|
|
1162
1165
|
b.classList.add('active');
|
|
1163
1166
|
state.imageModel = b.dataset.imgModel;
|
|
1167
|
+
localStorage.setItem('imageModel', state.imageModel);
|
|
1164
1168
|
});
|
|
1165
1169
|
});
|
|
1170
|
+
// При первом открытии: подсветить активную из state (а не первую в DOM).
|
|
1171
|
+
function syncImageModelActive() {
|
|
1172
|
+
document.querySelectorAll('#genModal [data-img-model]').forEach(b =>
|
|
1173
|
+
b.classList.toggle('active', b.dataset.imgModel === state.imageModel));
|
|
1174
|
+
}
|
|
1175
|
+
syncImageModelActive();
|
|
1176
|
+
// Переключатель качества (low / medium / high) — пишется в каждый запрос
|
|
1177
|
+
// генерации картинки (см. submit handler).
|
|
1178
|
+
document.querySelectorAll('#genModal [data-img-quality]').forEach(b => {
|
|
1179
|
+
b.addEventListener('click', () => {
|
|
1180
|
+
document.querySelectorAll('#genModal [data-img-quality]').forEach(x => x.classList.remove('active'));
|
|
1181
|
+
b.classList.add('active');
|
|
1182
|
+
state.imageQuality = b.dataset.imgQuality;
|
|
1183
|
+
localStorage.setItem('imageQuality', state.imageQuality);
|
|
1184
|
+
});
|
|
1185
|
+
});
|
|
1186
|
+
function syncImageQualityActive() {
|
|
1187
|
+
document.querySelectorAll('#genModal [data-img-quality]').forEach(b =>
|
|
1188
|
+
b.classList.toggle('active', b.dataset.imgQuality === state.imageQuality));
|
|
1189
|
+
}
|
|
1190
|
+
syncImageQualityActive();
|
|
1166
1191
|
// Переключатель модели видео
|
|
1167
1192
|
document.querySelectorAll('#genModal [data-vid-model]').forEach(b => {
|
|
1168
1193
|
b.addEventListener('click', () => {
|
|
@@ -1499,6 +1524,7 @@ $('genSubmit').addEventListener('click', async () => {
|
|
|
1499
1524
|
'seedream': 'seedream/4.5-text-to-image',
|
|
1500
1525
|
'seedream-5-lite': 'seedream/5-lite-text-to-image',
|
|
1501
1526
|
'nano-banana-2': 'nano-banana-2',
|
|
1527
|
+
'nano-banana-pro': 'nano-banana-pro',
|
|
1502
1528
|
}[state.imageModel] || 'nano-banana-2')
|
|
1503
1529
|
: ({
|
|
1504
1530
|
'seedance-2': 'bytedance/seedance-2',
|
|
@@ -1518,6 +1544,7 @@ $('genSubmit').addEventListener('click', async () => {
|
|
|
1518
1544
|
} : {}),
|
|
1519
1545
|
...(kind === 'image' ? {
|
|
1520
1546
|
aspectRatio: resolvedImageAspect,
|
|
1547
|
+
quality: state.imageQuality,
|
|
1521
1548
|
} : {}),
|
|
1522
1549
|
},
|
|
1523
1550
|
};
|
|
@@ -1678,6 +1705,8 @@ async function startGenerationJob(node, kind, prompt, mediaRefs, boardHandle, bK
|
|
|
1678
1705
|
// Grok-imagine требует aspect_ratio из {2:3, 3:2, 1:1, 9:16, 16:9}.
|
|
1679
1706
|
// Остальные модели (nano-banana-2, seedream и др.) тоже принимают.
|
|
1680
1707
|
submitBody.aspectRatio = node.generated?.aspectRatio ?? boardAspect ?? state.imageAspect;
|
|
1708
|
+
const q = node.generated?.quality ?? state.imageQuality;
|
|
1709
|
+
if (q) submitBody.quality = q;
|
|
1681
1710
|
}
|
|
1682
1711
|
logJob(node.id, `POST /api/generate body: ${logSafe(submitBody)}`);
|
|
1683
1712
|
logJob(node.id, `POST /api/generate (image_input=${imageInputs.length}, video_input=${videoInputs.length}, model=${modelKey})`);
|
package/renderer/state.js
CHANGED
|
@@ -397,7 +397,8 @@ const state = {
|
|
|
397
397
|
filmHandle: null,
|
|
398
398
|
currentBoard: null, // { kind, name, key, handle, metadata, urls }
|
|
399
399
|
genKind: 'image',
|
|
400
|
-
imageModel: 'nano-banana-2', // 'nano-banana-2' | 'grok' | ...
|
|
400
|
+
imageModel: localStorage.getItem('imageModel') || 'nano-banana-2', // 'nano-banana-2' | 'nano-banana-pro' | 'grok' | ...
|
|
401
|
+
imageQuality: localStorage.getItem('imageQuality') || 'medium', // low | medium | high
|
|
401
402
|
imageAspect: localStorage.getItem('imageAspect') || '1:1', // 1:1 / 16:9 / 9:16 / 3:2 / 2:3 / 4:3 / 3:4
|
|
402
403
|
videoModel: localStorage.getItem('videoModel') || 'seedance-2', // 'seedance-2' | 'kling-o1' | 'kling-3.0' | ...
|
|
403
404
|
ttsModel: localStorage.getItem('ttsModel') || 'qwen/qwen3-tts', // qwen/elevenlabs/v3/minimax/speech-02-hd/gemini
|
package/server.js
CHANGED
|
@@ -91,11 +91,11 @@ function sendError(res, e, defaultStatus = 500, providerHeader = null) {
|
|
|
91
91
|
|
|
92
92
|
async function handleGenerate(req, res) {
|
|
93
93
|
const body = await readJson(req);
|
|
94
|
-
const { kind, prompt, imageInputs, videoInputs, aspectRatio, resolution, duration, model: modelKey } = body;
|
|
94
|
+
const { kind, prompt, imageInputs, videoInputs, aspectRatio, resolution, duration, quality, model: modelKey } = body;
|
|
95
95
|
if (!prompt || !prompt.trim()) return send(res, 400, { error: 'prompt обязателен' });
|
|
96
96
|
try {
|
|
97
97
|
const r = await providers.startGeneration({
|
|
98
|
-
kind, prompt, modelKey, imageInputs, videoInputs, aspectRatio, resolution, duration,
|
|
98
|
+
kind, prompt, modelKey, imageInputs, videoInputs, aspectRatio, resolution, duration, quality,
|
|
99
99
|
settings: getSettings(),
|
|
100
100
|
});
|
|
101
101
|
send(res, 200, { taskId: r.taskId }, { 'X-Provider': r.provider });
|