kingkont 0.14.4 → 0.14.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.14.4",
3
+ "version": "0.14.6",
4
4
  "description": "KingKont · Chatium — нод-редактор сцен с AI-генерацией (картинки/видео/голос/SFX/музыка/текст)",
5
5
  "main": "main.js",
6
6
  "bin": {
package/renderer/board.js CHANGED
@@ -1873,9 +1873,20 @@ async function selectBoard(board) {
1873
1873
  applyZoomStyles(state.zoom);
1874
1874
  $('zoomLabel').textContent = Math.round(state.zoom * 100) + '%';
1875
1875
  }
1876
- // ВАЖНО: для backward-compat старых view (где scrollLeft/Top сохранены
1877
- // БЕЗ padding'а — до этой фичи) добавляем padding к сохранённому
1878
- // значению. Если view свежий и >= padding'а, считаем что padding уже учтён.
1876
+ } else {
1877
+ state.zoom = 1;
1878
+ applyZoomStyles(1);
1879
+ $('zoomLabel').textContent = '100%';
1880
+ }
1881
+ // ВАЖНО: applyZoomStyles ресайзит frame через setTimeout 200ms. Если
1882
+ // ставить scrollLeft СРАЗУ — браузер клампит к старому frame.scrollWidth
1883
+ // (zoom<1 → frame маленький → scroll урезается). Поэтому сначала
1884
+ // форсим frame в полный размер прямо сейчас, потом ставим scroll.
1885
+ const z = state.zoom || 1;
1886
+ document.querySelector('.canvas-frame').style.width = (6000 * z + 2 * padX) + 'px';
1887
+ document.querySelector('.canvas-frame').style.height = (4000 * z + 2 * padY) + 'px';
1888
+ if (view) {
1889
+ // Backward-compat старых view (без padding) — добавляем.
1879
1890
  if (typeof view.scrollLeft === 'number') {
1880
1891
  canvasWrap.scrollLeft = view.scrollLeft >= padX ? view.scrollLeft : view.scrollLeft + padX;
1881
1892
  } else canvasWrap.scrollLeft = padX;
@@ -1883,21 +1894,14 @@ async function selectBoard(board) {
1883
1894
  canvasWrap.scrollTop = view.scrollTop >= padY ? view.scrollTop : view.scrollTop + padY;
1884
1895
  } else canvasWrap.scrollTop = padY;
1885
1896
  } else {
1886
- state.zoom = 1;
1887
- applyZoomStyles(1);
1888
- $('zoomLabel').textContent = '100%';
1889
1897
  canvasWrap.scrollLeft = padX;
1890
1898
  canvasWrap.scrollTop = padY;
1891
1899
  }
1892
1900
 
1893
- // Auto-scroll к bbox нод ТОЛЬКО если view не был сохранён ранее
1894
- // (т.е. это первый раз открываем доску, или юзер ни разу не скроллил).
1895
- // Раньше срабатывало всегда из-за гонки с applyZoomStyles (resize
1896
- // через setTimeout 200ms) scrollLeft при zoom<1 клампился, bbox казался
1897
- // невидимым, view перебивался → юзер терял сохранённую позицию.
1898
- if (!view || (typeof view.scrollLeft !== 'number' && typeof view.scrollTop !== 'number')) {
1899
- requestAnimationFrame(() => _autoScrollToNodesIfHidden(padX, padY));
1900
- }
1901
+ // Auto-scroll к bbox нод если ни одна не попадает в viewport.
1902
+ // Frame уже ресайзнут синхронно выше, scroll проставлен корректно
1903
+ // в next frame _autoScrollToNodesIfHidden имеет валидную картинку.
1904
+ requestAnimationFrame(() => _autoScrollToNodesIfHidden(padX, padY));
1901
1905
 
1902
1906
  // Возобновить незавершённые джобы текущей доски
1903
1907
  for (const n of state.currentBoard.metadata.nodes) {
package/renderer/chat.js CHANGED
@@ -816,7 +816,8 @@
816
816
  const parts = [];
817
817
  if (ctx.scene) parts.push({ key: 'scene', label: `🎬 ${ctx.scene.name}`, removable: false });
818
818
  // Группируем выделенные ноды по type. Если 1 — показываем имя/id;
819
- // если 2+ — «N картинок» с локализацией.
819
+ // если 2+ — «N картинок» с локализацией. Клик по чипу — снимает
820
+ // выделение с этой ноды (или со всех нод этого type для группы).
820
821
  const byType = {};
821
822
  for (const sel of ctx.selected) {
822
823
  (byType[sel.type] = byType[sel.type] || []).push(sel);
@@ -825,10 +826,28 @@
825
826
  const icon = type === 'image' ? '🖼' : type === 'video' ? '🎬' : type === 'audio' ? '🎙' : type === 'text' ? '📝' : '◉';
826
827
  if (items.length === 1) {
827
828
  const s = items[0];
828
- parts.push({ key: 'sel:' + s.id, label: `${icon} ${s.name || s.id.slice(0, 6)}`, removable: false });
829
+ parts.push({
830
+ key: 'sel:' + s.id,
831
+ label: `${icon} ${s.name || s.id.slice(0, 6)}`,
832
+ removable: true,
833
+ onRemove: () => {
834
+ state.selectedNodeIds.delete(s.id);
835
+ if (typeof renderSelection === 'function') renderSelection();
836
+ renderContextRow();
837
+ },
838
+ });
829
839
  } else {
830
840
  const noun = _typeForms[type] || ['нода','ноды','нод'];
831
- parts.push({ key: 'sel-grp:' + type, label: `${icon} ${items.length} ${_ru_plural(items.length, noun)}`, removable: false });
841
+ parts.push({
842
+ key: 'sel-grp:' + type,
843
+ label: `${icon} ${items.length} ${_ru_plural(items.length, noun)}`,
844
+ removable: true,
845
+ onRemove: () => {
846
+ for (const it of items) state.selectedNodeIds.delete(it.id);
847
+ if (typeof renderSelection === 'function') renderSelection();
848
+ renderContextRow();
849
+ },
850
+ });
832
851
  }
833
852
  }
834
853
  for (const a of ctx.attachments) {
@@ -260,6 +260,10 @@
260
260
  box-shadow: -8px 0 32px rgba(0,0,0,0.4);
261
261
  /* --chat-font задаётся inline в chat.js (Cmd+/Cmd-). Дефолт 13px. */
262
262
  font-size: var(--chat-font, 13px);
263
+ /* Защита от horizontal-overflow: дочерние flex-элементы (длинные
264
+ имена чипов, tool-args в pre) могли push'ать ширину — fixed
265
+ position спасал, но без этого max-width дочерних не работал. */
266
+ overflow-x: hidden; box-sizing: border-box;
263
267
  }
264
268
  .chat-panel.chat-drag::before {
265
269
  content: '📎 Отпусти, чтобы прикрепить файл к чату';
@@ -274,6 +278,11 @@
274
278
  display: flex; flex-wrap: wrap; gap: 4px;
275
279
  padding: 6px 10px;
276
280
  background: #1a1a1a; border-top: 1px solid #2a2a2a;
281
+ /* min-width:0 — без этого flex-children не «сжимаются», и длинная
282
+ строка чипов раздвигает родителя (chat-panel получает горизонтальный
283
+ overflow). С flex-wrap:wrap + min-width:0 чипы корректно
284
+ переносятся на следующую строку. */
285
+ min-width: 0; max-width: 100%; box-sizing: border-box;
277
286
  }
278
287
  .chat-context-row:empty { display: none; }
279
288
  .chat-chip {
@@ -281,7 +290,12 @@
281
290
  background: #232a36; border: 1px solid #2c3848;
282
291
  color: #aac; font-size: 11px;
283
292
  padding: 2px 8px; border-radius: 999px;
284
- max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
293
+ max-width: 100%; /* был 200px длинные имена ужимались, но
294
+ если width контейнера меньше 200px, чип всё
295
+ ещё мог push'нуть строку. 100% относительно
296
+ .chat-context-row → гарантированно влезает. */
297
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
298
+ min-width: 0; /* чтобы flex-shrink работал */
285
299
  }
286
300
  .chat-chip-x {
287
301
  background: transparent; border: none; color: #889;