kingkont 0.20.6 → 0.20.7

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 CHANGED
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>Видео-редактор сериала</title>
6
+ <title>KingKont</title>
7
7
  <link rel="stylesheet" href="renderer/styles.css">
8
8
  </head>
9
9
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kingkont",
3
- "version": "0.20.6",
3
+ "version": "0.20.7",
4
4
  "description": "KingKont · Chatium — нод-редактор сцен с AI-генерацией (картинки/видео/голос/SFX/музыка/текст)",
5
5
  "main": "main.js",
6
6
  "bin": {
package/renderer/board.js CHANGED
@@ -1467,6 +1467,8 @@ async function openFilm(handle) {
1467
1467
  // Подзаголовок шапки = имя открытого проекта (вместо «Видео-редактор»).
1468
1468
  const sub = $('brandSub');
1469
1469
  if (sub) { sub.textContent = handle.name; sub.classList.add('has-project'); }
1470
+ // Tab title = «<имя> — KingKont».
1471
+ document.title = (handle.name || 'Project') + ' — KingKont';
1470
1472
  // Для cloud-проектов имя авторитетное на сервере (manifest.name). Если
1471
1473
  // юзер пришёл по уведомлению / прямой ссылке — handle.name мог быть
1472
1474
  // id или старое имя; подтягиваем актуальное и обновляем шапку +
@@ -1477,6 +1479,7 @@ async function openFilm(handle) {
1477
1479
  .then(proj => {
1478
1480
  if (!proj?.name || state.cloudProjectId !== proj.id) return;
1479
1481
  if (sub && proj.name !== sub.textContent) sub.textContent = proj.name;
1482
+ document.title = proj.name + ' — KingKont';
1480
1483
  // Patch handle.name (используется sidebar/recents и т.п.).
1481
1484
  try { handle.name = proj.name; } catch {}
1482
1485
  // Sync cache.
@@ -1518,6 +1521,7 @@ async function openFilm(handle) {
1518
1521
  }
1519
1522
 
1520
1523
  const raw = localStorage.getItem(`lastBoard:${handle.name}`);
1524
+ let restored = false;
1521
1525
  if (raw) {
1522
1526
  try {
1523
1527
  const last = JSON.parse(raw);
@@ -1525,8 +1529,18 @@ async function openFilm(handle) {
1525
1529
  : last.kind === 'location' ? await listLocations(handle)
1526
1530
  : await listEpisodes(handle);
1527
1531
  const found = list.find(x => x.name === last.name);
1528
- if (found) await selectBoard({ kind: last.kind, ...found });
1529
- } catch {}
1532
+ if (found) { await selectBoard({ kind: last.kind, ...found }); restored = true; }
1533
+ } catch (e) { console.warn('[openFilm] restore lastBoard failed:', e?.message || e); }
1534
+ }
1535
+ // Нет запомненной доски (первый open проекта) — авто-открываем первую сцену.
1536
+ // Без этого юзер видит пустой canvas с подсказкой «выбери сцену» и должен
1537
+ // лишний раз кликать в сайдбаре.
1538
+ if (!restored && !state.currentBoard) {
1539
+ try {
1540
+ const eps = await listEpisodes(handle);
1541
+ console.log('[openFilm] auto-select first scene:', eps.length, 'episodes');
1542
+ if (eps.length) await selectBoard({ kind: 'episode', ...eps[0] });
1543
+ } catch (e) { console.warn('[openFilm] auto-select failed:', e?.message || e); }
1530
1544
  }
1531
1545
 
1532
1546
  // Сканируем все доски на незавершённые задачи генерации (после перезагрузки)
@@ -1605,6 +1619,12 @@ async function closeProject() {
1605
1619
  safe('dispatch project-changed', () => {
1606
1620
  window.dispatchEvent(new CustomEvent('project-changed'));
1607
1621
  });
1622
+ // Web: чистим hash (мы вернулись на welcome).
1623
+ if (window.__KINGKONT_WEB__ && location.hash.startsWith('#project=')) {
1624
+ history.replaceState(null, '', location.pathname + location.search);
1625
+ }
1626
+ // Reset tab title.
1627
+ document.title = 'KingKont';
1608
1628
  state.charactersInfo = [];
1609
1629
  state.locationsInfo = [];
1610
1630
  state.selectedNodeIds.clear();
@@ -279,6 +279,7 @@
279
279
  touchCloudOpened(projectId);
280
280
  await openFilm(handle);
281
281
  setCloudButtonsVisibility();
282
+ if (window.__KINGKONT_WEB__) location.hash = '#project=' + projectId;
282
283
  return;
283
284
  }
284
285
  }
@@ -398,6 +399,7 @@
398
399
  touchCloudOpened(projectId);
399
400
  await openFilm(handle);
400
401
  setCloudButtonsVisibility();
402
+ if (window.__KINGKONT_WEB__) location.hash = '#project=' + projectId;
401
403
  PROGRESS.hide();
402
404
  } catch (e) {
403
405
  PROGRESS.hide();
@@ -24,6 +24,14 @@ canvasWrap.addEventListener('contextmenu', e => {
24
24
  // Rubber-band селектор + сброс выделения по клику в пустое место
25
25
  canvasWrap.addEventListener('mousedown', e => {
26
26
  if (!state.currentBoard) return;
27
+ // Middle-button (button=1) → pan-режим. Дефолтное поведение браузера —
28
+ // auto-scroll режим со специальным курсором; перехватываем чтобы пана
29
+ // через drag, без активации auto-scroll.
30
+ if (e.button === 1) {
31
+ e.preventDefault();
32
+ startMiddleMousePan(e);
33
+ return;
34
+ }
27
35
  if (e.button !== 0) return;
28
36
  if (e.target.closest('.node, .conn, .anchor, .resize-handle, button, input, textarea, select, .modal, #addMenu, #nodeMenu')) return;
29
37
  // Если без модификатора — сбрасываем старое выделение
@@ -36,6 +44,29 @@ canvasWrap.addEventListener('mousedown', e => {
36
44
  startRubberBand(e, additive);
37
45
  });
38
46
 
47
+ // Pan через зажатую среднюю кнопку. Курсор меняем на grab во время дrag'а.
48
+ function startMiddleMousePan(e) {
49
+ const startX = e.clientX, startY = e.clientY;
50
+ const startScrollLeft = canvasWrap.scrollLeft;
51
+ const startScrollTop = canvasWrap.scrollTop;
52
+ const prevCursor = canvasWrap.style.cursor;
53
+ canvasWrap.style.cursor = 'grabbing';
54
+ document.body.style.userSelect = 'none';
55
+ const onMove = ev => {
56
+ canvasWrap.scrollLeft = startScrollLeft - (ev.clientX - startX);
57
+ canvasWrap.scrollTop = startScrollTop - (ev.clientY - startY);
58
+ };
59
+ const onUp = ev => {
60
+ if (ev.button !== 1) return;
61
+ document.removeEventListener('mousemove', onMove);
62
+ document.removeEventListener('mouseup', onUp);
63
+ canvasWrap.style.cursor = prevCursor;
64
+ document.body.style.userSelect = '';
65
+ };
66
+ document.addEventListener('mousemove', onMove);
67
+ document.addEventListener('mouseup', onUp);
68
+ }
69
+
39
70
  function startRubberBand(e, additive) {
40
71
  const rectStart = canvas.getBoundingClientRect();
41
72
  const startC = { x: (e.clientX - rectStart.left) / state.zoom, y: (e.clientY - rectStart.top) / state.zoom };