kingkont 0.20.34 → 0.20.36
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/package.json +1 -1
- package/renderer/generate.js +29 -6
- package/renderer/settings.js +10 -2
- package/renderer/timeline.js +5 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kingkont",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.36",
|
|
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/generate.js
CHANGED
|
@@ -2507,21 +2507,32 @@ async function mutateNode(bKey, boardHandle, nodeId, mutator) {
|
|
|
2507
2507
|
const fileBefore = node.file;
|
|
2508
2508
|
const statusBefore = node.status;
|
|
2509
2509
|
const stateBefore = node.generated?.state;
|
|
2510
|
+
const taskIdBefore = node.generated?.taskId;
|
|
2510
2511
|
mutator(node);
|
|
2511
2512
|
const fileChanged = node.file !== fileBefore;
|
|
2512
2513
|
const statusChanged = node.status !== statusBefore;
|
|
2513
|
-
const
|
|
2514
|
+
const taskIdChanged = node.generated?.taskId !== taskIdBefore;
|
|
2515
|
+
const stateOnly = !statusChanged && !fileChanged && !taskIdChanged
|
|
2516
|
+
&& node.generated?.state !== stateBefore;
|
|
2517
|
+
// === State-only: cosmetic-обновление статуса генерации ====================
|
|
2518
|
+
// Юзер: «а зачем ты меняешь ноду во время генерации?» Раньше каждый
|
|
2519
|
+
// pollJob-тик (≈4s) дёргал scheduleSave → writeFile scene.json. За одно
|
|
2520
|
+
// видео — 10-30 записей чисто ради текста "queued"/"generating".
|
|
2521
|
+
// Это пузырило external-watcher (mtime drift), бил по диску и cloud-sync,
|
|
2522
|
+
// плюс засирал history-slot transient'ами.
|
|
2523
|
+
// Resume после рестарта использует n.status='generating' + n.generated.taskId
|
|
2524
|
+
// — оба пишутся ОДИН раз в начале джоба и не меняются. state — чисто UI.
|
|
2525
|
+
// Поэтому здесь: только in-memory + DOM, без scheduleSave/syncHistorySlot.
|
|
2526
|
+
if (stateOnly) {
|
|
2527
|
+
updateNodeStateText(nodeId, node.generated?.state);
|
|
2528
|
+
return;
|
|
2529
|
+
}
|
|
2514
2530
|
// Если файл сменился (после регена) — подхватить новый файл в клипах таймлайна
|
|
2515
2531
|
if (fileChanged && node.file) {
|
|
2516
2532
|
syncTimelineClipsForNode(node);
|
|
2517
2533
|
}
|
|
2518
2534
|
syncHistorySlot(node);
|
|
2519
2535
|
scheduleSave();
|
|
2520
|
-
// Частый кейс: меняется только текст статуса генерации. Точечно обновляем .state-text,
|
|
2521
|
-
// не перестраивая всё тело ноды и не дёргая таймлайн.
|
|
2522
|
-
if (stateOnly && updateNodeStateText(nodeId, node.generated?.state)) {
|
|
2523
|
-
return;
|
|
2524
|
-
}
|
|
2525
2536
|
await refreshNodeDOM(nodeId);
|
|
2526
2537
|
if (fileChanged || statusChanged) refreshTimelineForNode(nodeId);
|
|
2527
2538
|
return;
|
|
@@ -2534,7 +2545,19 @@ async function mutateNode(bKey, boardHandle, nodeId, mutator) {
|
|
|
2534
2545
|
const meta = await loadBoardMetadata(boardHandle);
|
|
2535
2546
|
const node = meta.nodes.find(n => n.id === nodeId);
|
|
2536
2547
|
if (!node) return;
|
|
2548
|
+
const fileBefore = node.file;
|
|
2549
|
+
const statusBefore = node.status;
|
|
2550
|
+
const stateBefore = node.generated?.state;
|
|
2551
|
+
const taskIdBefore = node.generated?.taskId;
|
|
2537
2552
|
mutator(node);
|
|
2553
|
+
// Если ни file, ни status, ни taskId не поменялись — это cosmetic-обновление
|
|
2554
|
+
// state (см. подробности в branch выше для текущей доски). Не пишем на диск.
|
|
2555
|
+
// Узел не на экране (другая доска) — UI всё равно не виден.
|
|
2556
|
+
const isStateOnly = node.file === fileBefore
|
|
2557
|
+
&& node.status === statusBefore
|
|
2558
|
+
&& node.generated?.taskId === taskIdBefore
|
|
2559
|
+
&& node.generated?.state !== stateBefore;
|
|
2560
|
+
if (isStateOnly) return;
|
|
2538
2561
|
syncHistorySlot(node);
|
|
2539
2562
|
await saveBoardMetadata(boardHandle, meta);
|
|
2540
2563
|
}
|
package/renderer/settings.js
CHANGED
|
@@ -1666,11 +1666,19 @@ async function pasteClipboardNodes() {
|
|
|
1666
1666
|
|
|
1667
1667
|
// Если paste идёт в ту же доску, где копировали — кладём рядом с оригиналами
|
|
1668
1668
|
// (oldX + offset, oldY + offset). При cross-board — переносим bbox в видимую область.
|
|
1669
|
+
// ВАЖНО: scrollLeft/Top — координаты в frame-системе (включая canvas-pad-x/y),
|
|
1670
|
+
// а ноды живут в canvas-content-coord. Конвертация:
|
|
1671
|
+
// canvas-x = (scrollLeft - padX) / zoom
|
|
1672
|
+
// Раньше тут было `scrollLeft / zoom + 80 - minX` — без вычета padding,
|
|
1673
|
+
// и cross-board paste улетал примерно на (padX/zoom, padY/zoom) ≈ (2000, 1500)
|
|
1674
|
+
// в правую/нижнюю невидимую часть canvas'а. Юзер: «вставка попадает в
|
|
1675
|
+
// невидимую область».
|
|
1669
1676
|
const sameBoard = state.clipboardSourceBoardKey === state.currentBoard.key;
|
|
1677
|
+
const _pad = (typeof _getFramePadding === 'function') ? _getFramePadding() : { padX: 0, padY: 0 };
|
|
1670
1678
|
const baseX = sameBoard ? offset
|
|
1671
|
-
: (canvasWrap.scrollLeft / state.zoom + 80 - minX);
|
|
1679
|
+
: ((canvasWrap.scrollLeft - _pad.padX) / state.zoom + 80 - minX);
|
|
1672
1680
|
const baseY = sameBoard ? offset
|
|
1673
|
-
: (canvasWrap.scrollTop / state.zoom + 80 - minY);
|
|
1681
|
+
: ((canvasWrap.scrollTop - _pad.padY) / state.zoom + 80 - minY);
|
|
1674
1682
|
|
|
1675
1683
|
for (const item of state.clipboard) {
|
|
1676
1684
|
const oldNode = item.node;
|
package/renderer/timeline.js
CHANGED
|
@@ -1779,8 +1779,11 @@ async function replicaToBoard(charInfo, replica, statusEl) {
|
|
|
1779
1779
|
type: 'audio',
|
|
1780
1780
|
name: slug,
|
|
1781
1781
|
file,
|
|
1782
|
-
|
|
1783
|
-
|
|
1782
|
+
// Конвертация frame-coord → canvas-content-coord: вычитаем padX/padY.
|
|
1783
|
+
// Без этого нода с реплики персонажа улетает в невидимую правую/нижнюю
|
|
1784
|
+
// часть canvas'а (≈ padX, padY смещение).
|
|
1785
|
+
x: (canvasWrap.scrollLeft - ((typeof _getFramePadding === 'function' ? _getFramePadding().padX : 0))) / state.zoom + 80,
|
|
1786
|
+
y: (canvasWrap.scrollTop - ((typeof _getFramePadding === 'function' ? _getFramePadding().padY : 0))) / state.zoom + 80,
|
|
1784
1787
|
generated: {
|
|
1785
1788
|
kind: 'audio', prompt: replica.text, rawPrompt: replica.text,
|
|
1786
1789
|
model: 'eleven_v3', voiceId: charInfo.voice, voiceName: charInfo.voiceName || '',
|