kingkont 0.10.7 → 0.10.8

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.10.7",
3
+ "version": "0.10.8",
4
4
  "description": "KingKont · Chatium — нод-редактор сцен с AI-генерацией (картинки/видео/голос/SFX/музыка/текст)",
5
5
  "main": "main.js",
6
6
  "bin": {
package/renderer/board.js CHANGED
@@ -913,6 +913,10 @@ async function openFilm(handle) {
913
913
  showEmpty();
914
914
  // Cloud-кнопки: показать «☁ Сохранить на сервер» когда проект открыт.
915
915
  if (window.cloudProjects?.setVisibility) window.cloudProjects.setVisibility();
916
+ // Загружаем chat-историю проекта (если есть .kingkont-chat.json).
917
+ if (window.kingChat?.loadFromCurrentProject) {
918
+ window.kingChat.loadFromCurrentProject().catch(() => {});
919
+ }
916
920
 
917
921
  const raw = localStorage.getItem(`lastBoard:${handle.name}`);
918
922
  if (raw) {
@@ -956,10 +960,10 @@ async function closeProject() {
956
960
  // Помечаем что юзер вышел явно — на следующем старте autoload пропускается.
957
961
  // Cmd+R после close = welcome, а не реоткрытие.
958
962
  try { localStorage.setItem('welcomeOnNextStart', '1'); } catch {}
959
- // Чат привязан к одному проекту — чистим историю при выходе чтобы
960
- // не путал контекст следующего открытого проекта. И прячем панель —
961
- // на welcome-экране чат не имеет смысла (нет state.currentBoard).
962
- if (window.kingChat?.clear) window.kingChat.clear();
963
+ // Чат привязан к одному проекту — сбрасываем in-memory (НЕ трогая
964
+ // .kingkont-chat.json на диске; при следующем открытии загрузится).
965
+ // Прячем панель — на welcome без проекта чат не имеет смысла.
966
+ if (window.kingChat?.resetInMemory) window.kingChat.resetInMemory();
963
967
  if (window.kingChat?.close) window.kingChat.close();
964
968
  stopExternalWatcher();
965
969
  // Сбрасываем UI таймлайна/превью — иначе при возврате через welcome
package/renderer/chat.js CHANGED
@@ -243,6 +243,50 @@
243
243
  return text.replace(/<tool>[\s\S]*?<\/tool>/g, '').trim();
244
244
  }
245
245
 
246
+ // ============== PERSISTENCE ==============
247
+ // Чат-история живёт в `<filmHandle>/.kingkont-chat.json`. Работает для
248
+ // папочных проектов и cloud (cloudFs-shim — те же FSAH-методы).
249
+ // Загружается в loadHistoryFromCurrentProject() — board.js зовёт после openFilm.
250
+ // Сохраняется debounced на каждое изменение history (см. persistDebounced).
251
+ const CHAT_FILE = '.kingkont-chat.json';
252
+ let _persistTimer = null;
253
+ async function persistNow() {
254
+ if (!state.filmHandle) return;
255
+ try {
256
+ const fh = await state.filmHandle.getFileHandle(CHAT_FILE, { create: true });
257
+ const w = await fh.createWritable();
258
+ // Не сохраняем role==='system' (буфер) и tool_result-турны (промежуточные,
259
+ // нужны только модели). При re-load восстановим только pure user/assistant.
260
+ const persistable = history.filter(m =>
261
+ m.role !== 'system' &&
262
+ !(m.role === 'user' && m.content?.startsWith('<tool_result>')),
263
+ );
264
+ await w.write(JSON.stringify({ history: persistable, savedAt: Date.now() }, null, 2));
265
+ await w.close();
266
+ } catch (e) {
267
+ console.warn('[chat] persist failed:', e?.message || e);
268
+ }
269
+ }
270
+ function persistDebounced() {
271
+ clearTimeout(_persistTimer);
272
+ _persistTimer = setTimeout(persistNow, 600);
273
+ }
274
+ async function loadHistoryFromCurrentProject() {
275
+ if (!state.filmHandle) { history = []; renderHistory(); return; }
276
+ try {
277
+ const fh = await state.filmHandle.getFileHandle(CHAT_FILE);
278
+ const txt = await (await fh.getFile()).text();
279
+ const data = JSON.parse(txt);
280
+ if (Array.isArray(data?.history)) {
281
+ history = data.history;
282
+ renderHistory();
283
+ return;
284
+ }
285
+ } catch {} // нет файла или JSON — норма для нового проекта
286
+ history = [];
287
+ renderHistory();
288
+ }
289
+
246
290
  // ============== UI ==============
247
291
  let history = []; // [{role: 'user'|'assistant'|'system', content: string, tools?: [...], results?: [...]}]
248
292
  let busy = false;
@@ -364,6 +408,7 @@
364
408
  busy = true;
365
409
  history.push({ role: 'user', content: userText });
366
410
  renderHistory();
411
+ persistDebounced();
367
412
  const status = appendStatus('Claude думает…');
368
413
  const system = buildSystemPrompt();
369
414
  try {
@@ -384,6 +429,7 @@
384
429
  // Финальный ответ — больше нечего исполнять.
385
430
  status.remove();
386
431
  renderHistory();
432
+ persistDebounced();
387
433
  break;
388
434
  }
389
435
 
@@ -406,6 +452,7 @@
406
452
  }
407
453
  }
408
454
  renderHistory();
455
+ persistDebounced(); // assistant-турн с tool-вызовами уже частично готов
409
456
 
410
457
  // Шлём результаты обратно как user-message (чтобы Claude увидел и продолжил).
411
458
  const resultsMsg = results.map(r => `<tool_result>${JSON.stringify(r)}</tool_result>`).join('\n');
@@ -515,6 +562,9 @@
515
562
  const panel = $('chatPanel');
516
563
  panel.classList.toggle('hidden');
517
564
  if (!panel.classList.contains('hidden')) {
565
+ // Отрисовываем сохранённую историю при показе (на случай если она
566
+ // была подгружена в фоне через loadFromCurrentProject до первого toggle).
567
+ renderHistory();
518
568
  setTimeout(() => $('chatInput')?.focus(), 50);
519
569
  }
520
570
  }
@@ -525,7 +575,13 @@
525
575
  open: () => { ensureUI(); $('chatPanel').classList.remove('hidden'); setTimeout(() => $('chatInput')?.focus(), 50); },
526
576
  close: () => $('chatPanel')?.classList.add('hidden'),
527
577
  send,
528
- clear: () => { history = []; renderHistory(); },
578
+ // User-clear: чистит И на диске тоже (юзер явно нажал ⌫).
579
+ clear: () => { history = []; renderHistory(); persistNow().catch(() => {}); },
580
+ // Reset-on-close: только in-memory + UI, на диске НЕ трогает (история
581
+ // должна остаться чтобы при следующем открытии того же проекта подгрузилась).
582
+ resetInMemory: () => { history = []; renderHistory(); },
583
+ // board.js зовёт после openFilm чтобы подгрузить chat-историю проекта.
584
+ loadFromCurrentProject: () => loadHistoryFromCurrentProject(),
529
585
  tools: TOOLS,
530
586
  };
531
587