kingkont 0.20.5 → 0.20.6

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.5",
3
+ "version": "0.20.6",
4
4
  "description": "KingKont · Chatium — нод-редактор сцен с AI-генерацией (картинки/видео/голос/SFX/музыка/текст)",
5
5
  "main": "main.js",
6
6
  "bin": {
package/renderer/board.js CHANGED
@@ -1572,6 +1572,12 @@ async function closeProject() {
1572
1572
  catch (e) { console.warn(`[closeProject] ${label} failed:`, e?.message || e); }
1573
1573
  };
1574
1574
 
1575
+ // КРИТИЧНО первым шагом: слить pending debounced save (см. media.js
1576
+ // scheduleSave). Без этого мутации только что отработавшего chat-тула
1577
+ // теряются — saveTimer ещё ждёт 300мс, а мы уже обнулили state.currentBoard.
1578
+ await safeAwait('flush pending save', async () => {
1579
+ if (typeof window.flushScheduledSave === 'function') await window.flushScheduledSave();
1580
+ });
1575
1581
  safe('persist welcome flag', () => {
1576
1582
  localStorage.setItem('welcomeOnNextStart', '1');
1577
1583
  localStorage.setItem('lastLocation', 'welcome');
package/renderer/media.js CHANGED
@@ -345,14 +345,23 @@ const _mediaHydrationObserver = new IntersectionObserver((entries) => {
345
345
  }, { rootMargin: '400px' });
346
346
 
347
347
  let saveTimer = null;
348
+ let _saveInFlight = null; // promise of currently-running save, или null
348
349
  function scheduleSave() {
349
350
  if (!state.currentBoard) return;
350
351
  // Mark dirty чтобы external file-watcher (pollExternalChanges в board.js)
351
352
  // знал что у нас есть pending изменения; reload без подтверждения не делаем.
352
353
  state.currentBoard.dirty = true;
353
354
  clearTimeout(saveTimer);
354
- saveTimer = setTimeout(async () => {
355
- saveTimer = null;
355
+ saveTimer = setTimeout(() => { saveTimer = null; _runSaveNow(); }, 300);
356
+ }
357
+
358
+ // Сериализуем последовательные save'ы (если предыдущий ещё в полёте — ждём
359
+ // его прежде чем запускать новый). Без этого два scheduleSave подряд могли
360
+ // бы конкурентно дёргать FSAH-handle.
361
+ async function _runSaveNow() {
362
+ if (_saveInFlight) { try { await _saveInFlight; } catch {} }
363
+ if (!state.currentBoard?.handle) return;
364
+ _saveInFlight = (async () => {
356
365
  try {
357
366
  const mtime = await saveBoardMetadata(state.currentBoard.handle, state.currentBoard.metadata);
358
367
  // Обновляем lastDiskMtime — иначе pollExternalChanges подумает что это
@@ -362,8 +371,24 @@ function scheduleSave() {
362
371
  }
363
372
  if (state.currentBoard) state.currentBoard.dirty = false;
364
373
  } catch (e) { console.error('save failed', e); }
365
- }, 300);
374
+ })();
375
+ try { await _saveInFlight; } finally { _saveInFlight = null; }
376
+ }
377
+
378
+ // Принудительно слить pending debounced save (и дождаться уже идущего).
379
+ // Нужно перед closeProject — иначе тулы чата, которые только что
380
+ // scheduleSave'нули, теряют изменения когда state.currentBoard обнуляется
381
+ // до того как 300мс-таймер сработает.
382
+ async function flushScheduledSave() {
383
+ if (saveTimer) {
384
+ clearTimeout(saveTimer);
385
+ saveTimer = null;
386
+ await _runSaveNow();
387
+ } else if (_saveInFlight) {
388
+ try { await _saveInFlight; } catch {}
389
+ }
366
390
  }
391
+ window.flushScheduledSave = flushScheduledSave;
367
392
 
368
393
  async function refreshNodeDOM(nodeId) {
369
394
  if (!state.currentBoard) return;