const $ = (id) => document.getElementById(id); const state = { sessionId: null, isComplete: false }; const stageLabels = { intro: 'تمهيد', exploration: 'استكشاف', usecase: 'حالات الاستخدام', evaluation: 'تقييم', done: 'مكتمل' }; $('start-btn').addEventListener('click', startSession); $('project-title').addEventListener('keydown', e => { if (e.key === 'Enter') startSession(); }); $('send-btn').addEventListener('click', sendMessage); $('input').addEventListener('keydown', e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }); $('input').addEventListener('input', e => { e.target.style.height = 'auto'; e.target.style.height = Math.min(e.target.scrollHeight, 140) + 'px'; }); $('export-btn').addEventListener('click', () => { if (state.sessionId) window.open(`/report/${state.sessionId}`, '_blank'); }); async function startSession() { const title = $('project-title').value.trim(); if (!title) { $('project-title').focus(); return; } $('start-btn').disabled = true; try { const res = await fetch('/api/start', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title }) }); const data = await res.json(); if (!res.ok) throw new Error(data.error || 'فشل'); state.sessionId = data.sessionId; $('welcome').classList.add('hidden'); $('app').classList.remove('hidden'); $('project-name').textContent = title; addAgentMessage(data.reply, true); updateState(data); } catch (e) { alert('خطأ: ' + e.message); $('start-btn').disabled = false; } } async function sendMessage() { const input = $('input'); const text = input.value.trim(); if (!text || !state.sessionId) return; addUserMessage(text); input.value = ''; input.style.height = 'auto'; $('send-btn').disabled = true; const thinkingEl = addAgentMessage('يفكّر...', false, true); try { const res = await fetch('/api/message', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sessionId: state.sessionId, message: text }) }); const data = await res.json(); if (!res.ok) throw new Error(data.error || 'فشل'); thinkingEl.remove(); addAgentMessage(data.reply, true); updateState(data); } catch (e) { thinkingEl.querySelector('.bubble').textContent = '⚠️ ' + e.message; thinkingEl.classList.remove('thinking'); } finally { $('send-btn').disabled = false; $('input').focus(); } } function addUserMessage(text) { const el = document.createElement('div'); el.className = 'msg user'; el.innerHTML = `
أنا
`; el.querySelector('.bubble').textContent = text; $('messages').appendChild(el); scrollMsgs(); } function addAgentMessage(text, typewriter = false, thinking = false) { document.querySelectorAll('.msg.agent.fresh').forEach(e => e.classList.remove('fresh')); const el = document.createElement('div'); el.className = 'msg agent fresh' + (thinking ? ' thinking' : ''); el.innerHTML = `
AI
`; $('messages').appendChild(el); const bubble = el.querySelector('.bubble'); if (typewriter) typeText(bubble, text); else bubble.textContent = text; scrollMsgs(); return el; } function typeText(el, text) { el.textContent = ''; const cursor = document.createElement('span'); cursor.className = 'cursor'; el.appendChild(cursor); let i = 0; const speed = Math.max(8, Math.min(28, 1200 / text.length)); const tick = () => { if (i < text.length) { cursor.insertAdjacentText('beforebegin', text[i]); i++; scrollMsgs(); setTimeout(tick, speed); } else { cursor.remove(); } }; tick(); } function scrollMsgs() { const m = $('messages'); m.scrollTop = m.scrollHeight; } function updateState(data) { const fps = data.functionPoints || []; const ucs = data.useCases || []; const actors = data.actors || []; $('fp-count').textContent = fps.length; $('uc-count').textContent = ucs.length; $('fp-total').textContent = fps.reduce((s, f) => s + (Number(f.fpScore) || 0), 0); $('stage-pill').textContent = stageLabels[data.stage] || data.stage || '—'; // Tree const tree = $('tree'); if (!actors.length && !fps.length) { tree.innerHTML = '
ستظهر الـ Actors والوظائف هنا تلقائياً.
'; } else { let html = ''; if (actors.length) { actors.forEach(a => { html += `
${esc(a)}
`; }); } fps.forEach(f => { html += `
${esc(f.id || '')} · ${esc(f.name || '')}
`; }); tree.innerHTML = html; } // Live preview const prev = $('preview-content'); let html = ''; if (data.scope) html += `

نطاق النظام

${esc(data.scope)}
`; if (actors.length) { html += `

المستخدمون

${actors.map(esc).join(' · ')}
`; } if (fps.length) { html += `

المتطلبات الوظيفية (${fps.length})

`; fps.forEach(f => { html += `
${esc(f.id || '')} ${esc(f.name || '')} — ${esc(f.complexity || '')} (FP ${esc(String(f.fpScore || ''))})
`; }); } if (ucs.length) { html += `

حالات الاستخدام (${ucs.length})

`; ucs.forEach(u => { html += `
${esc(u.id || '')} ${esc(u.title || '')}
الفاعل: ${esc(u.actor || '')}
`; }); } prev.innerHTML = html || '
سيتشكّل النص الهندسي للتقرير هنا أثناء الحوار.
'; // Show export when there's enough content or marked complete if (data.isComplete || (fps.length >= 3 && ucs.length >= 1)) { $('export-btn').classList.remove('hidden'); } state.isComplete = !!data.isComplete; } function esc(s) { return String(s ?? '').replace(/[&<>"']/g, c => ({ '&':'&','<':'<','>':'>','"':'"',"'":''' }[c])); }