kingkont 0.7.69 → 0.7.71
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 +10 -2
- package/lib/cli.js +3 -2
- package/lib/providers.js +10 -6
- package/main.js +1 -1
- package/package.json +1 -1
- package/renderer/board.js +2 -2
- package/renderer/generate.js +29 -0
- package/renderer/media.js +2 -2
- package/renderer/state.js +2 -1
- package/server.js +2 -2
package/index.html
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
</div>
|
|
33
33
|
|
|
34
34
|
<div class="sidebar-section">
|
|
35
|
-
<h3
|
|
35
|
+
<h3>Общее <button id="newLocation" disabled title="Создать общую доску">+</button></h3>
|
|
36
36
|
<div class="sidebar-list" id="locationList"></div>
|
|
37
37
|
</div>
|
|
38
38
|
|
|
@@ -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"
|
|
@@ -437,7 +445,7 @@
|
|
|
437
445
|
<label id="charsPickRow" style="display:none;">Персонажи
|
|
438
446
|
<div class="picker-chips" id="charsPickChips"></div>
|
|
439
447
|
</label>
|
|
440
|
-
<label id="locPickRow" style="display:none;"
|
|
448
|
+
<label id="locPickRow" style="display:none;">Из общего
|
|
441
449
|
<select id="locPickSelect"><option value="">— не выбрана —</option></select>
|
|
442
450
|
</label>
|
|
443
451
|
<!-- Дефолтные промпты сцены (стек чекбоксов; рендерится динамически) -->
|
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/main.js
CHANGED
package/package.json
CHANGED
package/renderer/board.js
CHANGED
|
@@ -1211,7 +1211,7 @@ $('newLocation').addEventListener('click', async () => {
|
|
|
1211
1211
|
vlog('info', 'newLocation click; hasFilm=' + !!state.filmHandle);
|
|
1212
1212
|
if (!state.filmHandle) { alert('Сначала открой проект (папку).'); return; }
|
|
1213
1213
|
try {
|
|
1214
|
-
const name = await askName('Название
|
|
1214
|
+
const name = await askName('Название (общее):', 'например, Кухня, Револьвер, Фон…');
|
|
1215
1215
|
vlog('info', 'newLocation name=' + JSON.stringify(name));
|
|
1216
1216
|
if (!name) return;
|
|
1217
1217
|
const root = await state.filmHandle.getDirectoryHandle(LOC_DIR, { create: true });
|
|
@@ -1778,7 +1778,7 @@ function showNodeContextMenu(node, clientX, clientY) {
|
|
|
1778
1778
|
// Локация: пометить картинку как location sheet
|
|
1779
1779
|
if (node.type === 'image' && node.file && state.currentBoard?.kind === 'location') {
|
|
1780
1780
|
const isSheet = state.currentBoard.metadata.location?.sheet === node.file;
|
|
1781
|
-
add(isSheet ? '⭐ Это
|
|
1781
|
+
add(isSheet ? '⭐ Это главная картинка' : '⭐ Сделать главной картинкой', () => {
|
|
1782
1782
|
if (!state.currentBoard.metadata.location) state.currentBoard.metadata.location = {};
|
|
1783
1783
|
state.currentBoard.metadata.location.sheet = isSheet ? null : node.file;
|
|
1784
1784
|
scheduleSave();
|
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/media.js
CHANGED
|
@@ -1299,8 +1299,8 @@ function updateMentionPopup() {
|
|
|
1299
1299
|
t.className = `mtype ${s.type}`;
|
|
1300
1300
|
t.textContent = s.scope === 'char' ? 'персонаж'
|
|
1301
1301
|
: s.scope === 'char-image' ? `персонаж·${s.charName}`
|
|
1302
|
-
: s.scope === 'loc' ? '
|
|
1303
|
-
: s.scope === 'loc-image' ?
|
|
1302
|
+
: s.scope === 'loc' ? 'общее'
|
|
1303
|
+
: s.scope === 'loc-image' ? `общее·${s.locName}`
|
|
1304
1304
|
: s.scope === 'scene-folder' ? 'сцена'
|
|
1305
1305
|
: s.type;
|
|
1306
1306
|
const nm = document.createElement('span');
|
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 });
|