ابدأ التسجيل حذف الكل
مُصمّم لك على HTML، CSS، وJavaScript — يعمل بدون خادم (حفظ محلي).
{recognizing=true;statusEl.textContent='يسجل (تحويل نصي)';micDot.classList.add('active');} r.onend = ()=>{recognizing=false;statusEl.textContent='متوقف';micDot.classList.remove('active');} r.onerror = (e)=>{console.error('speech error',e);statusEl.textContent='خطأ: '+(e.error||e.message)} r.onresult = (event)=>{ let interim=''; for(let i=event.resultIndex;i{ finalTranscript=''; interimEl.textContent='استمع...'; const ok = await ensureMic(); if(!ok){alert('يرجى السماح بصلاحية الميكروفون من المتصفح');return} if(recognition){ try{recognition.start();}catch(e){console.warn(e);} } // start recording audio too (for fallback / download) if(mediaRecorder){ chunks = []; mediaRecorder.ondataavailable = e=>{ if(e.data && e.data.size) chunks.push(e.data) }; mediaRecorder.onstop = ()=>{ // create blob URL and attach to the last saved note when user saves const blob = new Blob(chunks,{type:'audio/webm'}); // store temporarily in memory window._lastRecordedAudio = blob; } try{mediaRecorder.start();}catch(e){console.warn('media start err',e)} } statusEl.textContent='يعمل...'; }); stopBtn.addEventListener('click', ()=>{ if(recognition && recognizing){ try{recognition.stop();}catch(e){} } if(mediaRecorder && mediaRecorder.state!=='inactive'){ try{mediaRecorder.stop();}catch(e){} } statusEl.textContent='متوقف'; }); saveBtn.addEventListener('click', ()=>{ const text = noteEdit.value.trim() || interimEl.textContent.trim(); if(!text){alert('النص فارغ — تحدث أولاً ثم احفظ.');return} const ts = formatDate(new Date()); const note = {id:Date.now(), text, created_at:ts}; // attach audio blob if available if(window._lastRecordedAudio){ // store in IndexedDB is better, but for simplicity we store as Object URL and keep in memory // convert blob to base64 (careful: could be large). We'll store as object URL and not persist across reloads. note.audioURL = URL.createObjectURL(window._lastRecordedAudio); // clear temp delete window._lastRecordedAudio; } notes.unshift(note); saveNotes(notes); renderNotes(); interimEl.textContent = 'تم الحفظ.'; noteEdit.value=''; finalTranscript=''; }); clearBtn.addEventListener('click', ()=>{noteEdit.value=''; interimEl.textContent='اضغط "ابدأ التسجيل" ثم تحدث — ستظهر الملاحظات هنا.'}); // rendering function renderNotes(filterTerm=''){ notesList.innerHTML=''; const filtered = notes.filter(n=>n.text.includes(filterTerm)); if(filtered.length===0){ notesList.innerHTML='
لا توجد ملاحظات.
'; return } for(const n of filtered){ const card = document.createElement('div'); card.className='note'; const meta = document.createElement('div'); meta.className='meta'; meta.innerHTML = `
${n.created_at}
#${n.id}
`; const p = document.createElement('p'); p.textContent = n.text; const actions = document.createElement('div'); actions.className='actions'; const play = document.createElement('button'); play.className='small btn-ghost'; play.textContent='استماع'; play.onclick = ()=>{ if(n.audioURL){ const a = new Audio(n.audioURL); a.play(); } else alert('لا يوجد تسجيل صوتي لهذه الملاحظة.'); } document.createElement('a'); a.href = url; a.download = 'voice-notes.json'; a.click(); URL.revokeObjectURL(url); }); // Accessibility / hint document.addEventListener('keydown', (e)=>{ if((e.ctrlKey||e.metaKey) && e.key==='k'){ e.preventDefault(); noteEdit.focus(); } }); // Helpful notice if SpeechRecognition not supported if(!window.SpeechRecognition && !window.webkitSpeechRecognition){ // switch label const notice = document.createElement('div'); notice.style.color='var(--muted)'; notice.style.marginTop='8px'; notice.textContent = 'تنبيه: ميزة التحويل الصوتي الفوري غير مدعومة في متصفحك. نوصي باستخدام Chrome أو Edge للتعرّف على الكلام.'; document.querySelector('.card').appendChild(notice); }