kingkont 0.7.20 → 0.7.22

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
@@ -1343,6 +1343,17 @@
1343
1343
  <button class="seg" data-img-model="sdxl-lightning" type="button" title="Очень быстрая (3-5с)">⚡ SDXL Lightning</button>
1344
1344
  </div>
1345
1345
  </label>
1346
+ <label id="imageOptionsRow">Соотношение сторон
1347
+ <div class="seg-control" id="imageAspectCtl">
1348
+ <button class="seg active" data-img-asp="1:1" type="button">1:1</button>
1349
+ <button class="seg" data-img-asp="16:9" type="button">16:9</button>
1350
+ <button class="seg" data-img-asp="9:16" type="button">9:16</button>
1351
+ <button class="seg" data-img-asp="3:2" type="button">3:2</button>
1352
+ <button class="seg" data-img-asp="2:3" type="button">2:3</button>
1353
+ <button class="seg" data-img-asp="4:3" type="button" title="Не поддерживается Grok">4:3</button>
1354
+ <button class="seg" data-img-asp="3:4" type="button" title="Не поддерживается Grok">3:4</button>
1355
+ </div>
1356
+ </label>
1346
1357
  <label id="videoModelRow" style="display: none;">Модель для видео
1347
1358
  <div class="seg-control">
1348
1359
  <button class="seg active" data-vid-model="seedance-2" type="button" title="ByteDance Seedance 2 — основная">Seedance 2</button>
@@ -1778,6 +1789,7 @@ const state = {
1778
1789
  currentBoard: null, // { kind, name, key, handle, metadata, urls }
1779
1790
  genKind: 'image',
1780
1791
  imageModel: 'nano-banana-2', // 'nano-banana-2' | 'grok' | ...
1792
+ imageAspect: localStorage.getItem('imageAspect') || '1:1', // 1:1 / 16:9 / 9:16 / 3:2 / 2:3 / 4:3 / 3:4
1781
1793
  videoModel: localStorage.getItem('videoModel') || 'seedance-2', // 'seedance-2' | 'kling-o1' | 'kling-3.0' | ...
1782
1794
  ttsModel: localStorage.getItem('ttsModel') || 'qwen/qwen3-tts', // qwen/elevenlabs/v3/minimax/speech-02-hd/gemini
1783
1795
  videoDuration: +(localStorage.getItem('videoDuration') || 5),
@@ -3866,6 +3878,8 @@ async function openGenerateForRef(fromNode, clientX, clientY, forceKind) {
3866
3878
  document.querySelectorAll('#genModal [data-kind]').forEach(b =>
3867
3879
  b.classList.toggle('active', b.dataset.kind === forceKind));
3868
3880
  $('imageModelRow').style.display = forceKind === 'image' ? '' : 'none';
3881
+
3882
+ $('imageOptionsRow').style.display = forceKind === 'image' ? '' : 'none';
3869
3883
  $('videoOptionsRow').style.display = forceKind === 'video' ? '' : 'none';
3870
3884
 
3871
3885
  $('videoModelRow').style.display = forceKind === 'video' ? '' : 'none';
@@ -5120,6 +5134,8 @@ async function regenerateNode(node) {
5120
5134
  document.querySelectorAll('#genModal [data-kind]').forEach(b =>
5121
5135
  b.classList.toggle('active', b.dataset.kind === state.genKind));
5122
5136
  $('imageModelRow').style.display = state.genKind === 'image' ? '' : 'none';
5137
+
5138
+ $('imageOptionsRow').style.display = state.genKind === 'image' ? '' : 'none';
5123
5139
  $('videoOptionsRow').style.display = state.genKind === 'video' ? '' : 'none';
5124
5140
 
5125
5141
  $('videoModelRow').style.display = state.genKind === 'video' ? '' : 'none';
@@ -5132,6 +5148,10 @@ async function regenerateNode(node) {
5132
5148
  document.querySelectorAll('#genModal [data-img-model]').forEach(b =>
5133
5149
  b.classList.toggle('active', b.dataset.imgModel === g.modelKey));
5134
5150
  }
5151
+ if (state.genKind === 'image') {
5152
+ if (g.aspectRatio) state.imageAspect = g.aspectRatio;
5153
+ syncImageAspectActive();
5154
+ }
5135
5155
  // Видео: подставляем сохранённые duration/resolution/aspect для regenerate
5136
5156
  if (state.genKind === 'video') {
5137
5157
  if (g.duration) state.videoDuration = g.duration;
@@ -6074,6 +6094,8 @@ function openPhraseFor(charInfo) {
6074
6094
  document.querySelectorAll('#genModal [data-kind]').forEach(b =>
6075
6095
  b.classList.toggle('active', b.dataset.kind === 'audio'));
6076
6096
  $('imageModelRow').style.display = 'none';
6097
+
6098
+ $('imageOptionsRow').style.display = 'none';
6077
6099
  $('videoOptionsRow').style.display = 'none';
6078
6100
 
6079
6101
  $('videoModelRow').style.display = 'none';
@@ -6257,6 +6279,8 @@ async function openGenModal(kind) {
6257
6279
  b.classList.toggle('active', b.dataset.kind === kind));
6258
6280
  // Видимость рядов под текущий kind
6259
6281
  $('imageModelRow').style.display = kind === 'image' ? '' : 'none';
6282
+
6283
+ $('imageOptionsRow').style.display = kind === 'image' ? '' : 'none';
6260
6284
  $('videoOptionsRow').style.display = kind === 'video' ? '' : 'none';
6261
6285
 
6262
6286
  $('videoModelRow').style.display = kind === 'video' ? '' : 'none';
@@ -6679,6 +6703,8 @@ document.querySelectorAll('#genModal [data-kind]').forEach(b => {
6679
6703
  b.classList.add('active');
6680
6704
  state.genKind = b.dataset.kind;
6681
6705
  $('imageModelRow').style.display = state.genKind === 'image' ? '' : 'none';
6706
+
6707
+ $('imageOptionsRow').style.display = state.genKind === 'image' ? '' : 'none';
6682
6708
  $('videoOptionsRow').style.display = state.genKind === 'video' ? '' : 'none';
6683
6709
 
6684
6710
  $('videoModelRow').style.display = state.genKind === 'video' ? '' : 'none';
@@ -6953,6 +6979,21 @@ document.querySelectorAll('#genModal [data-vid-model]').forEach(b => {
6953
6979
  localStorage.setItem('videoModel', state.videoModel);
6954
6980
  });
6955
6981
  });
6982
+ // Переключатель aspect ratio для image
6983
+ document.querySelectorAll('#genModal [data-img-asp]').forEach(b => {
6984
+ b.addEventListener('click', () => {
6985
+ document.querySelectorAll('#genModal [data-img-asp]').forEach(x => x.classList.remove('active'));
6986
+ b.classList.add('active');
6987
+ state.imageAspect = b.dataset.imgAsp;
6988
+ localStorage.setItem('imageAspect', state.imageAspect);
6989
+ });
6990
+ });
6991
+ function syncImageAspectActive() {
6992
+ document.querySelectorAll('#genModal [data-img-asp]').forEach(b =>
6993
+ b.classList.toggle('active', b.dataset.imgAsp === state.imageAspect));
6994
+ }
6995
+ syncImageAspectActive();
6996
+
6956
6997
  // Переключатель модели TTS
6957
6998
  document.querySelectorAll('#genModal [data-tts-model]').forEach(b => {
6958
6999
  b.addEventListener('click', () => {
@@ -7229,12 +7270,15 @@ $('genSubmit').addEventListener('click', async () => {
7229
7270
  // Связь с родительской нодой (выведена через "вытягивание"). Хранится
7230
7271
  // даже если sourceRef.use=false в state — сохраняем структурную связь.
7231
7272
  ...(savedSourceRef ? { sourceRef: savedSourceRef } : {}),
7232
- // Параметры видео сохраняем — при regenerate используем те же (предсказуемость).
7273
+ // Параметры видео/картинки сохраняем — при regenerate используем те же.
7233
7274
  ...(kind === 'video' ? {
7234
7275
  duration: state.videoDuration,
7235
7276
  resolution: state.videoResolution,
7236
7277
  aspectRatio: state.videoAspect,
7237
7278
  } : {}),
7279
+ ...(kind === 'image' ? {
7280
+ aspectRatio: state.imageAspect,
7281
+ } : {}),
7238
7282
  },
7239
7283
  };
7240
7284
  if (saveOnly) node.status = 'draft';
@@ -7384,6 +7428,10 @@ async function startGenerationJob(node, kind, prompt, mediaRefs, boardHandle, bK
7384
7428
  submitBody.duration = node.generated?.duration ?? state.videoDuration;
7385
7429
  submitBody.resolution = node.generated?.resolution ?? state.videoResolution;
7386
7430
  submitBody.aspectRatio = node.generated?.aspectRatio ?? state.videoAspect;
7431
+ } else if (kind === 'image') {
7432
+ // Grok-imagine требует aspect_ratio из {2:3, 3:2, 1:1, 9:16, 16:9}.
7433
+ // Остальные модели (nano-banana-2, seedream и др.) тоже принимают.
7434
+ submitBody.aspectRatio = node.generated?.aspectRatio ?? state.imageAspect;
7387
7435
  }
7388
7436
  logJob(node.id, `POST /api/generate body: ${logSafe(submitBody)}`);
7389
7437
  logJob(node.id, `POST /api/generate (image_input=${imageInputs.length}, video_input=${videoInputs.length}, model=${modelKey})`);
@@ -7945,6 +7993,8 @@ async function openGenAudioForTimeline(charInfo, track, time) {
7945
7993
  document.querySelectorAll('#genModal [data-kind]').forEach(b =>
7946
7994
  b.classList.toggle('active', b.dataset.kind === 'audio'));
7947
7995
  $('imageModelRow').style.display = 'none';
7996
+
7997
+ $('imageOptionsRow').style.display = 'none';
7948
7998
  $('videoOptionsRow').style.display = 'none';
7949
7999
 
7950
8000
  $('videoModelRow').style.display = 'none';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kingkont",
3
- "version": "0.7.20",
3
+ "version": "0.7.22",
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": {
package/server.js CHANGED
@@ -100,7 +100,7 @@ async function chatiumStart(s, kind, body) {
100
100
  let data; try { data = JSON.parse(text); } catch { data = { raw: text }; }
101
101
  if (!r.ok) {
102
102
  const reason = data?.reason || data?.error || data?.raw || `HTTP ${r.status}`;
103
- throw new Error(`Chatium /api/${kind} HTTP ${r.status}: ${String(reason).slice(0, 300)}`);
103
+ throw new Error(`Chatium /api/${kind} HTTP ${r.status}: ${String(reason).slice(0, 800)}`);
104
104
  }
105
105
  if (!data.taskId) throw new Error(`Chatium /api/${kind} вернул без taskId: ${text.slice(0, 200)}`);
106
106
  return data.taskId;
package/updates.html CHANGED
@@ -89,22 +89,22 @@
89
89
  </div>
90
90
  </div>
91
91
  <div id="updateBlock" style="display:none;">
92
- <div style="display:flex; gap:8px; align-items:center; margin-top:6px;">
92
+ <div style="display:flex; gap:8px; align-items:center; margin-top:6px; flex-wrap:wrap;">
93
93
  <button class="primary" id="installBtn" style="padding:8px 16px;">⬇ Установить и перезапустить</button>
94
+ <button class="primary" id="relaunchBtn" style="padding:8px 16px; display:none; background: var(--ok); border-color: var(--ok);">↻ Перезапустить сейчас</button>
94
95
  <span style="color:#888; font-size:11px;">или вручную:</span>
95
96
  </div>
96
97
  <div class="install-cmd">
97
98
  <code id="installCmd">npm i -g kingkont@latest</code>
98
99
  <button class="copy" id="copyBtn">Скопировать</button>
99
100
  </div>
100
- <pre id="installLog" style="display:none; max-height:160px; overflow-y:auto; margin-top:10px;
101
+ <div id="installDone" style="display:none; color: var(--ok); margin-top: 8px; font-size: 12px;">
102
+ ✓ Установлено. Кнопка «Перезапустить сейчас» — выше.
103
+ </div>
104
+ <pre id="installLog" style="display:none; max-height:200px; overflow-y:auto; margin-top:10px;
101
105
  font-family: ui-monospace, monospace; font-size: 11px; line-height: 1.4;
102
106
  background: #0e0e0e; border: 1px solid #2a2a2a; border-radius: 4px;
103
107
  padding: 8px 10px; color: #aaa; white-space: pre-wrap; word-break: break-all;"></pre>
104
- <div id="installDone" style="display:none; color: var(--ok); margin-top: 10px; font-size: 12px;">
105
- ✓ Установлено! Можно перезапустить:
106
- <button class="primary" id="relaunchBtn" style="padding:5px 12px; font-size:12px; margin-left:6px;">Перезапустить</button>
107
- </div>
108
108
  </div>
109
109
  <div id="upToDate" style="display:none; color: var(--ok); font-size: 12px; margin-top: 6px;">
110
110
  ✓ У вас последняя версия.
@@ -185,7 +185,8 @@ $('installBtn').addEventListener('click', async () => {
185
185
  try {
186
186
  await window.appUpdates.install(target);
187
187
  done.style.display = '';
188
- installBtn.textContent = '✓ Установлено';
188
+ installBtn.style.display = 'none';
189
+ $('relaunchBtn').style.display = '';
189
190
  } catch (e) {
190
191
  log.style.color = '#f88';
191
192
  log.textContent += `\n[error] ${e?.message || String(e)}`;