| |
|
|
| |
|
|
| import * as state from './state.js'; |
| import * as db from './db.js'; |
| import * as chatUI from './ui/chat.js'; |
| import * as toolUI from './ui/tools.js'; |
|
|
| |
| |
| export function interruptibleTimeout(duration, signal) { |
| return new Promise((resolve, reject) => { |
| if (signal.aborted) { |
| return reject(new DOMException('Aborted', 'AbortError')); |
| } |
| const timeoutId = setTimeout(resolve, duration); |
| signal.addEventListener('abort', () => { |
| clearTimeout(timeoutId); |
| reject(new DOMException('Aborted', 'AbortError')); |
| }); |
| }); |
| } |
| |
|
|
|
|
| export async function getBrowserFingerprint() { |
| const components = [navigator.userAgent, navigator.language, screen.width + 'x' + screen.height, new Date().getTimezoneOffset()]; |
| try { |
| const canvas = document.createElement('canvas'); |
| const ctx = canvas.getContext('2d'); |
| ctx.textBaseline = "top"; |
| ctx.font = "14px 'Arial'"; |
| ctx.textBaseline = "alphabetic"; |
| ctx.fillStyle = "#f60"; |
| ctx.fillRect(125, 1, 62, 20); |
| ctx.fillStyle = "#069"; |
| ctx.fillText("a1b2c3d4e5f6g7h8i9j0_!@#$%^&*()", 2, 15); |
| components.push(canvas.toDataURL()); |
| } catch (e) { |
| components.push("canvas-error"); |
| } |
| const fingerprintString = components.join('~~~'); |
| let hash = 0; |
| for (let i = 0; i < fingerprintString.length; i++) { |
| const char = fingerprintString.charCodeAt(i); |
| hash = ((hash << 5) - hash) + char; |
| hash |= 0; |
| } |
| return 'fp_' + Math.abs(hash).toString(16); |
| } |
|
|
| export async function convertTextToFile(content, format, buttonElement) { |
| chatUI.showLoadingOnButton(buttonElement, true); |
| try { |
| const convertFormData = new FormData(); |
| convertFormData.append('content', content); |
| convertFormData.append('format', format); |
| const convertResponse = await fetch('https://texttopdf-5irq.onrender.com/', { method: 'POST', body: convertFormData }); |
| if (!convertResponse.ok) throw new Error(`خطا در ارتباط با سرور تبدیل: ${convertResponse.statusText}`); |
| const fileBlob = await convertResponse.blob(); |
| const fileName = `alpha-export-${Date.now()}.${format}`; |
| const uploadFormData = new FormData(); |
| uploadFormData.append('image', fileBlob, fileName); |
| const uploadResponse = await fetch('https://www.aisada.ir/hamed/upload.php', { method: 'POST', body: uploadFormData }); |
| if (!uploadResponse.ok) { |
| const errorText = await uploadResponse.text().catch(() => `HTTP ${uploadResponse.status}`); |
| throw new Error(`آپلود فایل ساخته شده به سرور شما ناموفق بود: ${errorText}`); |
| } |
| const uploadData = await uploadResponse.json(); |
| if (uploadData.success && uploadData.url) { |
| window.parent.postMessage({ type: 'OPEN_EXTERNAL_URL', url: uploadData.url }, '*'); |
| } else { |
| throw new Error(uploadData.message || 'پاسخ سرور آپلود شما پس از ساخت فایل، نامعتبر بود.'); |
| } |
| } catch (error) { |
| console.error('خطا در فرآیند تبدیل و آپلود فایل:', error); |
| alert(`متاسفانه در آمادهسازی فایل برای باز کردن خطایی رخ داد: ${error.message}`); |
| } finally { |
| chatUI.showLoadingOnButton(buttonElement, false); |
| } |
| } |
|
|
| export async function processAndUploadFile(file) { |
| const readFileAsBase64 = (file) => new Promise((resolve, reject) => { |
| const reader = new FileReader(); |
| reader.onload = () => resolve(reader.result.split(',')[1]); |
| reader.onerror = (error) => reject(error); |
| reader.readAsDataURL(file); |
| }); |
| try { |
| const [fileId, base64Data] = await Promise.all([db.storeFile(file), readFileAsBase64(file)]); |
| const blobUrl = URL.createObjectURL(file); |
| return { id: fileId, blobUrl, base64Data, name: file.name, mimeType: file.type }; |
| } catch (error) { |
| console.error("خطا در پردازش و ذخیره فایل:", error); |
| throw error; |
| } |
| } |
|
|
| export async function getChatStream(conversationHistory, signal) { |
| const activeChat = state.getActiveChat(); |
| const messagesForApi = conversationHistory |
| .filter(msg => !msg.isTemporary) |
| .map(msg => { |
| const apiMsg = { role: msg.role, parts: [] }; |
| msg.parts.forEach(part => { |
| if (part.text) apiMsg.parts.push({ type: 'text', text: part.text }); |
| if (part.base64Data && part.mimeType) apiMsg.parts.push({ base64Data: part.base64Data, mimeType: part.mimeType }); |
| }); |
| return apiMsg; |
| }).filter(msg => msg.parts.length > 0); |
|
|
| const bodyPayload = { |
| messages: messagesForApi, |
| id: 'chat_' + Date.now(), |
| trigger: 'submit-message', |
| show_thoughts: activeChat ? activeChat.showThoughts : false |
| }; |
|
|
| const response = await fetch('/chat', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| signal: signal, |
| body: JSON.stringify(bodyPayload), |
| }); |
|
|
| if (!response.ok) { |
| const errorText = await response.text(); |
| throw new Error(`خطای شبکه: ${response.status} - ${errorText}`); |
| } |
|
|
| return response; |
| } |
|
|
| export async function readStreamAndDisplay(response, modelBubbleOuterDivElement) { |
| let fullBotResponse = ""; |
| const activeChat = state.getActiveChat(); |
| const activeTool = state.getActiveTool(); |
|
|
| try { |
| const reader = response.body.getReader(); |
| const decoder = new TextDecoder(); |
| let buffer = ''; |
|
|
| while (true) { |
| const { value, done } = await reader.read(); |
| if (done) break; |
|
|
| buffer += decoder.decode(value, { stream: true }); |
| const events = buffer.split('\n\n'); |
| buffer = events.pop(); |
|
|
| for (const event of events) { |
| if (event.startsWith('data: ')) { |
| const dataString = event.substring(6); |
| if (dataString.trim() === '[DONE]') break; |
| try { |
| const jsonData = JSON.parse(dataString); |
| if (jsonData.type === 'thought' && jsonData.content) { |
| chatUI.streamThought(jsonData.content, modelBubbleOuterDivElement); |
| } else if (jsonData.choices?.[0]?.delta?.content) { |
| const textChunk = jsonData.choices[0].delta.content; |
| fullBotResponse += textChunk; |
| if (activeTool === 'deep-think' || activeTool === 'reasoning') { |
| chatUI.streamFinalText(fullBotResponse, modelBubbleOuterDivElement); |
| } else { |
| const hasThoughtsUI = modelBubbleOuterDivElement.querySelector('.thinking-panel-wrapper'); |
| if (hasThoughtsUI) chatUI.streamFinalText(fullBotResponse, modelBubbleOuterDivElement); |
| else chatUI.streamFreeWsChunk(modelBubbleOuterDivElement, fullBotResponse); |
| } |
| } else if (jsonData.type === 'error') { |
| throw new Error(jsonData.message || 'خطای ناشناخته از سرور'); |
| } |
| } catch (e) { |
| console.warn('خطا در پردازش قطعه JSON:', dataString, e); |
| } |
| } |
| } |
| } |
| } catch (error) { |
| throw error; |
| } finally { |
| if (fullBotResponse) { |
| const finalMessageObject = { |
| role: 'assistant', |
| parts: [{ text: fullBotResponse }], |
| toolUsed: activeTool, |
| wasGeneratedWithThoughts: activeChat ? activeChat.showThoughts : false |
| }; |
| const tempMsgIndex = activeChat.messages.findIndex(m => m.isTemporary); |
| if (tempMsgIndex > -1) activeChat.messages[tempMsgIndex] = finalMessageObject; |
| else activeChat.messages.push(finalMessageObject); |
| |
| if (activeTool === 'deep-think') { |
| chatUI.finalizeFinalText(modelBubbleOuterDivElement, fullBotResponse); |
| toolUI.hideDeepThinkPanel(modelBubbleOuterDivElement); |
| } else if (activeTool === 'reasoning') { |
| chatUI.finalizeFinalText(modelBubbleOuterDivElement, fullBotResponse); |
| toolUI.hideReasoningPanel(modelBubbleOuterDivElement); |
| } else { |
| const hasThoughtsUI = modelBubbleOuterDivElement.querySelector('.thinking-panel-wrapper'); |
| if (hasThoughtsUI) chatUI.finalizeFinalText(modelBubbleOuterDivElement, fullBotResponse); |
| else chatUI.finalizeFreeWsMessage(modelBubbleOuterDivElement, fullBotResponse); |
| } |
| chatUI.updateMessageActions(modelBubbleOuterDivElement, finalMessageObject, false, true); |
| } else { |
| const tempMsgIndex = activeChat.messages.findIndex(m => m.isTemporary); |
| if (tempMsgIndex > -1) { |
| if(!modelBubbleOuterDivElement.querySelector('.message-content')?.innerText.includes('متوقف شد')) { |
| activeChat.messages.splice(tempMsgIndex, 1); |
| const tempElement = document.getElementById('chat-window').querySelector(`.message-entry[data-index="${tempMsgIndex}"]`); |
| if (tempElement) tempElement.remove(); |
| } |
| } |
| } |
| } |
| } |
|
|