Devmightwin commited on
Commit
94ef2f3
·
verified ·
1 Parent(s): 8b9d43f

Update app.js

Browse files
Files changed (1) hide show
  1. app.js +56 -31
app.js CHANGED
@@ -7,6 +7,7 @@ window.onerror = (msg, url, line) => {
7
  // CONFIG & STATE
8
  // ============================================================
9
  const STREAM_API_URL = "https://aqhedgyiworhbfkrzreb.supabase.co/functions/v1/quick-processor";
 
10
  const RAZORPAY_KEY_ID = "rzp_test_TZ9SeSSO5G8eCq";
11
  const firebaseConfig = { apiKey: "AIzaSyAChu97QI-tyulpisxH6aECBYlj88DGB1o", authDomain: "arki-ai-280b8.firebaseapp.com", projectId: "arki-ai-280b8", storageBucket: "arki-ai-280b8.firebasestorage.app", messagingSenderId: "1075792140029", appId: "1:1075792140029:web:9f3af7c6b89530c7ee9094", measurementId: "G-PL1YY34MBZ" };
12
 
@@ -35,7 +36,7 @@ let userPersona = safeGet('arki_persona', ''), userName = safeGet('arki_username
35
  let currentUser = null, currentPlan = "Free", selectedModel = 'orki-3.0', currentDailyUsage = 0;
36
  let isAuthRegister = false, abortController = null, isGenerating = false, currentChatId = null;
37
  let currentChatMessages = [], selectedCycle = 'monthly', cachedThreads = [], isIncognito = false;
38
- let mediaRecorder = null, audioChunks = [], isRecording = false, gradioClient = null;
39
  let currentAudio = null, activeTtsBtn = null, ttsAudioCache = new Map();
40
  let auth = null, db = null;
41
 
@@ -147,7 +148,7 @@ function updateUiLangUI() {
147
  }
148
 
149
  // ============================================================
150
- // CORE LOGIC
151
  // ============================================================
152
  function buildPersonaContext() {
153
  return [userName ? `The user's name is ${userName}; address them by name naturally.` : '', userPersona].filter(Boolean).join(' ');
@@ -178,26 +179,30 @@ function toggleDrawer(open) {
178
  drawerBackdrop.classList.toggle('hidden', !open);
179
  }
180
 
181
- async function getGradioClient() {
182
- if (!gradioClient) {
183
- const { Client } = await import("https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js");
184
- gradioClient = await Client.connect("ai4bharat/indic-parler-tts");
185
- }
186
- return gradioClient;
187
- }
188
-
189
  function cleanTextForTTS(rawText) {
190
- return rawText.replace(/[#*_`~>\[\]\(\)]/g, '').replace(/[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{2600}-\u{26FF}]/gu, '').trim();
 
 
 
191
  }
192
 
193
  async function playBodoSpeech(btn, text) {
194
- if (activeTtsBtn === btn && currentAudio && !currentAudio.paused) { currentAudio.pause(); btn.innerHTML = `🔊 Listen`; return; }
195
- if (currentAudio) { currentAudio.pause(); if (activeTtsBtn) activeTtsBtn.innerHTML = `🔊 Listen`; }
 
 
 
 
 
 
 
196
  const cleanText = cleanTextForTTS(text);
197
  if (!cleanText) return;
198
 
199
  if (ttsAudioCache.has(cleanText)) {
200
- activeTtsBtn = btn; btn.disabled = false; btn.innerHTML = `⏹ Stop`;
 
 
201
  currentAudio = new Audio(ttsAudioCache.get(cleanText));
202
  currentAudio.onended = () => { btn.innerHTML = `🔊 Listen`; activeTtsBtn = null; currentAudio = null; };
203
  try { await currentAudio.play(); } catch { btn.innerHTML = `⚠️ Retry`; }
@@ -205,36 +210,50 @@ async function playBodoSpeech(btn, text) {
205
  }
206
 
207
  try {
208
- activeTtsBtn = btn; btn.disabled = true; btn.innerHTML = `<span class="animate-pulse">⏳ Rendering...</span>`;
209
- const app = await getGradioClient();
210
- let result;
211
- try { result = await app.predict(0, [cleanText, "Maya speaks in a clear, pleasant voice with natural pacing in studio quality.", 2.0]); }
212
- catch { result = await app.predict("/generate_base", [cleanText, "Maya speaks in a clear, pleasant voice with natural pacing in studio quality.", 2.0]); }
213
-
214
- const audioData = result.data?.[0];
215
- const audioUrl = typeof audioData === 'string' ? audioData : audioData?.url;
216
- if (!audioUrl) throw new Error("Audio URL missing");
217
-
 
 
 
 
 
 
 
 
 
218
  ttsAudioCache.set(cleanText, audioUrl);
219
- btn.disabled = false; btn.innerHTML = `⏹ Stop`;
 
220
  currentAudio = new Audio(audioUrl);
221
  currentAudio.onended = () => { btn.innerHTML = `🔊 Listen`; activeTtsBtn = null; currentAudio = null; };
222
  await currentAudio.play();
223
  } catch (err) {
224
- console.error("TTS error:", err); btn.disabled = false; btn.innerHTML = `⚠️ Retry`;
 
 
225
  alert("Voice queue is busy or timed out. Please tap retry in a moment.");
226
  }
227
  }
228
 
229
  function attachTtsButton(bubble, text) {
230
- if (!bubble) return;
231
  const actionRow = document.createElement('div');
232
  actionRow.className = "flex items-center gap-2 mt-2 pt-1 border-t border-zinc-700/30";
233
  const ttsBtn = document.createElement('button');
234
- ttsBtn.type = "button"; ttsBtn.className = "text-[11px] font-medium text-zinc-400 hover:text-emerald-400 flex items-center gap-1 transition cursor-pointer";
 
235
  ttsBtn.innerHTML = `🔊 Listen`;
236
  ttsBtn.onclick = () => playBodoSpeech(ttsBtn, text.trim());
237
- actionRow.appendChild(ttsBtn); bubble.appendChild(actionRow);
 
238
  }
239
 
240
  function createBubbleElement(role) {
@@ -358,7 +377,13 @@ async function handleSend() {
358
  attachTtsButton(aiBubble, finalizedText);
359
  } else {
360
  thinking && thinking.classList.add('hidden');
361
- appendMessage('model', err.message.includes('429') ? 'Rate limit hit (5 req/min). 15s नेनानै फिन लिर।' : `Error: ${err.message}`);
 
 
 
 
 
 
362
  }
363
  } finally {
364
  thinking && thinking.classList.add('hidden');
@@ -762,4 +787,4 @@ on('auth-submit-btn', 'click', async () => {
762
  // ============================================================
763
  // INIT
764
  // ============================================================
765
- updateScriptUI(); updateUiLangUI(); updatePricingUI(); updateUsageUI(); updateIncognitoUI(); applyUILanguage(uiLanguage);
 
7
  // CONFIG & STATE
8
  // ============================================================
9
  const STREAM_API_URL = "https://aqhedgyiworhbfkrzreb.supabase.co/functions/v1/quick-processor";
10
+ const TTS_API_URL = "https://aqhedgyiworhbfkrzreb.supabase.co/functions/v1/swift-worker";
11
  const RAZORPAY_KEY_ID = "rzp_test_TZ9SeSSO5G8eCq";
12
  const firebaseConfig = { apiKey: "AIzaSyAChu97QI-tyulpisxH6aECBYlj88DGB1o", authDomain: "arki-ai-280b8.firebaseapp.com", projectId: "arki-ai-280b8", storageBucket: "arki-ai-280b8.firebasestorage.app", messagingSenderId: "1075792140029", appId: "1:1075792140029:web:9f3af7c6b89530c7ee9094", measurementId: "G-PL1YY34MBZ" };
13
 
 
36
  let currentUser = null, currentPlan = "Free", selectedModel = 'orki-3.0', currentDailyUsage = 0;
37
  let isAuthRegister = false, abortController = null, isGenerating = false, currentChatId = null;
38
  let currentChatMessages = [], selectedCycle = 'monthly', cachedThreads = [], isIncognito = false;
39
+ let mediaRecorder = null, audioChunks = [], isRecording = false;
40
  let currentAudio = null, activeTtsBtn = null, ttsAudioCache = new Map();
41
  let auth = null, db = null;
42
 
 
148
  }
149
 
150
  // ============================================================
151
+ // CORE LOGIC & TTS ENGINE
152
  // ============================================================
153
  function buildPersonaContext() {
154
  return [userName ? `The user's name is ${userName}; address them by name naturally.` : '', userPersona].filter(Boolean).join(' ');
 
179
  drawerBackdrop.classList.toggle('hidden', !open);
180
  }
181
 
 
 
 
 
 
 
 
 
182
  function cleanTextForTTS(rawText) {
183
+ return rawText
184
+ .replace(/[#*_`~>\[\]\(\)]/g, '')
185
+ .replace(/[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{2600}-\u{26FF}]/gu, '')
186
+ .trim();
187
  }
188
 
189
  async function playBodoSpeech(btn, text) {
190
+ if (activeTtsBtn === btn && currentAudio && !currentAudio.paused) {
191
+ currentAudio.pause();
192
+ btn.innerHTML = `🔊 Listen`;
193
+ return;
194
+ }
195
+ if (currentAudio) {
196
+ currentAudio.pause();
197
+ if (activeTtsBtn) activeTtsBtn.innerHTML = `🔊 Listen`;
198
+ }
199
  const cleanText = cleanTextForTTS(text);
200
  if (!cleanText) return;
201
 
202
  if (ttsAudioCache.has(cleanText)) {
203
+ activeTtsBtn = btn;
204
+ btn.disabled = false;
205
+ btn.innerHTML = `⏹ Stop`;
206
  currentAudio = new Audio(ttsAudioCache.get(cleanText));
207
  currentAudio.onended = () => { btn.innerHTML = `🔊 Listen`; activeTtsBtn = null; currentAudio = null; };
208
  try { await currentAudio.play(); } catch { btn.innerHTML = `⚠️ Retry`; }
 
210
  }
211
 
212
  try {
213
+ activeTtsBtn = btn;
214
+ btn.disabled = true;
215
+ btn.innerHTML = `<span class="animate-pulse">⏳ Rendering...</span>`;
216
+
217
+ const res = await fetch(TTS_API_URL, {
218
+ method: 'POST',
219
+ headers: { 'Content-Type': 'application/json' },
220
+ body: JSON.stringify({ text: cleanText })
221
+ });
222
+
223
+ if (!res.ok) {
224
+ const errData = await res.json().catch(() => ({}));
225
+ throw new Error(errData.error || `Server status ${res.status}`);
226
+ }
227
+
228
+ const data = await res.json();
229
+ const audioUrl = data.audioUrl || (data.data && (typeof data.data[0] === 'string' ? data.data[0] : data.data[0]?.url));
230
+ if (!audioUrl) throw new Error("Audio URL missing from TTS worker");
231
+
232
  ttsAudioCache.set(cleanText, audioUrl);
233
+ btn.disabled = false;
234
+ btn.innerHTML = `⏹ Stop`;
235
  currentAudio = new Audio(audioUrl);
236
  currentAudio.onended = () => { btn.innerHTML = `🔊 Listen`; activeTtsBtn = null; currentAudio = null; };
237
  await currentAudio.play();
238
  } catch (err) {
239
+ console.error("TTS generation error:", err);
240
+ btn.disabled = false;
241
+ btn.innerHTML = `⚠️ Retry`;
242
  alert("Voice queue is busy or timed out. Please tap retry in a moment.");
243
  }
244
  }
245
 
246
  function attachTtsButton(bubble, text) {
247
+ if (!bubble || !text.trim()) return;
248
  const actionRow = document.createElement('div');
249
  actionRow.className = "flex items-center gap-2 mt-2 pt-1 border-t border-zinc-700/30";
250
  const ttsBtn = document.createElement('button');
251
+ ttsBtn.type = "button";
252
+ ttsBtn.className = "text-[11px] font-medium text-zinc-400 hover:text-emerald-400 flex items-center gap-1 transition cursor-pointer";
253
  ttsBtn.innerHTML = `🔊 Listen`;
254
  ttsBtn.onclick = () => playBodoSpeech(ttsBtn, text.trim());
255
+ actionRow.appendChild(ttsBtn);
256
+ bubble.appendChild(actionRow);
257
  }
258
 
259
  function createBubbleElement(role) {
 
377
  attachTtsButton(aiBubble, finalizedText);
378
  } else {
379
  thinking && thinking.classList.add('hidden');
380
+ let friendlyMsg = err.message;
381
+ if (err.message.includes('429')) {
382
+ friendlyMsg = currentScript === 'roman' ? 'Rate limit hit. 15s nena-nwi fin lwir.' : 'रेत लिमिट थफैबाय। 15 सेकेन्द नेनानै फिन लिर।';
383
+ } else if (err.message.includes('high demand') || err.message.includes('503')) {
384
+ friendlyMsg = currentScript === 'roman' ? 'Server-ao traffic baraydwng. 10s unao fin sonanai nay.' : 'सार्भार आव मानसि बारा जादों। 10 सेकेन्द उनाव फिन नाजा।';
385
+ }
386
+ appendMessage('model', friendlyMsg);
387
  }
388
  } finally {
389
  thinking && thinking.classList.add('hidden');
 
787
  // ============================================================
788
  // INIT
789
  // ============================================================
790
+ updateScriptUI(); updateUiLangUI(); updatePricingUI(); updateUsageUI(); updateIncognitoUI(); applyUILanguage(uiLanguage);