isaikr 0.0.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/README.md +35 -0
- package/cdn/api.js +19 -0
- package/cdn/character.js +254 -0
- package/cdn/chat.js +33 -0
- package/cdn/code-editor.js +1131 -0
- package/cdn/community-compose.js +270 -0
- package/cdn/games/2048/index.html +12 -0
- package/cdn/games/breakout/index.html +13 -0
- package/cdn/games/clicker/index.html +26 -0
- package/cdn/games/flappy/index.html +11 -0
- package/cdn/games/memory/index.html +34 -0
- package/cdn/games/pong/index.html +13 -0
- package/cdn/games/reaction/index.html +38 -0
- package/cdn/games/runner/index.html +11 -0
- package/cdn/games/snake/index.html +11 -0
- package/cdn/games/tetris/index.html +14 -0
- package/cdn/games/whack/index.html +8 -0
- package/cdn/go.js +126 -0
- package/cdn/go2.js +127 -0
- package/cdn/header3_behavior.js +1167 -0
- package/cdn/header3_layout.js +1004 -0
- package/cdn/header3_layout.js.bak +1004 -0
- package/cdn/header3_style.css +3524 -0
- package/cdn/header3_style.css.bak +3514 -0
- package/cdn/lang.js +198 -0
- package/cdn/loading.js +143 -0
- package/cdn/loading2.js +144 -0
- package/cdn/local-model.js +2941 -0
- package/cdn/main.js +4 -0
- package/cdn/main_asset.js +1849 -0
- package/cdn/main_asset.js.bak +6999 -0
- package/cdn/main_index.css +287 -0
- package/cdn/re_board3.css +733 -0
- package/cdn/re_board3.js +734 -0
- package/cdn/re_chat_tts.js +652 -0
- package/cdn/re_local_runtime.js +2246 -0
- package/cdn/re_local_runtime.js.bak +2246 -0
- package/cdn/re_share.js +577 -0
- package/cdn/re_voice.js +542 -0
- package/cdn/utils.js +36 -0
- package/cdn/view.js +321 -0
- package/header3_behavior.js +804 -0
- package/header3_layout.js +998 -0
- package/header3_style.css +2740 -0
- package/index.js +0 -0
- package/lang.js +179 -0
- package/main_asset.js +2416 -0
- package/main_index.css +274 -0
- package/package.json +14 -0
- package/re_chat_tts.js +1419 -0
- package/re_voice.js +430 -0
package/cdn/re_voice.js
ADDED
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
function getIsaiVoiceSettingsSafe() {
|
|
2
|
+
const settings = (typeof window.getIsaiVoiceSettings === 'function')
|
|
3
|
+
? (window.getIsaiVoiceSettings() || {})
|
|
4
|
+
: (window.ISAI_VOICE_SETTINGS || {});
|
|
5
|
+
|
|
6
|
+
const merged = Object.assign({
|
|
7
|
+
ttsEngine: 'browser',
|
|
8
|
+
recognitionLang: 'auto',
|
|
9
|
+
speechLang: 'auto',
|
|
10
|
+
voiceName: '',
|
|
11
|
+
rate: 1,
|
|
12
|
+
pitch: 1,
|
|
13
|
+
volume: 1
|
|
14
|
+
}, settings || {});
|
|
15
|
+
merged.ttsEngine = 'browser';
|
|
16
|
+
return merged;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function stopSupertonicPlayback() {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function parseJsonResponseSafeVoice(response) {
|
|
24
|
+
if (typeof parseJsonResponseSafe === 'function') {
|
|
25
|
+
return parseJsonResponseSafe(response);
|
|
26
|
+
}
|
|
27
|
+
const rawText = await response.text();
|
|
28
|
+
const cleaned = String(rawText || '').replace(/^\uFEFF/, '').trimStart();
|
|
29
|
+
return cleaned ? JSON.parse(cleaned) : {};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getVoiceApiMaxChars() {
|
|
33
|
+
const raw = Number(window.ISAI_API_MAX_CHARS || 700);
|
|
34
|
+
if (!Number.isFinite(raw)) return 700;
|
|
35
|
+
return Math.max(120, Math.min(4000, Math.floor(raw)));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function extractPlainTextVoice(value) {
|
|
39
|
+
const normalized = String(value || '')
|
|
40
|
+
.replace(/<think>[\s\S]*?(<\/think>|$)/gi, ' ')
|
|
41
|
+
.replace(/```json/gi, '')
|
|
42
|
+
.replace(/```/g, '')
|
|
43
|
+
.trim();
|
|
44
|
+
const holder = document.createElement('div');
|
|
45
|
+
holder.innerHTML = normalized;
|
|
46
|
+
return String(holder.textContent || holder.innerText || normalized).replace(/\s+/g, ' ').trim();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalizeIsaiVoiceLang(langCode) {
|
|
50
|
+
const raw = String(langCode || '').trim();
|
|
51
|
+
const fallback = (typeof LANG !== 'undefined' && LANG === 'ko') ? 'ko-KR' : (navigator.language || 'en-US');
|
|
52
|
+
if (!raw || raw === 'auto') return fallback;
|
|
53
|
+
if (raw.includes('-')) return raw;
|
|
54
|
+
|
|
55
|
+
const map = {
|
|
56
|
+
ko: 'ko-KR',
|
|
57
|
+
en: 'en-US',
|
|
58
|
+
ja: 'ja-JP',
|
|
59
|
+
zh: 'zh-CN',
|
|
60
|
+
es: 'es-ES',
|
|
61
|
+
fr: 'fr-FR',
|
|
62
|
+
de: 'de-DE',
|
|
63
|
+
ru: 'ru-RU',
|
|
64
|
+
pt: 'pt-PT',
|
|
65
|
+
hi: 'hi-IN'
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return map[raw] || fallback;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function resolveVoiceLangCode(value, fallback = 'en-US') {
|
|
72
|
+
if (typeof window.resolveSpeechLangCode === 'function') {
|
|
73
|
+
return window.resolveSpeechLangCode(value, fallback);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const raw = String(value || '').trim();
|
|
77
|
+
if (!raw) return normalizeIsaiVoiceLang(fallback);
|
|
78
|
+
|
|
79
|
+
const alias = {
|
|
80
|
+
en: 'en-US',
|
|
81
|
+
english: 'en-US',
|
|
82
|
+
ko: 'ko-KR',
|
|
83
|
+
kr: 'ko-KR',
|
|
84
|
+
korean: 'ko-KR',
|
|
85
|
+
ja: 'ja-JP',
|
|
86
|
+
jp: 'ja-JP',
|
|
87
|
+
japanese: 'ja-JP',
|
|
88
|
+
zh: 'zh-CN',
|
|
89
|
+
cn: 'zh-CN',
|
|
90
|
+
chinese: 'zh-CN',
|
|
91
|
+
es: 'es-ES',
|
|
92
|
+
sp: 'es-ES',
|
|
93
|
+
spanish: 'es-ES',
|
|
94
|
+
fr: 'fr-FR',
|
|
95
|
+
french: 'fr-FR',
|
|
96
|
+
de: 'de-DE',
|
|
97
|
+
ge: 'de-DE',
|
|
98
|
+
german: 'de-DE',
|
|
99
|
+
ru: 'ru-RU',
|
|
100
|
+
russian: 'ru-RU',
|
|
101
|
+
pt: 'pt-PT',
|
|
102
|
+
portuguese: 'pt-PT',
|
|
103
|
+
hi: 'hi-IN',
|
|
104
|
+
hindi: 'hi-IN'
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
if (/^[a-z]{2}-[a-z]{2}$/i.test(raw)) {
|
|
108
|
+
const [lang, region] = raw.split('-');
|
|
109
|
+
return `${String(lang).toLowerCase()}-${String(region).toUpperCase()}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const normalized = raw.toLowerCase().replace(/_/g, '-');
|
|
113
|
+
if (alias[normalized]) return alias[normalized];
|
|
114
|
+
|
|
115
|
+
return normalizeIsaiVoiceLang(raw || fallback);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function resolveSpeechVoiceByName(name, langCode) {
|
|
119
|
+
const voices = window.speechSynthesis && typeof window.speechSynthesis.getVoices === 'function'
|
|
120
|
+
? window.speechSynthesis.getVoices()
|
|
121
|
+
: [];
|
|
122
|
+
|
|
123
|
+
if (!voices.length) return null;
|
|
124
|
+
|
|
125
|
+
const voiceName = String(name || '').trim();
|
|
126
|
+
if (voiceName) {
|
|
127
|
+
const exactMatch = voices.find((voice) => voice.name === voiceName);
|
|
128
|
+
if (exactMatch) return exactMatch;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const normalizedLang = String(langCode || '').toLowerCase();
|
|
132
|
+
const langPrefix = normalizedLang.split('-')[0];
|
|
133
|
+
return voices.find((voice) => String(voice.lang || '').toLowerCase() === normalizedLang)
|
|
134
|
+
|| voices.find((voice) => String(voice.lang || '').toLowerCase().startsWith(`${langPrefix}-`))
|
|
135
|
+
|| voices[0]
|
|
136
|
+
|| null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function updateRecognitionLang() {
|
|
140
|
+
if (!recognition) return;
|
|
141
|
+
|
|
142
|
+
let targetLangCode = 'en-US';
|
|
143
|
+
|
|
144
|
+
if (currentMode === 'translate') {
|
|
145
|
+
const leftEl = document.getElementById('trans-select-left');
|
|
146
|
+
const rightEl = document.getElementById('trans-select-right');
|
|
147
|
+
const leftVal = leftEl ? leftEl.value : 'English';
|
|
148
|
+
const rightVal = rightEl ? rightEl.value : 'Korean';
|
|
149
|
+
|
|
150
|
+
if (translationSide === 'left') {
|
|
151
|
+
targetLangCode = resolveVoiceLangCode(leftVal, 'en-US');
|
|
152
|
+
} else {
|
|
153
|
+
targetLangCode = resolveVoiceLangCode(rightVal, 'ko-KR');
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
const settings = getIsaiVoiceSettingsSafe();
|
|
157
|
+
targetLangCode = normalizeIsaiVoiceLang(settings.recognitionLang);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
recognition.lang = targetLangCode;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function beginListeningForSide(side) {
|
|
164
|
+
translationSide = side;
|
|
165
|
+
setTimeout(() => {
|
|
166
|
+
try {
|
|
167
|
+
isVoiceListening = true;
|
|
168
|
+
if (typeof setVoiceState === 'function') setVoiceState(side, 'recording');
|
|
169
|
+
if (recognition) {
|
|
170
|
+
updateRecognitionLang();
|
|
171
|
+
recognition.start();
|
|
172
|
+
} else {
|
|
173
|
+
initVoiceMode();
|
|
174
|
+
}
|
|
175
|
+
} catch (error) {
|
|
176
|
+
initVoiceMode();
|
|
177
|
+
}
|
|
178
|
+
}, 100);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function initVoiceMode() {
|
|
182
|
+
window.SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
|
|
183
|
+
|
|
184
|
+
if (!window.SpeechRecognition) {
|
|
185
|
+
if (typeof showToast === 'function') showToast('Browser does not support Speech Recognition.');
|
|
186
|
+
if (typeof setMode === 'function') setMode('chat');
|
|
187
|
+
if (typeof setVoiceState === 'function') setVoiceState('left', 'idle');
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (!recognition) {
|
|
192
|
+
recognition = new SpeechRecognition();
|
|
193
|
+
recognition.continuous = true;
|
|
194
|
+
recognition.interimResults = true;
|
|
195
|
+
|
|
196
|
+
recognition.onstart = function () {
|
|
197
|
+
isVoiceListening = true;
|
|
198
|
+
if (typeof setVoiceState === 'function') setVoiceState(translationSide, 'recording');
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
recognition.onend = function () {
|
|
202
|
+
const activeVoiceMode = (currentMode === 'voice' || currentMode === 'translate');
|
|
203
|
+
if (activeVoiceMode && isVoiceListening && !isVoiceProcessing) {
|
|
204
|
+
try {
|
|
205
|
+
updateRecognitionLang();
|
|
206
|
+
recognition.start();
|
|
207
|
+
return;
|
|
208
|
+
} catch (error) {}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
isVoiceListening = false;
|
|
212
|
+
if (typeof setVoiceState === 'function') setVoiceState(translationSide, 'idle');
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
recognition.onresult = function (event) {
|
|
216
|
+
if (isVoiceProcessing) return;
|
|
217
|
+
|
|
218
|
+
let interim = '';
|
|
219
|
+
let final = '';
|
|
220
|
+
|
|
221
|
+
for (let i = event.resultIndex; i < event.results.length; i += 1) {
|
|
222
|
+
if (event.results[i].isFinal) final += event.results[i][0].transcript;
|
|
223
|
+
else interim += event.results[i][0].transcript;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const input = document.getElementById('prompt-input');
|
|
227
|
+
if (!input) return;
|
|
228
|
+
|
|
229
|
+
if (interim) {
|
|
230
|
+
input.value = interim;
|
|
231
|
+
input.style.opacity = '0.7';
|
|
232
|
+
if (typeof handleInput === 'function') handleInput(input);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (!final) return;
|
|
236
|
+
|
|
237
|
+
input.value = final.trim();
|
|
238
|
+
input.style.opacity = '1';
|
|
239
|
+
if (typeof handleInput === 'function') handleInput(input);
|
|
240
|
+
|
|
241
|
+
if (!input.value) return;
|
|
242
|
+
|
|
243
|
+
if (recognition) recognition.stop();
|
|
244
|
+
|
|
245
|
+
if (currentMode === 'translate') {
|
|
246
|
+
performTranslationRequest(input.value, translationSide);
|
|
247
|
+
} else {
|
|
248
|
+
performVoiceConversationRequest(input.value);
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
recognition.onerror = function (event) {
|
|
253
|
+
if (typeof console !== 'undefined' && console.error) console.error('Mic Error:', event.error);
|
|
254
|
+
if (event.error === 'not-allowed' && typeof showToast === 'function') {
|
|
255
|
+
showToast('Microphone permission denied.');
|
|
256
|
+
}
|
|
257
|
+
isVoiceListening = false;
|
|
258
|
+
if (typeof setVoiceState === 'function') setVoiceState(translationSide, 'idle');
|
|
259
|
+
if ((event.error === 'not-allowed' || event.error === 'service-not-allowed') && typeof setMode === 'function') {
|
|
260
|
+
setMode('chat');
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
updateRecognitionLang();
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
isVoiceListening = true;
|
|
269
|
+
recognition.start();
|
|
270
|
+
} catch (error) {
|
|
271
|
+
if (typeof console !== 'undefined' && console.log) console.log('Recognition start ignored', error);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async function performVoiceConversationRequest(text) {
|
|
276
|
+
isVoiceProcessing = true;
|
|
277
|
+
if (typeof setVoiceState === 'function') setVoiceState('left', 'processing');
|
|
278
|
+
|
|
279
|
+
const input = document.getElementById('prompt-input');
|
|
280
|
+
const userText = String(text || '').trim();
|
|
281
|
+
if (!userText) {
|
|
282
|
+
isVoiceProcessing = false;
|
|
283
|
+
if (typeof setVoiceState === 'function') setVoiceState('left', 'idle');
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (input) {
|
|
288
|
+
input.value = '';
|
|
289
|
+
input.style.opacity = '1';
|
|
290
|
+
if (typeof handleInput === 'function') handleInput(input);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (typeof appendMsg === 'function') appendMsg('user', userText);
|
|
294
|
+
if (typeof startExperience === 'function') startExperience();
|
|
295
|
+
if (typeof showLoader === 'function') showLoader(true);
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
let finalPrompt = userText;
|
|
299
|
+
let sysPrompt = '';
|
|
300
|
+
|
|
301
|
+
if (typeof activeApp !== 'undefined' && activeApp && activeApp.system_prompt) {
|
|
302
|
+
sysPrompt = String(activeApp.system_prompt || '').trim();
|
|
303
|
+
}
|
|
304
|
+
if (typeof window.buildIsaiSystemPrompt === 'function') {
|
|
305
|
+
sysPrompt = window.buildIsaiSystemPrompt(sysPrompt);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const history = Array.isArray(chatHistory)
|
|
309
|
+
? chatHistory
|
|
310
|
+
.filter((msg) => {
|
|
311
|
+
if (!msg || (msg.role !== 'user' && msg.role !== 'assistant')) return false;
|
|
312
|
+
const content = String(msg.content || '').trim();
|
|
313
|
+
if (!content) return false;
|
|
314
|
+
if (msg.role === 'assistant' && typeof isIgnorableAssistantGreeting === 'function' && isIgnorableAssistantGreeting(content)) {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
return true;
|
|
318
|
+
})
|
|
319
|
+
.slice(-6)
|
|
320
|
+
: [];
|
|
321
|
+
const response = await fetch('?action=ai_chat', {
|
|
322
|
+
method: 'POST',
|
|
323
|
+
body: JSON.stringify({
|
|
324
|
+
prompt: finalPrompt,
|
|
325
|
+
history: history,
|
|
326
|
+
system_prompt: sysPrompt,
|
|
327
|
+
max_chars: getVoiceApiMaxChars()
|
|
328
|
+
})
|
|
329
|
+
});
|
|
330
|
+
const data = await parseJsonResponseSafeVoice(response);
|
|
331
|
+
|
|
332
|
+
if (!data || !data.response) {
|
|
333
|
+
throw new Error(data && data.error ? data.error : 'Voice chat failed');
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const bubbleHtml = (typeof parseMarkdownLocal === 'function')
|
|
337
|
+
? parseMarkdownLocal(data.response, true)
|
|
338
|
+
: data.response;
|
|
339
|
+
|
|
340
|
+
if (typeof appendMsg === 'function') appendMsg('ai', bubbleHtml);
|
|
341
|
+
|
|
342
|
+
const historyResult = String(data.response).replace(/<think>[\s\S]*?(<\/think>|$)/gi, '').trim();
|
|
343
|
+
if (Array.isArray(chatHistory)) {
|
|
344
|
+
chatHistory.push(
|
|
345
|
+
{ role: 'user', content: userText },
|
|
346
|
+
{ role: 'assistant', content: historyResult }
|
|
347
|
+
);
|
|
348
|
+
if (typeof isIgnorableAssistantGreeting === 'function') {
|
|
349
|
+
chatHistory = chatHistory.filter((msg) => {
|
|
350
|
+
if (!msg || (msg.role !== 'user' && msg.role !== 'assistant')) return false;
|
|
351
|
+
const content = String(msg.content || '').trim();
|
|
352
|
+
if (!content) return false;
|
|
353
|
+
if (msg.role === 'assistant' && isIgnorableAssistantGreeting(content)) return false;
|
|
354
|
+
return true;
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (typeof scrollBottom === 'function') scrollBottom();
|
|
360
|
+
speakText(data.response);
|
|
361
|
+
} catch (error) {
|
|
362
|
+
isVoiceProcessing = false;
|
|
363
|
+
if (typeof appendMsg === 'function') appendMsg('error', `Voice Error: ${error.message || error}`);
|
|
364
|
+
if (typeof setVoiceState === 'function') setVoiceState('left', 'idle');
|
|
365
|
+
} finally {
|
|
366
|
+
if (typeof showLoader === 'function') showLoader(false);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
async function performTranslationRequest(text, side) {
|
|
371
|
+
isVoiceProcessing = true;
|
|
372
|
+
if (typeof setVoiceState === 'function') setVoiceState(side, 'processing');
|
|
373
|
+
|
|
374
|
+
const input = document.getElementById('prompt-input');
|
|
375
|
+
const userText = String(text || '').trim();
|
|
376
|
+
if (!userText) {
|
|
377
|
+
isVoiceProcessing = false;
|
|
378
|
+
if (typeof setVoiceState === 'function') setVoiceState(side, 'idle');
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (input) {
|
|
383
|
+
input.value = '';
|
|
384
|
+
input.style.opacity = '1';
|
|
385
|
+
if (typeof handleInput === 'function') handleInput(input);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (typeof appendMsg === 'function') appendMsg('user', userText);
|
|
389
|
+
|
|
390
|
+
const leftLang = document.getElementById('trans-select-left')?.value || 'English';
|
|
391
|
+
const rightLang = document.getElementById('trans-select-right')?.value || 'Korean';
|
|
392
|
+
const sourceLangName = side === 'left' ? leftLang : rightLang;
|
|
393
|
+
const targetLangName = side === 'left' ? rightLang : leftLang;
|
|
394
|
+
|
|
395
|
+
try {
|
|
396
|
+
const res = await fetch('?action=ai_translate', {
|
|
397
|
+
method: 'POST',
|
|
398
|
+
body: JSON.stringify({ text: userText, source_lang: sourceLangName, target_lang: targetLangName })
|
|
399
|
+
});
|
|
400
|
+
const data = await parseJsonResponseSafeVoice(res);
|
|
401
|
+
|
|
402
|
+
let resultText = data.response || '';
|
|
403
|
+
let detectedLang = data.lang_code || '';
|
|
404
|
+
try {
|
|
405
|
+
if (data.response && typeof data.response === 'object') {
|
|
406
|
+
resultText = data.response.text || resultText;
|
|
407
|
+
detectedLang = data.response.lang_code || detectedLang;
|
|
408
|
+
} else {
|
|
409
|
+
const cleanRaw = String(data.response || '').replace(/```json/gi, '').replace(/```/g, '').trim();
|
|
410
|
+
let clean = cleanRaw;
|
|
411
|
+
const jsonMatch = cleanRaw.match(/\{[\s\S]*\}/);
|
|
412
|
+
if (jsonMatch) clean = jsonMatch[0];
|
|
413
|
+
const parsed = JSON.parse(clean);
|
|
414
|
+
resultText = parsed.text || resultText;
|
|
415
|
+
detectedLang = parsed.lang_code || detectedLang;
|
|
416
|
+
}
|
|
417
|
+
} catch (error) {}
|
|
418
|
+
|
|
419
|
+
const plainTranslatedText = extractPlainTextVoice(resultText);
|
|
420
|
+
if (typeof appendMsg === 'function') {
|
|
421
|
+
const translatedBubble = appendMsg('ai', plainTranslatedText);
|
|
422
|
+
if (translatedBubble) {
|
|
423
|
+
translatedBubble.style.color = '#111111';
|
|
424
|
+
translatedBubble.style.fontWeight = '700';
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const targetCode = resolveVoiceLangCode(detectedLang || targetLangName, resolveVoiceLangCode(targetLangName, 'en-US'));
|
|
429
|
+
speakText(plainTranslatedText, targetCode);
|
|
430
|
+
} catch (error) {
|
|
431
|
+
if (typeof console !== 'undefined' && console.error) console.error(error);
|
|
432
|
+
isVoiceProcessing = false;
|
|
433
|
+
if (isVoiceListening) {
|
|
434
|
+
try {
|
|
435
|
+
if (recognition) recognition.start();
|
|
436
|
+
} catch (restartError) {}
|
|
437
|
+
if (typeof setVoiceState === 'function') setVoiceState(side, 'recording');
|
|
438
|
+
} else if (typeof setVoiceState === 'function') {
|
|
439
|
+
setVoiceState(side, 'idle');
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function stopVoiceMode(fullExit = true) {
|
|
445
|
+
if (recognition) {
|
|
446
|
+
if (fullExit) {
|
|
447
|
+
recognition.onend = null;
|
|
448
|
+
recognition.abort();
|
|
449
|
+
recognition = null;
|
|
450
|
+
} else {
|
|
451
|
+
recognition.stop();
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
isVoiceListening = false;
|
|
456
|
+
if (typeof setVoiceState === 'function') setVoiceState(translationSide || 'left', 'idle');
|
|
457
|
+
|
|
458
|
+
const input = document.getElementById('prompt-input');
|
|
459
|
+
if (input) {
|
|
460
|
+
input.placeholder = '';
|
|
461
|
+
input.style.opacity = '1';
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (fullExit) {
|
|
465
|
+
isVoiceProcessing = false;
|
|
466
|
+
stopSupertonicPlayback();
|
|
467
|
+
window.speechSynthesis.cancel();
|
|
468
|
+
if (typeof window.stopIsaiTtsPreview === 'function') {
|
|
469
|
+
window.stopIsaiTtsPreview();
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function speakText(text, langCode = null) {
|
|
475
|
+
if (currentMode !== 'voice' && currentMode !== 'translate') return;
|
|
476
|
+
|
|
477
|
+
stopSupertonicPlayback();
|
|
478
|
+
window.speechSynthesis.cancel();
|
|
479
|
+
isVoiceProcessing = true;
|
|
480
|
+
if (recognition) recognition.stop();
|
|
481
|
+
|
|
482
|
+
if (typeof setVoiceState === 'function') setVoiceState(translationSide || 'left', 'processing');
|
|
483
|
+
|
|
484
|
+
const cleanText = String(text || '')
|
|
485
|
+
.replace(/```[\s\S]*?```/g, '')
|
|
486
|
+
.replace(/[*#`\[\]]/g, '')
|
|
487
|
+
.replace(/<[^>]*>?/gm, '');
|
|
488
|
+
|
|
489
|
+
const settings = getIsaiVoiceSettingsSafe();
|
|
490
|
+
const utterance = new SpeechSynthesisUtterance(cleanText);
|
|
491
|
+
const resolvedLang = normalizeIsaiVoiceLang(langCode || settings.speechLang);
|
|
492
|
+
const selectedVoice = resolveSpeechVoiceByName(settings.voiceName, resolvedLang);
|
|
493
|
+
|
|
494
|
+
utterance.lang = selectedVoice && selectedVoice.lang ? selectedVoice.lang : resolvedLang;
|
|
495
|
+
utterance.rate = Math.max(0.7, Math.min(1.4, parseFloat(settings.rate) || 1));
|
|
496
|
+
utterance.pitch = Math.max(0.6, Math.min(1.6, parseFloat(settings.pitch) || 1));
|
|
497
|
+
utterance.volume = Math.max(0, Math.min(1, parseFloat(settings.volume) || 1));
|
|
498
|
+
if (selectedVoice) utterance.voice = selectedVoice;
|
|
499
|
+
|
|
500
|
+
utterance.onend = () => {
|
|
501
|
+
isVoiceProcessing = false;
|
|
502
|
+
if (currentMode === 'voice' || currentMode === 'translate') {
|
|
503
|
+
beginListeningForSide(translationSide || 'left');
|
|
504
|
+
} else if (typeof updateSubmitIcon === 'function') {
|
|
505
|
+
updateSubmitIcon('mic');
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
utterance.onerror = () => {
|
|
510
|
+
isVoiceProcessing = false;
|
|
511
|
+
if (currentMode === 'voice' || currentMode === 'translate') {
|
|
512
|
+
if (typeof setVoiceState === 'function') setVoiceState(translationSide || 'left', 'idle');
|
|
513
|
+
} else if (typeof updateSubmitIcon === 'function') {
|
|
514
|
+
updateSubmitIcon('mic');
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
window.speechSynthesis.speak(utterance);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
function updateSubmitIcon(state, side = 'right') {
|
|
522
|
+
if (typeof setVoiceState === 'function') {
|
|
523
|
+
setVoiceState('left', state === 'mic' || state === 'default' ? 'idle' : state);
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const btn = document.getElementById('btn-submit-left');
|
|
528
|
+
const icon = document.getElementById('icon-submit-left');
|
|
529
|
+
|
|
530
|
+
if (!btn || !icon) return;
|
|
531
|
+
|
|
532
|
+
if (state === 'recording') {
|
|
533
|
+
icon.className = 'ri-voiceprint-line text-lg text-white animate-mic-breath';
|
|
534
|
+
btn.style.backgroundColor = '#ef4444';
|
|
535
|
+
} else if (state === 'processing') {
|
|
536
|
+
icon.className = 'ri-voiceprint-line text-lg text-white';
|
|
537
|
+
btn.style.backgroundColor = '';
|
|
538
|
+
} else {
|
|
539
|
+
icon.className = 'ri-voiceprint-line text-lg text-white';
|
|
540
|
+
btn.style.backgroundColor = '';
|
|
541
|
+
}
|
|
542
|
+
}
|
package/cdn/utils.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const root = (typeof window !== 'undefined') ? window : globalThis;
|
|
2
|
+
|
|
3
|
+
export function clamp(value, min, max) {
|
|
4
|
+
const n = Number(value);
|
|
5
|
+
if (!Number.isFinite(n)) return min;
|
|
6
|
+
return Math.max(min, Math.min(max, n));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function escapeCssUrlValue(value) {
|
|
10
|
+
return String(value || '').trim().replace(/["\\\n\r]/g, '');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function normalizeText(value) {
|
|
14
|
+
return String(value || '').replace(/\s+/g, ' ').trim();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function safeJsonParse(input, fallback = null) {
|
|
18
|
+
try {
|
|
19
|
+
if (input == null || input === '') return fallback;
|
|
20
|
+
return JSON.parse(String(input));
|
|
21
|
+
} catch (error) {
|
|
22
|
+
return fallback;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getElement(id) {
|
|
27
|
+
return document.getElementById(id);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
root.ISAI_CDN_UTILS = Object.assign(root.ISAI_CDN_UTILS || {}, {
|
|
31
|
+
clamp,
|
|
32
|
+
escapeCssUrlValue,
|
|
33
|
+
normalizeText,
|
|
34
|
+
safeJsonParse,
|
|
35
|
+
getElement
|
|
36
|
+
});
|