kingkont 0.18.6 → 0.18.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kingkont",
3
- "version": "0.18.6",
3
+ "version": "0.18.7",
4
4
  "description": "KingKont · Chatium — нод-редактор сцен с AI-генерацией (картинки/видео/голос/SFX/музыка/текст)",
5
5
  "main": "main.js",
6
6
  "bin": {
package/renderer/board.js CHANGED
@@ -2461,11 +2461,16 @@ async function createNodeEl(node) {
2461
2461
  el.dataset.id = node.id;
2462
2462
  el.style.left = node.x + 'px';
2463
2463
  el.style.top = node.y + 'px';
2464
- // Label-ноды всегда auto-размер (по тексту) — фиксированный width/height
2465
- // создавал «невидимые боксы» вокруг текста, что ломало hit-testing когда
2466
- // несколько label оказывались рядом (клик попадал в перекрывающий, а не
2467
- // в видимый текст). Игнорируем сохранённые width/height для label-нод.
2468
- if (node.type !== 'label') {
2464
+ // Label-ноды: высоту НЕ применяем (auto по контенту), но ширину ДА,
2465
+ // если юзер явно её задал через label-width-handle. Это позволяет
2466
+ // сделать многострочный label («ширина задаёт перенос»). Без width
2467
+ // auto-width по тексту (как раньше).
2468
+ if (node.type === 'label') {
2469
+ if (node.width) {
2470
+ el.style.width = node.width + 'px';
2471
+ el.dataset.fixedWidth = '1'; // CSS: разрешаем label-text занять 100%
2472
+ }
2473
+ } else {
2469
2474
  if (node.width) el.style.width = node.width + 'px';
2470
2475
  if (node.height) el.style.height = node.height + 'px';
2471
2476
  }
@@ -2514,6 +2519,18 @@ async function createNodeEl(node) {
2514
2519
  el.appendChild(rh);
2515
2520
  attachResize(el, node, rh);
2516
2521
 
2522
+ // Label-ноды получают ВТОРОЙ хендл — на правом краю — для управления
2523
+ // только ШИРИНОЙ (юзер просил: «ноде лейбл позволь менять ширину
2524
+ // отдельно, чтобы менялось количество строк если текст длинный»).
2525
+ // Существующий .resize-handle (правый-нижний угол) меняет fontSize.
2526
+ if (node.type === 'label') {
2527
+ const wh = document.createElement('div');
2528
+ wh.className = 'label-width-handle';
2529
+ wh.title = 'Тяни — ширина (для переноса строк)';
2530
+ el.appendChild(wh);
2531
+ attachLabelWidthResize(el, node, wh);
2532
+ }
2533
+
2517
2534
  const anchor = document.createElement('div');
2518
2535
  anchor.className = 'anchor';
2519
2536
  anchor.title = 'Тяни, чтобы сослаться или сгенерировать ноду со ссылкой';
@@ -746,6 +746,48 @@ async function openGenerateForRef(fromNode, clientX, clientY, forceKind) {
746
746
  }, 50);
747
747
  }
748
748
 
749
+ // Width-only resize для label-нод. Юзер тянет хендл на правом краю —
750
+ // устанавливаем node.width, label-text оборачивается. Высота auto.
751
+ // Двойной клик по хендлу — снять width (вернуть auto-width по тексту).
752
+ function attachLabelWidthResize(el, node, handle) {
753
+ handle.addEventListener('mousedown', e => {
754
+ e.preventDefault();
755
+ e.stopPropagation();
756
+ const startX = e.clientX;
757
+ const startW = el.offsetWidth;
758
+ const labelText = el.querySelector('.label-text');
759
+ const onMove = ev => {
760
+ const dx = (ev.clientX - startX) / state.zoom;
761
+ const newW = Math.max(80, Math.min(2000, startW + dx));
762
+ node.width = Math.round(newW);
763
+ el.style.width = node.width + 'px';
764
+ el.dataset.fixedWidth = '1';
765
+ // Force label-text to fill — иначе inline-block остаётся узким.
766
+ if (labelText) labelText.style.width = '100%';
767
+ renderConnections();
768
+ };
769
+ const onUp = () => {
770
+ document.removeEventListener('mousemove', onMove);
771
+ document.removeEventListener('mouseup', onUp);
772
+ scheduleSave();
773
+ };
774
+ document.addEventListener('mousemove', onMove);
775
+ document.addEventListener('mouseup', onUp);
776
+ });
777
+ // dblclick — сбросить ширину (вернуть auto-size).
778
+ handle.addEventListener('dblclick', e => {
779
+ e.preventDefault();
780
+ e.stopPropagation();
781
+ delete node.width;
782
+ el.style.width = '';
783
+ delete el.dataset.fixedWidth;
784
+ const labelText = el.querySelector('.label-text');
785
+ if (labelText) labelText.style.width = '';
786
+ renderConnections();
787
+ scheduleSave();
788
+ });
789
+ }
790
+
749
791
  function attachResize(el, node, handle) {
750
792
  handle.addEventListener('mousedown', e => {
751
793
  e.preventDefault();
@@ -519,6 +519,12 @@
519
519
  overflow: hidden;
520
520
  }
521
521
  .welcome-card-thumb {
522
+ /* position:absolute + inset:0 — заполняем всю карточку (4:3).
523
+ БАДЖИ внутри (cloud/bg) тоже absolute → этот thumb работает как
524
+ positioning context для них. РАНЬШЕ ниже было `.welcome-card-thumb
525
+ { position: relative; }` — оно переопределяло absolute → thumb
526
+ shrink'ался до размера контента (символ «+» / «☁» / «🎬») и
527
+ прибивался к верху карточки. Слились в один rule. */
522
528
  position: absolute; inset: 0;
523
529
  background: #1a1a1a;
524
530
  display: flex; align-items: center; justify-content: center;
@@ -528,7 +534,6 @@
528
534
  .welcome-card-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
529
535
  /* ☁-бейдж в углу обложки облачного проекта. Делает cloud/folder-проекты
530
536
  визуально различимыми в общем grid'е (без необходимости второго рядa). */
531
- .welcome-card-thumb { position: relative; }
532
537
  .welcome-card-cloud-badge {
533
538
  position: absolute; right: 6px; bottom: 6px;
534
539
  background: rgba(20, 30, 50, 0.85); color: #9cf;
@@ -764,6 +769,32 @@
764
769
  }
765
770
  .node.label-node:hover .resize-handle,
766
771
  .node.label-node:focus-within .resize-handle { opacity: 1; }
772
+ /* Width-only handle на правом краю — отдельно от font-size handle.
773
+ Тянуть → меняется ширина label, текст переносится. dblclick →
774
+ сбросить ширину обратно к auto. */
775
+ .node.label-node .label-width-handle {
776
+ position: absolute;
777
+ right: -3px; top: 50%;
778
+ transform: translateY(-50%);
779
+ width: 6px; height: 36px;
780
+ background: rgba(90,168,255,0.7);
781
+ border: 1px solid rgba(255,255,255,0.35);
782
+ border-radius: 3px;
783
+ cursor: ew-resize;
784
+ opacity: 0; transition: opacity 0.15s;
785
+ z-index: 11;
786
+ }
787
+ .node.label-node:hover .label-width-handle,
788
+ .node.label-node:focus-within .label-width-handle { opacity: 1; }
789
+ /* Когда label имеет explicit width (data-fixed-width="1") — внутренний
790
+ label-text должен ЗАПОЛНЯТЬ родителя, иначе inline-block остаётся
791
+ узким и текст не переносится. Сбрасываем min/max-width inner'a. */
792
+ .node.label-node[data-fixed-width="1"] .label-text {
793
+ width: 100%;
794
+ min-width: 0;
795
+ max-width: none;
796
+ box-sizing: border-box;
797
+ }
767
798
 
768
799
  /* Body — wrapper без визуального вклада. */
769
800
  .node.label-node .node-body {