kingkont 0.20.33 → 0.20.35

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kingkont",
3
- "version": "0.20.33",
3
+ "version": "0.20.35",
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
@@ -3615,13 +3615,25 @@ async function checkExternalChanges() {
3615
3615
  stopExternalWatcher();
3616
3616
  return;
3617
3617
  }
3618
- // Если у нас сейчас pending save (saveTimer != null в media.js) — пропускаем
3619
- // тик; иначе сравним свой grace-mtime с актуальным после нашего же flush.
3620
- // Для простоты не лезем в saveTimer dirty флаг покрывает этот сценарий.
3618
+ // Во время активной генерации НЕ реагируем на изменения mtime. Юзер:
3619
+ // «больше с этими файлами никто не работает, это происходит во время
3620
+ // генерации». pollJob и mutateNode регулярно мутируют scene.json через
3621
+ // scheduleSave (статус «submitting» → «queued» → «generating»…), и
3622
+ // микросекундная рассинхронизация между нашим записанным mtime и
3623
+ // snapshot'ом `lastDiskMtime` приводила к ложным алертам «изменён извне».
3624
+ // Watcher остаётся осмысленным для CLI / git-pull / другого редактора —
3625
+ // те случаи естественно происходят когда генерации не запущены.
3626
+ if (state.jobs && state.jobs.size > 0) return;
3627
+ // Grace-period 2s после последнего нашего save: на случай если только
3628
+ // что отстреливший save обновил lastDiskMtime с лагом и watcher попал
3629
+ // в окно «mtime записан, snapshot ещё обновляется». 2s достаточно для
3630
+ // любых внутренних async-цепочек, но мало чтобы реально пропустить
3631
+ // внешнее изменение — watcher подхватит его через следующие 3s тика.
3632
+ const GRACE_MS = 2000;
3621
3633
  const mtime = await _readSceneMtime(state.currentBoard.handle);
3622
3634
  if (mtime == null || state.currentBoard.lastDiskMtime == null)
3623
3635
  return;
3624
- if (mtime <= state.currentBoard.lastDiskMtime)
3636
+ if (mtime <= state.currentBoard.lastDiskMtime + GRACE_MS)
3625
3637
  return;
3626
3638
  // Обнаружено внешнее изменение.
3627
3639
  _externalReloadInFlight = true;
@@ -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 stateOnly = !statusChanged && !fileChanged && node.generated?.state !== stateBefore;
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
  }