kingkont 0.7.52 → 0.7.54
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 +3 -0
- package/package.json +1 -1
- package/renderer/board.js +10 -6
- package/renderer/generate.js +82 -3
package/index.html
CHANGED
|
@@ -352,6 +352,9 @@
|
|
|
352
352
|
</label>
|
|
353
353
|
<label id="imageOptionsRow">Соотношение сторон
|
|
354
354
|
<div class="seg-control" id="imageAspectCtl">
|
|
355
|
+
<button class="seg" data-img-asp="source" type="button" id="imgAspSource"
|
|
356
|
+
title="Взять формат у исходной картинки-референса"
|
|
357
|
+
style="display:none;">↺ Как у исходной</button>
|
|
355
358
|
<button class="seg active" data-img-asp="1:1" type="button">1:1</button>
|
|
356
359
|
<button class="seg" data-img-asp="16:9" type="button">16:9</button>
|
|
357
360
|
<button class="seg" data-img-asp="9:16" type="button">9:16</button>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kingkont",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.54",
|
|
4
4
|
"description": "KingKont \u00b7 Chatium \u2014 \u043d\u043e\u0434-\u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u0441\u0446\u0435\u043d \u0441 AI-\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0435\u0439 (\u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438/\u0432\u0438\u0434\u0435\u043e/\u0433\u043e\u043b\u043e\u0441/SFX/\u043c\u0443\u0437\u044b\u043a\u0430/\u0442\u0435\u043a\u0441\u0442)",
|
|
5
5
|
"main": "main.js",
|
|
6
6
|
"bin": {
|
package/renderer/board.js
CHANGED
|
@@ -985,9 +985,11 @@ $('newEpisode').addEventListener('click', async () => {
|
|
|
985
985
|
const name = await askName('Название сцены:', 'например, Сцена 1');
|
|
986
986
|
vlog('info', 'newEpisode name=' + JSON.stringify(name));
|
|
987
987
|
if (!name) return;
|
|
988
|
-
await state.filmHandle.getDirectoryHandle(name, { create: true });
|
|
988
|
+
const handle = await state.filmHandle.getDirectoryHandle(name, { create: true });
|
|
989
989
|
await refreshEpisodes();
|
|
990
|
-
|
|
990
|
+
// Сразу переходим на созданную сцену.
|
|
991
|
+
await selectBoard({ kind: 'episode', name, handle });
|
|
992
|
+
vlog('info', 'newEpisode created + selected: ' + name);
|
|
991
993
|
} catch (e) {
|
|
992
994
|
vlog('err', 'newEpisode failed: ' + (e?.message || e));
|
|
993
995
|
alert('Ошибка: ' + (e?.message || e));
|
|
@@ -1002,9 +1004,10 @@ $('newCharacter').addEventListener('click', async () => {
|
|
|
1002
1004
|
vlog('info', 'newCharacter name=' + JSON.stringify(name));
|
|
1003
1005
|
if (!name) return;
|
|
1004
1006
|
const root = await state.filmHandle.getDirectoryHandle(CHAR_DIR, { create: true });
|
|
1005
|
-
await root.getDirectoryHandle(name, { create: true });
|
|
1007
|
+
const handle = await root.getDirectoryHandle(name, { create: true });
|
|
1006
1008
|
await refreshCharacters();
|
|
1007
|
-
|
|
1009
|
+
await selectBoard({ kind: 'character', name, handle });
|
|
1010
|
+
vlog('info', 'newCharacter created + selected: ' + name);
|
|
1008
1011
|
} catch (e) {
|
|
1009
1012
|
vlog('err', 'newCharacter failed: ' + (e?.message || e));
|
|
1010
1013
|
alert('Ошибка: ' + (e?.message || e));
|
|
@@ -1019,9 +1022,10 @@ $('newLocation').addEventListener('click', async () => {
|
|
|
1019
1022
|
vlog('info', 'newLocation name=' + JSON.stringify(name));
|
|
1020
1023
|
if (!name) return;
|
|
1021
1024
|
const root = await state.filmHandle.getDirectoryHandle(LOC_DIR, { create: true });
|
|
1022
|
-
await root.getDirectoryHandle(name, { create: true });
|
|
1025
|
+
const handle = await root.getDirectoryHandle(name, { create: true });
|
|
1023
1026
|
await refreshLocations();
|
|
1024
|
-
|
|
1027
|
+
await selectBoard({ kind: 'location', name, handle });
|
|
1028
|
+
vlog('info', 'newLocation created + selected: ' + name);
|
|
1025
1029
|
} catch (e) {
|
|
1026
1030
|
vlog('err', 'newLocation failed: ' + (e?.message || e));
|
|
1027
1031
|
alert('Ошибка: ' + (e?.message || e));
|
package/renderer/generate.js
CHANGED
|
@@ -739,10 +739,73 @@ document.querySelectorAll('#genModal [data-kind]').forEach(b => {
|
|
|
739
739
|
});
|
|
740
740
|
});
|
|
741
741
|
|
|
742
|
+
// Вычислить ближайший поддерживаемый aspect ratio из state.sourceRef.
|
|
743
|
+
// Возвращает строку '16:9' / '1:1' / etc, или null если не удалось.
|
|
744
|
+
async function computeSourceImageAspect() {
|
|
745
|
+
if (!state.sourceRef || state.sourceRef.type !== 'image' || !state.sourceRef.file) return null;
|
|
746
|
+
const handle = state.sourceRef.boardHandle || state.currentBoard?.handle;
|
|
747
|
+
if (!handle) return null;
|
|
748
|
+
let url = null;
|
|
749
|
+
try {
|
|
750
|
+
const fh = await resolveBoardFile(handle, state.sourceRef.file);
|
|
751
|
+
const file = await fh.getFile();
|
|
752
|
+
url = URL.createObjectURL(file);
|
|
753
|
+
const dim = await new Promise((res, rej) => {
|
|
754
|
+
const img = new Image();
|
|
755
|
+
img.onload = () => res({ w: img.naturalWidth, h: img.naturalHeight });
|
|
756
|
+
img.onerror = rej;
|
|
757
|
+
img.src = url;
|
|
758
|
+
});
|
|
759
|
+
if (!dim.w || !dim.h) return null;
|
|
760
|
+
return nearestSupportedAspect(dim.w / dim.h);
|
|
761
|
+
} catch { return null; }
|
|
762
|
+
finally { if (url) URL.revokeObjectURL(url); }
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// Из произвольного ratio выбирает ближайший из поддерживаемых genModal'ом.
|
|
766
|
+
// Сравнение в логарифмической метрике — справедливо для пропорций
|
|
767
|
+
// (1.5 ↔ 0.667 равноудалены от 1.0).
|
|
768
|
+
function nearestSupportedAspect(ratio) {
|
|
769
|
+
const opts = [
|
|
770
|
+
{ name: '1:1', v: 1.0 },
|
|
771
|
+
{ name: '16:9', v: 16/9 },
|
|
772
|
+
{ name: '9:16', v: 9/16 },
|
|
773
|
+
{ name: '3:2', v: 3/2 },
|
|
774
|
+
{ name: '2:3', v: 2/3 },
|
|
775
|
+
{ name: '4:3', v: 4/3 },
|
|
776
|
+
{ name: '3:4', v: 3/4 },
|
|
777
|
+
];
|
|
778
|
+
let best = opts[0];
|
|
779
|
+
let bestDiff = Math.abs(Math.log(ratio / best.v));
|
|
780
|
+
for (const o of opts) {
|
|
781
|
+
const d = Math.abs(Math.log(ratio / o.v));
|
|
782
|
+
if (d < bestDiff) { best = o; bestDiff = d; }
|
|
783
|
+
}
|
|
784
|
+
return best.name;
|
|
785
|
+
}
|
|
786
|
+
|
|
742
787
|
// === Source frame controls ===
|
|
743
788
|
function syncSourceRefRow() {
|
|
744
789
|
const showable = state.sourceRef && (state.genKind === 'image' || state.genKind === 'video');
|
|
745
790
|
$('sourceRefRow').style.display = showable ? '' : 'none';
|
|
791
|
+
// Кнопка «↺ Как у исходной» в селекторе aspect ratio: показываем только
|
|
792
|
+
// когда есть image-source (для image-генерации). Если source выключен (use=false)
|
|
793
|
+
// — тоже скрываем; нет смысла предлагать aspect которого юзер не использует.
|
|
794
|
+
const srcBtn = $('imgAspSource');
|
|
795
|
+
if (srcBtn) {
|
|
796
|
+
const hasImgSource = state.sourceRef
|
|
797
|
+
&& state.sourceRef.type === 'image'
|
|
798
|
+
&& state.sourceRef.use
|
|
799
|
+
&& state.genKind === 'image';
|
|
800
|
+
srcBtn.style.display = hasImgSource ? '' : 'none';
|
|
801
|
+
// Если кнопка скрылась а была активной — переключаем на дефолтное.
|
|
802
|
+
if (!hasImgSource && state.imageAspect === 'source') {
|
|
803
|
+
state.imageAspect = localStorage.getItem('imageAspect') || '1:1';
|
|
804
|
+
if (state.imageAspect === 'source') state.imageAspect = '1:1';
|
|
805
|
+
document.querySelectorAll('#genModal [data-img-asp]').forEach(b =>
|
|
806
|
+
b.classList.toggle('active', b.dataset.imgAsp === state.imageAspect));
|
|
807
|
+
}
|
|
808
|
+
}
|
|
746
809
|
}
|
|
747
810
|
|
|
748
811
|
// === Персонажи + Локация: чипы/дропдаун ===
|
|
@@ -1016,13 +1079,18 @@ document.querySelectorAll('#genModal [data-vid-model]').forEach(b => {
|
|
|
1016
1079
|
localStorage.setItem('videoModel', state.videoModel);
|
|
1017
1080
|
});
|
|
1018
1081
|
});
|
|
1019
|
-
// Переключатель aspect ratio для image
|
|
1082
|
+
// Переключатель aspect ratio для image. Особый кейс — 'source' (взять
|
|
1083
|
+
// у исходной картинки): не сохраняем в localStorage (это ad-hoc выбор
|
|
1084
|
+
// привязанный к конкретному запуску). Реальный aspectRatio будет
|
|
1085
|
+
// вычислен в момент submit через computeSourceImageAspect().
|
|
1020
1086
|
document.querySelectorAll('#genModal [data-img-asp]').forEach(b => {
|
|
1021
1087
|
b.addEventListener('click', () => {
|
|
1022
1088
|
document.querySelectorAll('#genModal [data-img-asp]').forEach(x => x.classList.remove('active'));
|
|
1023
1089
|
b.classList.add('active');
|
|
1024
1090
|
state.imageAspect = b.dataset.imgAsp;
|
|
1025
|
-
|
|
1091
|
+
if (state.imageAspect !== 'source') {
|
|
1092
|
+
localStorage.setItem('imageAspect', state.imageAspect);
|
|
1093
|
+
}
|
|
1026
1094
|
});
|
|
1027
1095
|
});
|
|
1028
1096
|
function syncImageAspectActive() {
|
|
@@ -1283,6 +1351,17 @@ $('genSubmit').addEventListener('click', async () => {
|
|
|
1283
1351
|
}
|
|
1284
1352
|
let resolvedPrompt = resolveMentions(rawPrompt, mediaRefs);
|
|
1285
1353
|
if (sourceMarker) resolvedPrompt = sourceMarker + resolvedPrompt;
|
|
1354
|
+
|
|
1355
|
+
// Если выбран aspect 'source' — резолвим в реальный ratio из исходной
|
|
1356
|
+
// картинки ДО того как обнулим state.sourceRef (ниже).
|
|
1357
|
+
let resolvedImageAspect = state.imageAspect;
|
|
1358
|
+
if (state.imageAspect === 'source') {
|
|
1359
|
+
const fromSource = await computeSourceImageAspect();
|
|
1360
|
+
resolvedImageAspect = fromSource || '1:1';
|
|
1361
|
+
if (!fromSource) console.warn('[gen] не смог получить aspect исходной → fallback 1:1');
|
|
1362
|
+
else console.log(`[gen] aspect "как у исходной" → ${fromSource}`);
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1286
1365
|
// Сбрасываем sourceRef и пикеры после использования
|
|
1287
1366
|
state.sourceRef = null;
|
|
1288
1367
|
resetPicks();
|
|
@@ -1337,7 +1416,7 @@ $('genSubmit').addEventListener('click', async () => {
|
|
|
1337
1416
|
aspectRatio: state.videoAspect,
|
|
1338
1417
|
} : {}),
|
|
1339
1418
|
...(kind === 'image' ? {
|
|
1340
|
-
aspectRatio:
|
|
1419
|
+
aspectRatio: resolvedImageAspect,
|
|
1341
1420
|
} : {}),
|
|
1342
1421
|
},
|
|
1343
1422
|
};
|