kingkont 0.9.0 → 0.9.1

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
@@ -712,6 +712,18 @@ async function fetchBalances(s) {
712
712
  }
713
713
  } catch {}
714
714
  }
715
+ if (s.useKie && process.env.KIE_API_KEY) {
716
+ try {
717
+ // KIE: GET /api/v1/chat/credit → { code: 200, msg: 'success', data: <number> }
718
+ const r = await fetch(`${KIE_BASE}/api/v1/chat/credit`, {
719
+ headers: { 'Authorization': `Bearer ${process.env.KIE_API_KEY}` },
720
+ });
721
+ const d = await r.json().catch(() => ({}));
722
+ if (r.ok && d.code === 200 && typeof d.data === 'number') {
723
+ out.kie = { unit: 'credits', amount: d.data };
724
+ }
725
+ } catch {}
726
+ }
715
727
  return out;
716
728
  }
717
729
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kingkont",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "KingKont · Chatium — нод-редактор сцен с AI-генерацией (картинки/видео/голос/SFX/музыка/текст)",
5
5
  "main": "main.js",
6
6
  "bin": {
package/renderer/board.js CHANGED
@@ -174,6 +174,7 @@ async function refreshBalance() {
174
174
  // API не дал баланс) — pill не рендерим.
175
175
  const pills = [
176
176
  { key: 'kingkont', label: 'KingKont', onClick: () => window.openTxLog?.(), low: 100, fmt: (a) => `<b>${a.toLocaleString('ru-RU')}</b>&nbsp;credits` },
177
+ { key: 'kie', label: 'KIE', low: 5, fmt: (a) => `<b>${a.toLocaleString('ru-RU', { maximumFractionDigits: 2 })}</b>&nbsp;credits` },
177
178
  { key: 'openrouter', label: 'OpenRouter', low: 0.5, fmt: (a) => `<b>$${a.toFixed(2)}</b>` },
178
179
  { key: 'elevenlabs', label: 'ElevenLabs', low: 1000, fmt: (a) => `<b>${a.toLocaleString('ru-RU')}</b>&nbsp;chars` },
179
180
  ];
@@ -239,25 +239,33 @@ async function renderNodeBody(node, body) {
239
239
  if (node.status === 'draft') {
240
240
  const wrap = document.createElement('div');
241
241
  wrap.className = 'gen-pending';
242
- const ic = document.createElement('div');
243
- ic.style.cssText = 'font-size:36px; opacity:0.7;';
244
- ic.textContent = node.type === 'audio' ? '🎙'
245
- : node.type === 'video' ? '🎬'
246
- : node.type === 'image' ? '🖼' : '📝';
247
- const st = document.createElement('div');
248
- st.className = 'state-text';
249
- st.textContent = 'Черновик: подведи входы и запусти';
250
- const pp = document.createElement('div');
251
- pp.className = 'prompt-preview';
252
- pp.textContent = node.generated?.rawPrompt || node.generated?.prompt || '';
253
- wrap.append(ic, st, pp);
242
+ // Для image — кнопка наверху, без декоративных 🖼/«Черновик» (юзер
243
+ // и так видит ноду пустой). Для audio/video оставляем — у них
244
+ // меньше визуального контекста и иконка помогает идентифицировать тип.
245
+ const isImage = node.type === 'image';
254
246
  const runBtn = document.createElement('button');
255
247
  runBtn.textContent = '▶ Запустить генерацию';
256
248
  runBtn.className = 'primary';
257
- runBtn.style.cssText = 'margin-top:8px; font-size:12px; padding:6px 10px;';
249
+ runBtn.style.cssText = isImage
250
+ ? 'font-size:12px; padding:6px 10px; align-self:stretch;'
251
+ : 'margin-top:8px; font-size:12px; padding:6px 10px;';
258
252
  runBtn.addEventListener('mousedown', e => e.stopPropagation());
259
253
  runBtn.addEventListener('click', e => { e.stopPropagation(); regenerateNode(node); });
260
- wrap.appendChild(runBtn);
254
+ const pp = document.createElement('div');
255
+ pp.className = 'prompt-preview';
256
+ pp.textContent = node.generated?.rawPrompt || node.generated?.prompt || '';
257
+ if (isImage) {
258
+ wrap.append(runBtn, pp);
259
+ } else {
260
+ const ic = document.createElement('div');
261
+ ic.style.cssText = 'font-size:36px; opacity:0.7;';
262
+ ic.textContent = node.type === 'audio' ? '🎙'
263
+ : node.type === 'video' ? '🎬' : '📝';
264
+ const st = document.createElement('div');
265
+ st.className = 'state-text';
266
+ st.textContent = 'Черновик: подведи входы и запусти';
267
+ wrap.append(ic, st, pp, runBtn);
268
+ }
261
269
  body.appendChild(wrap);
262
270
  return;
263
271
  }
@@ -520,21 +528,21 @@ async function renderNodeBody(node, body) {
520
528
  body.appendChild(playRow);
521
529
  }
522
530
  } else if (node.type === 'image' && !node.file && (node.generated?.rawPrompt || node.generated?.prompt)) {
523
- // Fallback: image-нода с промптом, но без файла (ещё не генерилась
524
- // и status != 'draft'). Показываем preview-блок чтобы юзер видел
525
- // что собирается генерироваться.
531
+ // Image-нода с промптом, но без файла (ещё не генерилась).
532
+ // Кнопка Запустить сверху (без декоративной 🖼-иконки и
533
+ // подписи «Не сгенерировано» — они только занимают место).
526
534
  const wrap = document.createElement('div');
527
535
  wrap.className = 'gen-pending';
528
- const ic = document.createElement('div');
529
- ic.style.cssText = 'font-size:36px; opacity:0.7;';
530
- ic.textContent = '🖼';
531
- const st = document.createElement('div');
532
- st.className = 'state-text';
533
- st.textContent = 'Не сгенерировано открой для запуска';
536
+ const runBtn = document.createElement('button');
537
+ runBtn.textContent = ' Запустить генерацию';
538
+ runBtn.className = 'primary';
539
+ runBtn.style.cssText = 'font-size:12px; padding:6px 10px; align-self:stretch;';
540
+ runBtn.addEventListener('mousedown', e => e.stopPropagation());
541
+ runBtn.addEventListener('click', e => { e.stopPropagation(); regenerateNode(node); });
534
542
  const pp = document.createElement('div');
535
543
  pp.className = 'prompt-preview';
536
544
  pp.textContent = node.generated?.rawPrompt || node.generated?.prompt || '';
537
- wrap.append(ic, st, pp);
545
+ wrap.append(runBtn, pp);
538
546
  body.appendChild(wrap);
539
547
  }
540
548
  }