kingkont 0.7.24 → 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.
Files changed (3) hide show
  1. package/index.html +45 -8
  2. package/package.json +1 -1
  3. package/updates.html +13 -2
package/index.html CHANGED
@@ -6925,14 +6925,38 @@ let voicesLoaded = false;
6925
6925
  const VOICES_CACHE_KEY = 'voicesCache';
6926
6926
  const VOICES_CACHE_TTL = 24 * 60 * 60 * 1000;
6927
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
+
6928
6938
  function populateVoicesSelect(voices) {
6929
6939
  const select = $('genVoice');
6930
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).
6931
6955
  voices.sort((a, b) => a.name.localeCompare(b.name));
6932
6956
  for (const v of voices) {
6933
6957
  const opt = document.createElement('option');
6934
6958
  opt.value = v.id;
6935
- opt.dataset.voiceName = v.name; // нужно для KingKont elevenlabs/v3 (он ждёт name, не id)
6959
+ opt.dataset.voiceName = v.name;
6936
6960
  const lbl = v.labels || {};
6937
6961
  const tags = [lbl.gender, lbl.language || lbl.accent, lbl.age].filter(Boolean).join(' / ');
6938
6962
  opt.textContent = tags ? `${v.name} — ${tags}` : v.name;
@@ -7006,17 +7030,21 @@ document.querySelectorAll('#genModal [data-tts-model]').forEach(b => {
7006
7030
  b.classList.add('active');
7007
7031
  state.ttsModel = b.dataset.ttsModel;
7008
7032
  localStorage.setItem('ttsModel', state.ttsModel);
7009
- // voiceRow с ElevenLabs-голосами имеет смысл только для elevenlabs/v3.
7010
- const showVoice = state.ttsModel === 'elevenlabs/v3';
7011
- $('voiceRow').style.display = showVoice ? '' : 'none';
7033
+ syncTtsVoiceList();
7012
7034
  });
7013
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
+ }
7014
7044
  function syncTtsModelActive() {
7015
7045
  document.querySelectorAll('#genModal [data-tts-model]').forEach(b =>
7016
7046
  b.classList.toggle('active', b.dataset.ttsModel === state.ttsModel));
7017
- // Скрыть voiceRow если модель не elevenlabs (только для неё имеет смысл список).
7018
- const showVoice = state.ttsModel === 'elevenlabs/v3';
7019
- $('voiceRow').style.display = showVoice ? '' : 'none';
7047
+ syncTtsVoiceList();
7020
7048
  }
7021
7049
  // Подсветить активную video-модель при открытии modal'а
7022
7050
  function syncVideoModelActive() {
@@ -7112,7 +7140,16 @@ $('genSubmit').addEventListener('click', async () => {
7112
7140
  if (kind === 'audio') {
7113
7141
  const voiceId = $('genVoice').value || 'JBFqnCBsd6RMkjVDRZzb';
7114
7142
  const voiceName = $('genVoice').selectedOptions[0]?.textContent || '';
7115
- if (voiceId) localStorage.setItem('lastVoiceId', 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
+ }
7116
7153
  // Резолвим @-mentions ДО добавления tone-prefix: text-ноды инлайнятся
7117
7154
  // в их .md-содержимое, иначе ElevenLabs получит «[@name]» → пусто после
7118
7155
  // strip-spec-tags и упадёт «empty text».
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kingkont",
3
- "version": "0.7.24",
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)}`;