kingkont 0.20.52 → 0.20.54

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/lib/providers.js CHANGED
@@ -617,17 +617,27 @@ async function _startGenerationViaKie({ kind, prompt, key, imageInputs, videoInp
617
617
  if (aspectRatio) input.aspect_ratio = aspectRatio;
618
618
  input.output_format = 'jpg';
619
619
  // flux/sdxl быстрые, quality для них N/A — пропускаем.
620
- } else if (key === 'gpt-image-2' || key === 'gpt-image-1.5') {
621
- // OpenAI gpt-image-2 / 1.5 через KIE. Если есть imageInputs
622
- // переключаемся на image-to-image вариант slug'а (KIE использует
623
- // отдельные модели для t2i vs i2i).
620
+ } else if (key === 'gpt-image-2') {
621
+ // KIE GPT Image 2 отдельные slug для t2i vs i2i (см. switch ниже).
622
+ // Параметры для обеих:
623
+ // prompt (req), aspect_ratio (auto|1:1|9:16|16:9|4:3|3:4),
624
+ // resolution (1K|2K|4K).
625
+ // Для image-to-image — input_urls: string[] (<=16).
626
+ // (gpt-image-2 НЕ принимает image_input, output_format, quality,
627
+ // size — это OpenAI-direct поля; KIE proxy игнорирует но логирует
628
+ // warning. Не отправляем чтобы не мусорить.)
624
629
  if (imageInputs?.length) {
625
- // gpt-image-2-image-to-image — same input.image_input format,
626
- // нужно сменить fullModel ниже.
630
+ input.input_urls = imageInputs.slice(0, 16);
627
631
  }
632
+ // KIE: при aspect_ratio='auto' принудительно понижает resolution до 1K.
633
+ // Явный 16:9 даёт 2K/4K если попросит.
634
+ input.aspect_ratio = aspectRatio || '16:9';
635
+ input.resolution = resolution || '1K';
636
+ } else if (key === 'gpt-image-1.5') {
637
+ // gpt-image-1.5 (KIE): «OpenAI-стиль» параметры (size/quality/format) —
638
+ // совпадает с OpenAI direct API.
628
639
  if (aspectRatio) input.size = _gptImageAspectToSize(aspectRatio);
629
640
  if (imageInputs?.length) input.image_input = imageInputs;
630
- // quality: 'low' | 'medium' | 'high' — gpt-image поддерживает.
631
641
  if (quality) input.quality = quality;
632
642
  input.output_format = 'jpg';
633
643
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kingkont",
3
- "version": "0.20.52",
3
+ "version": "0.20.54",
4
4
  "description": "KingKont \u00b7 Chatium \u2014 \u043d\u043e\u0434-\u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u0441\u0446\u0435\u043d \u0441 AI-\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0435\u0439 (\u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438/\u0432\u0438\u0434\u0435\u043e/\u0433\u043e\u043b\u043e\u0441/SFX/\u043c\u0443\u0437\u044b\u043a\u0430/\u0442\u0435\u043a\u0441\u0442)",
5
5
  "main": "main.js",
6
6
  "bin": {
@@ -591,23 +591,30 @@ function ensureNodeDescriptionEl(node) {
591
591
  if (!node) return null;
592
592
  const canvasEl = document.getElementById('canvas');
593
593
  if (!canvasEl) return null;
594
- const text = (node.type === 'image' || node.type === 'video') ? (node.description || '').trim() : '';
595
- const sel = `.node-description-outside[data-desc-for="${node.id}"]`;
596
- let el = canvasEl.querySelector(sel);
597
- if (!text) {
598
- // Описания нет — удаляем sidecar.
599
- if (el) el.remove();
594
+ if (node.type !== 'image' && node.type !== 'video') {
595
+ const existing = canvasEl.querySelector(`.node-description-outside[data-desc-for="${node.id}"]`);
596
+ if (existing) existing.remove();
597
+ return null;
598
+ }
599
+ const text = (node.description || '').trim();
600
+ // В view-only-режиме (share-link template) placeholder «Добавить описание»
601
+ // не показываем — он имеет смысл только если юзер может редактировать.
602
+ const canModify = (typeof state !== 'undefined' && state)
603
+ ? (state.cloudCanModify !== false)
604
+ : true;
605
+ const isPlaceholder = !text;
606
+ if (isPlaceholder && !canModify) {
607
+ const existing = canvasEl.querySelector(`.node-description-outside[data-desc-for="${node.id}"]`);
608
+ if (existing) existing.remove();
600
609
  return null;
601
610
  }
611
+ const sel = `.node-description-outside[data-desc-for="${node.id}"]`;
612
+ let el = canvasEl.querySelector(sel);
602
613
  if (!el) {
603
614
  el = document.createElement('div');
604
615
  el.className = 'node-description-outside';
605
616
  el.dataset.descFor = node.id;
606
617
  canvasEl.appendChild(el);
607
- // Дабл-клик по описанию → редактор (юзер: «дабл-клик на описании ноды
608
- // должен открывать его редактирование»). editNodeDescription —
609
- // глобал из board.js. e.stopPropagation — иначе дабл-клик пробрасывался
610
- // в .node и срабатывало fullscreen-просмотр.
611
618
  el.addEventListener('dblclick', e => {
612
619
  e.stopPropagation();
613
620
  e.preventDefault();
@@ -615,8 +622,22 @@ function ensureNodeDescriptionEl(node) {
615
622
  window.editNodeDescription(node);
616
623
  }
617
624
  });
625
+ el.addEventListener('click', e => {
626
+ if (!el.classList.contains('empty')) return;
627
+ e.stopPropagation();
628
+ e.preventDefault();
629
+ if (typeof window.editNodeDescription === 'function') {
630
+ window.editNodeDescription(node);
631
+ }
632
+ });
633
+ }
634
+ if (isPlaceholder) {
635
+ el.classList.add('empty');
636
+ if (el.textContent !== 'Добавить описание') el.textContent = 'Добавить описание';
637
+ } else {
638
+ el.classList.remove('empty');
639
+ if (el.textContent !== text) el.textContent = text;
618
640
  }
619
- if (el.textContent !== text) el.textContent = text;
620
641
  positionNodeDescriptionEl(node, el);
621
642
  return el;
622
643
  }
@@ -658,13 +679,11 @@ function fitNodeHeightToMedia(node, nodeEl, mediaEl) {
658
679
  // Берём актуальную width — node.width если задано, иначе текущий offsetWidth.
659
680
  const w = node.width || nodeEl.offsetWidth;
660
681
  if (!w) return;
661
- // Высота описания (если есть) описание занимает нижнюю часть bbox'а
662
- // ноды (см. positionNodeDescriptionEl). Без этого описание выходило за
682
+ // Высота описания (если есть, включая placeholder «Добавить описание»)
683
+ // занимает нижнюю часть bbox'а ноды. Без этого описание выходило за
663
684
  // пределы node.height и «накладывалось» на соседнюю ноду снизу. Юзер:
664
685
  // «на ноде её описание показывается над нодой которая расположена выше».
665
- const descEl = (node.description || '').trim()
666
- ? document.querySelector(`.node-description-outside[data-desc-for="${node.id}"]`)
667
- : null;
686
+ const descEl = document.querySelector(`.node-description-outside[data-desc-for="${node.id}"]`);
668
687
  const descH = descEl ? descEl.offsetHeight + 4 : 0; // +4 чтобы зрительно отделять
669
688
  const newH = Math.round(w * (natH / natW) + chromeH + descH);
670
689
  // 2px tolerance — не шумим в save/connections при суб-пиксельных колебаниях.
@@ -130,6 +130,25 @@
130
130
  z-index: 2;
131
131
  pointer-events: auto;
132
132
  }
133
+ /* Placeholder: описание отсутствует, подсказка «Добавить описание».
134
+ Юзер: «если описания в ноде нет — показывай блок с описанием с текстом
135
+ 'добавить описание'». Стиль приглушённый, чтобы не отвлекать; клик/
136
+ дабл-клик открывает редактор. В view-only-режиме placeholder не
137
+ показываем (см. ensureNodeDescriptionEl). */
138
+ .node-description-outside.empty {
139
+ color: #6a6a6a;
140
+ font-style: italic;
141
+ border-left-color: #3a3a3a;
142
+ background: #1a1a1a;
143
+ box-shadow: none;
144
+ cursor: pointer;
145
+ transition: color 0.12s, border-left-color 0.12s, background 0.12s;
146
+ }
147
+ .node-description-outside.empty:hover {
148
+ color: #aac8e6;
149
+ border-left-color: #4a6a9a;
150
+ background: #1e1e1e;
151
+ }
133
152
  /* Legacy: оставляем класс для совместимости (вдруг где-то ещё ссылается). */
134
153
  .node .node-description { display: none; }
135
154