kingkont 0.7.23 → 0.7.25
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 +56 -14
- package/package.json +1 -1
- package/updates.html +13 -2
package/index.html
CHANGED
|
@@ -6251,18 +6251,23 @@ document.addEventListener('keydown', e => {
|
|
|
6251
6251
|
async function ensureApiKey(forKind) {
|
|
6252
6252
|
if (!window.appSettings) return true; // веб-режим — настройки не доступны
|
|
6253
6253
|
const s = await window.appSettings.get();
|
|
6254
|
-
|
|
6255
|
-
|
|
6254
|
+
// Если KingKont подключён и активен — он покроет любой kind, прямые
|
|
6255
|
+
// ключи не нужны.
|
|
6256
|
+
const hasChatium = !!(s.useChatium === true && s.chatium?.token && s.chatium?.base);
|
|
6257
|
+
if (hasChatium) return true;
|
|
6258
|
+
// Иначе нужен прямой ключ для соответствующего провайдера.
|
|
6259
|
+
if (forKind === 'audio' && (!s.useElevenlabs || !s.elevenKey)) {
|
|
6260
|
+
alert('Войдите в KingKont или включите ElevenLabs (нужен API-ключ).');
|
|
6256
6261
|
openSettings('eleven');
|
|
6257
6262
|
return false;
|
|
6258
6263
|
}
|
|
6259
|
-
if ((forKind === 'image' || forKind === 'video') && !s.kieKey) {
|
|
6260
|
-
alert('
|
|
6264
|
+
if ((forKind === 'image' || forKind === 'video') && (!s.useKie || !s.kieKey)) {
|
|
6265
|
+
alert('Войдите в KingKont или включите KIE (нужен API-ключ).');
|
|
6261
6266
|
openSettings('kie');
|
|
6262
6267
|
return false;
|
|
6263
6268
|
}
|
|
6264
|
-
if (forKind === 'text' && !s.openrouterKey) {
|
|
6265
|
-
alert('
|
|
6269
|
+
if (forKind === 'text' && (!s.useOpenrouter || !s.openrouterKey)) {
|
|
6270
|
+
alert('Войдите в KingKont или включите OpenRouter (нужен API-ключ).');
|
|
6266
6271
|
openSettings('openrouter');
|
|
6267
6272
|
return false;
|
|
6268
6273
|
}
|
|
@@ -6920,14 +6925,38 @@ let voicesLoaded = false;
|
|
|
6920
6925
|
const VOICES_CACHE_KEY = 'voicesCache';
|
|
6921
6926
|
const VOICES_CACHE_TTL = 24 * 60 * 60 * 1000;
|
|
6922
6927
|
|
|
6928
|
+
// ElevenLabs v3 в KingKont/Chatium ждёт voice СТРОГО из этого enum'а.
|
|
6929
|
+
// (см. server-side validation: ValidationError: voice: invalid enum value).
|
|
6930
|
+
// Это не воссы из ElevenLabs API, а hardcoded набор поддерживаемых @start/sdk.
|
|
6931
|
+
const ELEVENLABS_V3_VOICES = [
|
|
6932
|
+
'Rachel', 'Drew', 'Clyde', 'Paul', 'Aria', 'Domi', 'Dave', 'Roger',
|
|
6933
|
+
'Fin', 'Sarah', 'James', 'Jane', 'Juniper', 'Arabella', 'Hope',
|
|
6934
|
+
'Bradford', 'Reginald', 'Gaming', 'Austin', 'Kuon', 'Blondie',
|
|
6935
|
+
'Priyanka', 'Alexandra', 'Monika', 'Mark', 'Grimblewood',
|
|
6936
|
+
];
|
|
6937
|
+
|
|
6923
6938
|
function populateVoicesSelect(voices) {
|
|
6924
6939
|
const select = $('genVoice');
|
|
6925
6940
|
select.innerHTML = '';
|
|
6941
|
+
// Если активна ElevenLabs v3 (через KingKont) — показываем только enum.
|
|
6942
|
+
if (state.ttsModel === 'elevenlabs/v3') {
|
|
6943
|
+
for (const name of ELEVENLABS_V3_VOICES) {
|
|
6944
|
+
const opt = document.createElement('option');
|
|
6945
|
+
opt.value = name; // value = name (для KingKont)
|
|
6946
|
+
opt.dataset.voiceName = name;
|
|
6947
|
+
opt.textContent = name;
|
|
6948
|
+
select.appendChild(opt);
|
|
6949
|
+
}
|
|
6950
|
+
const last = localStorage.getItem('lastElevenV3Voice');
|
|
6951
|
+
if (last && ELEVENLABS_V3_VOICES.includes(last)) select.value = last;
|
|
6952
|
+
return;
|
|
6953
|
+
}
|
|
6954
|
+
// Иначе — список из ElevenLabs API (для прямого ElevenLabs, не KingKont).
|
|
6926
6955
|
voices.sort((a, b) => a.name.localeCompare(b.name));
|
|
6927
6956
|
for (const v of voices) {
|
|
6928
6957
|
const opt = document.createElement('option');
|
|
6929
6958
|
opt.value = v.id;
|
|
6930
|
-
opt.dataset.voiceName = v.name;
|
|
6959
|
+
opt.dataset.voiceName = v.name;
|
|
6931
6960
|
const lbl = v.labels || {};
|
|
6932
6961
|
const tags = [lbl.gender, lbl.language || lbl.accent, lbl.age].filter(Boolean).join(' / ');
|
|
6933
6962
|
opt.textContent = tags ? `${v.name} — ${tags}` : v.name;
|
|
@@ -7001,17 +7030,21 @@ document.querySelectorAll('#genModal [data-tts-model]').forEach(b => {
|
|
|
7001
7030
|
b.classList.add('active');
|
|
7002
7031
|
state.ttsModel = b.dataset.ttsModel;
|
|
7003
7032
|
localStorage.setItem('ttsModel', state.ttsModel);
|
|
7004
|
-
|
|
7005
|
-
const showVoice = state.ttsModel === 'elevenlabs/v3';
|
|
7006
|
-
$('voiceRow').style.display = showVoice ? '' : 'none';
|
|
7033
|
+
syncTtsVoiceList();
|
|
7007
7034
|
});
|
|
7008
7035
|
});
|
|
7036
|
+
function syncTtsVoiceList() {
|
|
7037
|
+
const showVoice = state.ttsModel === 'elevenlabs/v3';
|
|
7038
|
+
$('voiceRow').style.display = showVoice ? '' : 'none';
|
|
7039
|
+
if (showVoice) {
|
|
7040
|
+
// Перерисовываем select под elevenlabs/v3 enum (hardcoded 26 имён).
|
|
7041
|
+
populateVoicesSelect([]);
|
|
7042
|
+
}
|
|
7043
|
+
}
|
|
7009
7044
|
function syncTtsModelActive() {
|
|
7010
7045
|
document.querySelectorAll('#genModal [data-tts-model]').forEach(b =>
|
|
7011
7046
|
b.classList.toggle('active', b.dataset.ttsModel === state.ttsModel));
|
|
7012
|
-
|
|
7013
|
-
const showVoice = state.ttsModel === 'elevenlabs/v3';
|
|
7014
|
-
$('voiceRow').style.display = showVoice ? '' : 'none';
|
|
7047
|
+
syncTtsVoiceList();
|
|
7015
7048
|
}
|
|
7016
7049
|
// Подсветить активную video-модель при открытии modal'а
|
|
7017
7050
|
function syncVideoModelActive() {
|
|
@@ -7107,7 +7140,16 @@ $('genSubmit').addEventListener('click', async () => {
|
|
|
7107
7140
|
if (kind === 'audio') {
|
|
7108
7141
|
const voiceId = $('genVoice').value || 'JBFqnCBsd6RMkjVDRZzb';
|
|
7109
7142
|
const voiceName = $('genVoice').selectedOptions[0]?.textContent || '';
|
|
7110
|
-
if (voiceId)
|
|
7143
|
+
if (voiceId) {
|
|
7144
|
+
// Для ElevenLabs v3 (через KingKont) value === name из enum.
|
|
7145
|
+
// Сохраняем в отдельный ключ, иначе при переключении модели
|
|
7146
|
+
// подставится UUID-id и select не выберет его.
|
|
7147
|
+
if (state.ttsModel === 'elevenlabs/v3') {
|
|
7148
|
+
localStorage.setItem('lastElevenV3Voice', voiceId);
|
|
7149
|
+
} else {
|
|
7150
|
+
localStorage.setItem('lastVoiceId', voiceId);
|
|
7151
|
+
}
|
|
7152
|
+
}
|
|
7111
7153
|
// Резолвим @-mentions ДО добавления tone-prefix: text-ноды инлайнятся
|
|
7112
7154
|
// в их .md-содержимое, иначе ElevenLabs получит «[@name]» → пусто после
|
|
7113
7155
|
// strip-spec-tags и упадёт «empty text».
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kingkont",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.25",
|
|
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/updates.html
CHANGED
|
@@ -70,6 +70,10 @@
|
|
|
70
70
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
71
71
|
|
|
72
72
|
.error { color: #ef4444; font-size: 12px; margin-top: 12px; }
|
|
73
|
+
@keyframes pulse {
|
|
74
|
+
0%, 100% { box-shadow: 0 0 0 0 rgba(22,163,74,0.55); }
|
|
75
|
+
50% { box-shadow: 0 0 0 8px rgba(22,163,74,0); }
|
|
76
|
+
}
|
|
73
77
|
</style>
|
|
74
78
|
</head>
|
|
75
79
|
<body>
|
|
@@ -89,9 +93,10 @@
|
|
|
89
93
|
</div>
|
|
90
94
|
</div>
|
|
91
95
|
<div id="updateBlock" style="display:none;">
|
|
92
|
-
<div style="display:flex; gap:8px; align-items:center; margin-top:6px; flex-wrap:wrap;
|
|
96
|
+
<div style="display:flex; gap:8px; align-items:center; margin-top:6px; flex-wrap:wrap;
|
|
97
|
+
position: sticky; top: 0; background: var(--card); z-index: 5; padding: 6px 0;">
|
|
93
98
|
<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>
|
|
99
|
+
<button class="primary" id="relaunchBtn" style="padding:8px 16px; display:none; background: var(--ok); border-color: var(--ok); animation: pulse 1.5s ease-in-out infinite;">↻ Перезапустить сейчас</button>
|
|
95
100
|
<span style="color:#888; font-size:11px;">или вручную:</span>
|
|
96
101
|
</div>
|
|
97
102
|
<div class="install-cmd">
|
|
@@ -187,6 +192,12 @@ $('installBtn').addEventListener('click', async () => {
|
|
|
187
192
|
done.style.display = '';
|
|
188
193
|
installBtn.style.display = 'none';
|
|
189
194
|
$('relaunchBtn').style.display = '';
|
|
195
|
+
// Прокручиваем окно к верху чтобы кнопка «Перезапустить» точно
|
|
196
|
+
// была в видимой области (install-лог мог уехать на пол-экрана).
|
|
197
|
+
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
198
|
+
document.querySelector('.wrap')?.scrollTo?.({ top: 0, behavior: 'smooth' });
|
|
199
|
+
// Подсветка-привлечение внимания.
|
|
200
|
+
$('relaunchBtn').focus();
|
|
190
201
|
} catch (e) {
|
|
191
202
|
log.style.color = '#f88';
|
|
192
203
|
log.textContent += `\n[error] ${e?.message || String(e)}`;
|