kingkont 0.7.37 → 0.7.39

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
@@ -141,8 +141,20 @@
141
141
  scroll-snap-type: x proximity;
142
142
  }
143
143
  .welcome-recent-grid::-webkit-scrollbar { height: 8px; }
144
- .welcome-recent-grid::-webkit-scrollbar-thumb { background: #3a3a3a; border-radius: 4px; }
145
- .welcome-recent-grid::-webkit-scrollbar-thumb:hover { background: #4a4a4a; }
144
+
145
+ /* === Глобальные dark-scrollbars === */
146
+ /* Firefox */
147
+ * { scrollbar-color: #3a3a3a #1a1a1a; scrollbar-width: thin; }
148
+ /* WebKit (Electron/Chrome) — все scroll-области в приложении. */
149
+ ::-webkit-scrollbar { width: 10px; height: 10px; background: #1a1a1a; }
150
+ ::-webkit-scrollbar-track { background: #1a1a1a; }
151
+ ::-webkit-scrollbar-thumb {
152
+ background: #3a3a3a;
153
+ border-radius: 4px;
154
+ border: 2px solid #1a1a1a; /* gap, чтобы thumb «не прилипал» к краям */
155
+ }
156
+ ::-webkit-scrollbar-thumb:hover { background: #4a4a4a; }
157
+ ::-webkit-scrollbar-corner { background: #1a1a1a; }
146
158
  .welcome-card {
147
159
  background: #232323; border: 1px solid #333; border-radius: 8px;
148
160
  overflow: hidden; cursor: pointer; transition: border-color 0.12s, transform 0.12s;
@@ -1025,10 +1037,7 @@
1025
1037
  <div class="sidebar-list" id="episodeList"></div>
1026
1038
  </div>
1027
1039
  <div class="sidebar-footer">
1028
- <span id="balanceInfo" class="balance-info" style="display:none;" title="Баланс KingKont-аккаунта · клик — лог списаний" onclick="window.openTxLog && window.openTxLog()">
1029
- <span class="dot"></span>
1030
- <span id="balanceValue">— credits</span>
1031
- </span>
1040
+ <div id="balancesAll" style="display:flex; flex-direction:column; gap:4px;"></div>
1032
1041
  <span id="jobsInfo" class="jobs-info" style="display:none;"></span>
1033
1042
  <span class="hint">Перетаскивай файлы на холст · @имя для ссылок</span>
1034
1043
  </div>
@@ -2104,32 +2113,35 @@ window.addEventListener('DOMContentLoaded', async () => {
2104
2113
  // red (≤0). Также экспортирована глобально как window.refreshBalance —
2105
2114
  // чтобы settings-окно могло триггерить обновление после login/logout.
2106
2115
  async function refreshBalance() {
2107
- const pill = document.getElementById('balanceInfo');
2108
- const valueEl = document.getElementById('balanceValue');
2109
- if (!pill || !valueEl) return;
2116
+ const wrap = document.getElementById('balancesAll');
2117
+ if (!wrap) return;
2118
+ let data = {};
2110
2119
  try {
2111
- const s = await window.appSettings.get();
2112
- if (!s.useChatium || !s.chatium?.token) {
2113
- pill.style.display = 'none';
2114
- return;
2115
- }
2116
- const r = await fetch('/api/balance');
2117
- if (!r.ok) {
2118
- pill.style.display = '';
2119
- pill.classList.remove('low'); pill.classList.add('empty');
2120
- valueEl.textContent = `— credits (HTTP ${r.status})`;
2121
- return;
2122
- }
2123
- const d = await r.json();
2124
- const balance = Number(d.balance) || 0;
2125
- pill.style.display = '';
2126
- pill.classList.toggle('low', balance > 0 && balance < 100);
2127
- pill.classList.toggle('empty', balance <= 0);
2128
- valueEl.innerHTML = `<b>${balance.toLocaleString('ru-RU')}</b>&nbsp;credits`;
2129
- } catch (e) {
2130
- pill.style.display = '';
2131
- pill.classList.remove('low'); pill.classList.add('empty');
2132
- valueEl.textContent = `— credits (offline)`;
2120
+ const r = await fetch('/api/balance/all');
2121
+ if (r.ok) data = await r.json();
2122
+ } catch {}
2123
+ wrap.innerHTML = '';
2124
+ // Один pill на провайдер. Если у провайдера нет данных (выключен или
2125
+ // API не дал баланс) — pill не рендерим.
2126
+ const pills = [
2127
+ { key: 'kingkont', label: 'KingKont', onClick: () => window.openTxLog?.(), low: 100, fmt: (a) => `<b>${a.toLocaleString('ru-RU')}</b>&nbsp;credits` },
2128
+ { key: 'openrouter', label: 'OpenRouter', low: 0.5, fmt: (a) => `<b>$${a.toFixed(2)}</b>` },
2129
+ { key: 'elevenlabs', label: 'ElevenLabs', low: 1000, fmt: (a) => `<b>${a.toLocaleString('ru-RU')}</b>&nbsp;chars` },
2130
+ ];
2131
+ for (const p of pills) {
2132
+ const d = data[p.key];
2133
+ if (!d || typeof d.amount !== 'number') continue;
2134
+ const pill = document.createElement('span');
2135
+ pill.className = 'balance-info';
2136
+ pill.title = `Баланс ${p.label}` + (p.onClick ? ' · клик — лог списаний' : '');
2137
+ if (d.amount > 0 && d.amount < (p.low || 0)) pill.classList.add('low');
2138
+ if (d.amount <= 0) pill.classList.add('empty');
2139
+ pill.innerHTML = `<span class="dot"></span><span style="color:#888;font-size:10px;margin-right:4px;">${p.label}</span><span>${p.fmt(d.amount)}</span>`;
2140
+ if (p.onClick) {
2141
+ pill.style.cursor = 'pointer';
2142
+ pill.addEventListener('click', p.onClick);
2143
+ }
2144
+ wrap.appendChild(pill);
2133
2145
  }
2134
2146
  }
2135
2147
  window.refreshBalance = refreshBalance;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kingkont",
3
- "version": "0.7.37",
3
+ "version": "0.7.39",
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
@@ -657,7 +657,7 @@ async function handleTransactions(req, res) {
657
657
  }
658
658
  }
659
659
 
660
- // ---------- /api/balance (только Chatium) ----------
660
+ // ---------- /api/balance (KingKont — legacy single-balance endpoint) ----------
661
661
  async function handleBalance(req, res) {
662
662
  const s = getSettings();
663
663
  if (!s.useChatium || !s.chatium?.token || !s.chatium?.base) {
@@ -682,6 +682,54 @@ async function handleBalance(req, res) {
682
682
  }
683
683
  }
684
684
 
685
+ // ---------- /api/balance/all (агрегированные балансы всех включённых провайдеров) ----------
686
+ async function handleBalanceAll(req, res) {
687
+ const s = getSettings();
688
+ const out = {};
689
+ // KingKont — credits.
690
+ if (s.useChatium && s.chatium?.token && s.chatium?.base) {
691
+ try {
692
+ const url = s.chatium.base.replace(/\/$/, '') + CHATIUM_PATHS.balance;
693
+ const r = await fetch(url, { headers: { 'Authorization': `Bearer ${s.chatium.token}` } });
694
+ const d = await r.json().catch(() => ({}));
695
+ if (r.ok && typeof d.balance === 'number') out.kingkont = { unit: 'credits', amount: d.balance };
696
+ } catch {}
697
+ }
698
+ // ElevenLabs — characters_left = limit - used.
699
+ if (s.useElevenlabs && process.env.ELEVENLABS_API_KEY) {
700
+ try {
701
+ const r = await fetch(`${ELEVEN_BASE}/v1/user/subscription`, {
702
+ headers: { 'xi-api-key': process.env.ELEVENLABS_API_KEY },
703
+ });
704
+ const d = await r.json().catch(() => ({}));
705
+ if (r.ok && typeof d.character_count === 'number' && typeof d.character_limit === 'number') {
706
+ out.elevenlabs = {
707
+ unit: 'chars',
708
+ amount: Math.max(0, d.character_limit - d.character_count),
709
+ limit: d.character_limit,
710
+ };
711
+ }
712
+ } catch {}
713
+ }
714
+ // OpenRouter — credits в USD.
715
+ if (s.useOpenrouter && process.env.OPENROUTER_API_KEY) {
716
+ try {
717
+ const r = await fetch('https://openrouter.ai/api/v1/credits', {
718
+ headers: { 'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}` },
719
+ });
720
+ const d = await r.json().catch(() => ({}));
721
+ if (r.ok && d.data && typeof d.data.total_credits === 'number') {
722
+ out.openrouter = {
723
+ unit: 'usd',
724
+ amount: Math.max(0, (d.data.total_credits || 0) - (d.data.total_usage || 0)),
725
+ };
726
+ }
727
+ } catch {}
728
+ }
729
+ // KIE — нет публичного balance-endpoint в их API; пропускаем.
730
+ send(res, 200, out);
731
+ }
732
+
685
733
  // ---------- /api/sfx (Chatium ИЛИ ElevenLabs Sound Effects) ----------
686
734
  async function handleSfx(req, res) {
687
735
  const { text, durationSeconds, promptInfluence = 0.3 } = await readJson(req);
@@ -885,6 +933,7 @@ const server = createServer(async (req, res) => {
885
933
  if (req.method === 'POST' && url.pathname === '/api/music') return handleMusic(req, res);
886
934
  if (req.method === 'POST' && url.pathname === '/api/text') return handleText(req, res);
887
935
  if (req.method === 'GET' && url.pathname === '/api/balance') return handleBalance(req, res);
936
+ if (req.method === 'GET' && url.pathname === '/api/balance/all') return handleBalanceAll(req, res);
888
937
  if (req.method === 'GET' && url.pathname === '/api/transactions') return handleTransactions(req, res);
889
938
  if (req.method === 'GET') return serveStatic(res, url);
890
939
  send(res, 404, 'not found');
package/settings.html CHANGED
@@ -105,6 +105,11 @@
105
105
 
106
106
  .saved { color: var(--ok); font-size: 12px; opacity: 0; transition: opacity 0.3s; }
107
107
  .saved.show { opacity: 1; }
108
+ /* Dark scrollbars — same palette как в основном окне. */
109
+ * { scrollbar-color: #3a3a3a var(--bg); scrollbar-width: thin; }
110
+ ::-webkit-scrollbar { width: 10px; height: 10px; background: var(--bg); }
111
+ ::-webkit-scrollbar-thumb { background: #3a3a3a; border-radius: 4px; border: 2px solid var(--bg); }
112
+ ::-webkit-scrollbar-thumb:hover { background: #4a4a4a; }
108
113
  </style>
109
114
  </head>
110
115
  <body>
package/updates.html CHANGED
@@ -74,6 +74,11 @@
74
74
  0%, 100% { box-shadow: 0 0 0 0 rgba(22,163,74,0.55); }
75
75
  50% { box-shadow: 0 0 0 8px rgba(22,163,74,0); }
76
76
  }
77
+ /* Dark scrollbars — same palette как в основном окне. */
78
+ * { scrollbar-color: #3a3a3a var(--bg); scrollbar-width: thin; }
79
+ ::-webkit-scrollbar { width: 10px; height: 10px; background: var(--bg); }
80
+ ::-webkit-scrollbar-thumb { background: #3a3a3a; border-radius: 4px; border: 2px solid var(--bg); }
81
+ ::-webkit-scrollbar-thumb:hover { background: #4a4a4a; }
77
82
  </style>
78
83
  </head>
79
84
  <body>