AnesKAM commited on
Commit
a64dfa7
·
verified ·
1 Parent(s): ffa8d0c

Update results.html

Browse files
Files changed (1) hide show
  1. results.html +421 -206
results.html CHANGED
@@ -68,8 +68,8 @@
68
  .brand:hover{transform:scale(1.03)}
69
  .brand-icon{width:36px;height:36px;background:linear-gradient(135deg,var(--accent),var(--accent-2));border-radius:10px;display:flex;align-items:center;justify-content:center;color:#fff;font-size:15px;flex-shrink:0;box-shadow:0 4px 14px var(--accent-glow)}
70
  .search-container{flex:1;max-width:580px;width:100%}
71
-
72
- /* ========== صندوق البحث المحسن ========== */
73
  .search-bar{
74
  display:flex;align-items:center;background:var(--bg-input);border:2px solid var(--border);
75
  border-radius:var(--radius-pill);padding:4px 4px 4px 16px;transition:var(--transition);width:100%;
@@ -87,10 +87,39 @@
87
  .search-bar input::placeholder{color:var(--text-faint);opacity:0.7}
88
  .search-bar .clear-btn{
89
  background:none;border:none;color:var(--text-faint);cursor:pointer;
90
- padding:6px 10px;font-size:14px;display:none;transition:color 0.2s;
91
  }
92
  .search-bar .clear-btn:hover{color:var(--accent)}
93
  .search-bar .clear-btn.visible{display:block}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  .search-btn{
95
  background:linear-gradient(135deg,var(--accent),var(--accent-2));border:none;color:#fff;cursor:pointer;
96
  width:40px;height:36px;border-radius:var(--radius-pill);display:flex;align-items:center;
@@ -99,7 +128,10 @@
99
  }
100
  .search-btn:hover{opacity:0.9;transform:scale(1.06);box-shadow:0 4px 14px var(--accent-glow)}
101
  .search-btn:active{transform:scale(0.95)}
102
-
 
 
 
103
  .nav-actions{display:flex;align-items:center;gap:8px}
104
  #theme-toggle{
105
  background:var(--bg-input);border:1.5px solid var(--border);color:var(--text-sub);cursor:pointer;
@@ -133,6 +165,10 @@
133
  #loading-text.spinning::before{content:'';display:inline-block;width:14px;height:14px;border:2px solid var(--border);border-top-color:var(--accent);border-radius:50%;animation:spin 0.8s linear infinite}
134
  @keyframes spin{to{transform:rotate(360deg)}}
135
 
 
 
 
 
136
  /* ========== الاقتراحات ========== */
137
  .suggestions-row{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:18px}
138
  .suggestion-chip{
@@ -198,6 +234,22 @@
198
  .section-title a{color:var(--accent);text-decoration:none;font-size:14px;font-weight:500;margin-right:auto;transition:color 0.2s}
199
  .section-title a:hover{color:var(--accent-2)}
200
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  /* ========== مساعد الذكاء الاصطناعي ========== */
202
  .sidebar{width:100%;order:2;margin-top:12px}
203
  .ai-widget-wrap{position:relative;border-radius:var(--radius-card)}
@@ -224,14 +276,22 @@
224
  .empty-state{text-align:center;padding:60px 20px;color:var(--text-sub)}
225
  .empty-state i{font-size:40px;margin-bottom:16px;color:var(--text-faint);display:block}
226
  .empty-state p{font-size:15px;margin-top:8px}
227
- .error-state{text-align:center;padding:40px 20px;color:var(--accent-2)}
228
- .error-state i{font-size:36px;margin-bottom:14px;display:block}
229
 
230
  /* ========== زر العودة للأعلى ========== */
231
  .back-to-top{position:fixed;bottom:30px;right:30px;z-index:999;width:46px;height:46px;border-radius:50%;background:var(--accent);color:#fff;display:flex;align-items:center;justify-content:center;font-size:18px;cursor:pointer;opacity:0;visibility:hidden;transition:0.3s;box-shadow:0 6px 20px var(--accent-glow);border:none}
232
  .back-to-top.show{opacity:1;visibility:visible}
233
  .back-to-top:hover{transform:translateY(-3px)}
234
 
 
 
 
 
 
 
 
 
 
 
235
  @media(max-width:767px){
236
  .navbar{top:10px;border-radius:16px;padding:10px 14px}
237
  .tabs-bar{top:125px}
@@ -254,12 +314,24 @@
254
 
255
  <nav class="navbar">
256
  <a class="brand" href="index.html"><div class="brand-icon"><i class="fa-solid fa-bolt"></i></div><span>SurfGO</span></a>
257
- <div class="search-container">
258
  <form class="search-bar" id="search-form">
259
  <input id="main-input" placeholder="ابحث في الويب..." type="text" autocomplete="off" />
260
  <button type="button" class="clear-btn" id="clear-btn" aria-label="مسح النص"><i class="fa-solid fa-times"></i></button>
261
- <button class="search-btn" type="submit" aria-label="بحث"><i class="fa-solid fa-magnifying-glass"></i></button>
 
 
 
 
262
  </form>
 
 
 
 
 
 
 
 
263
  </div>
264
  <div class="nav-actions"><button id="theme-toggle" aria-label="تغيير السمة"></button></div>
265
  </nav>
@@ -285,7 +357,14 @@
285
  </div>
286
  </div>
287
  <div id="suggestions-container"></div>
 
288
  <div id="results-container"></div>
 
 
 
 
 
 
289
  </main>
290
  <aside class="sidebar" id="sidebar">
291
  <div class="ai-widget-wrap"><div class="ai-glow-ring"></div>
@@ -301,6 +380,7 @@
301
  </div>
302
 
303
  <button class="back-to-top" id="backToTop" aria-label="العودة للأعلى"><i class="fa-solid fa-arrow-up"></i></button>
 
304
 
305
  <script>
306
  (() => {
@@ -308,7 +388,7 @@
308
  const GEMINI_API = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent";
309
  const GEMINI_KEY = "";
310
 
311
- // ========== إدارة السمة ==========
312
  let currentTheme = 'light';
313
  const themeBtn = document.getElementById('theme-toggle');
314
  const setTheme = (t) => {
@@ -319,63 +399,56 @@
319
  setTheme('light');
320
  themeBtn.addEventListener('click', () => setTheme(currentTheme === 'dark' ? 'light' : 'dark'));
321
 
322
- // ========== المتغيرات العامة ==========
323
  const params = new URLSearchParams(window.location.search);
324
  let query = params.get('q') || '';
325
  let currentTab = params.get('tab') || 'all';
326
- const inputEl = document.getElementById('main-input');
327
- const resultsEl = document.getElementById('results-container');
328
- const loadingEl = document.getElementById('loading-text');
329
- const sidebarEl = document.getElementById('sidebar');
330
- const aiMobileWrap = document.getElementById('ai-mobile-wrap');
331
- const suggestionsEl = document.getElementById('suggestions-container');
332
- const clearBtn = document.getElementById('clear-btn');
333
 
334
- // ========== إدارة التبويبات ==========
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
335
  const setActiveTab = (tab) => {
336
  document.querySelectorAll('.tab-pill').forEach(b => b.classList.toggle('active', b.dataset.tab === tab));
337
  };
338
  setActiveTab(currentTab);
339
 
340
- // ========== صندوق البحث (زر المسح) ==========
341
  const updateClearBtn = () => {
342
- if (inputEl.value.length > 0) {
343
- clearBtn.classList.add('visible');
344
- } else {
345
- clearBtn.classList.remove('visible');
346
- }
347
  };
348
  inputEl.addEventListener('input', updateClearBtn);
349
- clearBtn.addEventListener('click', () => {
350
- inputEl.value = '';
351
- updateClearBtn();
352
- inputEl.focus();
353
- });
354
-
355
- // مفتاح Escape لمسح حقل البحث
356
  inputEl.addEventListener('keydown', (e) => {
357
- if (e.key === 'Escape' && inputEl.value.length > 0) {
358
- inputEl.value = '';
359
- updateClearBtn();
360
- inputEl.blur();
361
- }
362
  });
363
 
364
- // ========== دوال مساعدة ==========
365
  const esc = (s) => s ? s.replace(/[&<>'"]/g, m => ({'&':'&amp;','<':'&lt;','>':'&gt;',"'":'&#39;','"':'&quot;'}[m]||m)) : '';
366
  const getDomain = (url) => { try { return new URL(url).hostname.replace('www.',''); } catch { return url||''; } };
367
  const proxyImg = (url) => url && !url.startsWith('data:') ? `https://wsrv.nl/?url=${encodeURIComponent(url)}&output=webp` : url;
368
  const faviconURL = (d) => `https://www.google.com/s2/favicons?domain=${d}&sz=32`;
369
  const extractBestImage = (r) => {
370
- const candidates = [r.img_src, r.thumbnail_src, r.thumbnail, r.image];
371
- for (const c of candidates) {
372
- if (c && typeof c === 'string' && c.match(/\.(jpg|jpeg|png|gif|webp|bmp)/i)) return c;
373
  if (c && typeof c === 'string' && c.startsWith('http')) return c;
374
  }
375
  return null;
376
  };
377
-
378
- // قص النص بعد عدد معين من الكلمات
379
  const truncateWords = (text, maxWords) => {
380
  if (!text) return '';
381
  const words = text.split(/\s+/).filter(w => w.length > 0);
@@ -383,7 +456,75 @@
383
  return words.slice(0, maxWords).join(' ') + '...';
384
  };
385
 
386
- // ========== الذكاء الاصطناعي ==========
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
387
  let currentAItext = '';
388
  const aiSetLoading = () => {
389
  ['ai-answer-body','ai-answer-body-mobile'].forEach(id => { const el=document.getElementById(id); if(el){el.classList.add('loading');el.innerHTML='<div class="ai-skeleton"></div><div class="ai-skeleton"></div><div class="ai-skeleton"></div>'} });
@@ -394,7 +535,7 @@
394
  currentAItext = text;
395
  ['ai-answer-body','ai-answer-body-mobile'].forEach(id => { const el=document.getElementById(id); if(el){el.classList.remove('loading');el.textContent=text} });
396
  ['ai-copy-desktop','ai-copy-mobile'].forEach(id => { const b=document.getElementById(id); if(b)b.style.display='inline-flex' });
397
- const chips = sources.slice(0,4).map(s => `<a class="ai-source-chip" href="${s.url}" target="_blank"><i class="fa-solid fa-link"></i>${esc(getDomain(s.url))}</a>`).join('');
398
  ['ai-sources-desktop','ai-sources-mobile'].forEach(id => { const el=document.getElementById(id); if(el)el.innerHTML=chips });
399
  };
400
  const runAI = async (q, results) => {
@@ -406,7 +547,7 @@
406
  const data = await resp.json();
407
  aiUpdate(data.candidates?.[0]?.content?.parts?.[0]?.text||'لا توجد إجابة.', results.slice(0,4));
408
  return;
409
- }catch(e){}
410
  }
411
  const best = results[0];
412
  const summary = best ? truncateWords(best.content, 35) : '';
@@ -417,138 +558,125 @@
417
  if(aiMobileWrap) aiMobileWrap.style.display = visible ? '' : 'none';
418
  };
419
 
420
- // ========== معالجة أخطاء الصور ==========
421
- window.handleImageError = function(img) {
422
- const card = img.closest('.image-card');
423
- if (card) card.remove();
424
- };
425
- window.handleVideoThumbError = function(img) {
426
- const card = img.closest('.video-card');
427
- if (card) card.remove();
428
- };
429
-
430
- // ========== مراقب الظهور ==========
431
- const observer = new IntersectionObserver(entries => entries.forEach(en => { if(en.isIntersecting) en.target.classList.add('visible') }), {threshold:0.1});
432
- const observe = () => document.querySelectorAll('.search-result,.image-card,.video-card').forEach(el => observer.observe(el));
433
 
434
- // ========== زر العودة للأعلى ==========
435
  const backBtn = document.getElementById('backToTop');
436
  window.addEventListener('scroll', () => backBtn.classList.toggle('show', scrollY > 400));
437
  backBtn.addEventListener('click', () => scrollTo({top:0,behavior:'smooth'}));
438
 
439
- // ========== جلب البيانات ==========
440
  const fetchJSON = (url) => fetch(url, {signal: AbortSignal.timeout(12000)}).then(r=>r.json());
441
- const fetchCategory = (cat) => {
442
- const url = `${SEARXNG_URL}?q=${encodeURIComponent(query)}&format=json&categories=${cat}&language=ar&safesearch=1&limit=100`;
443
- return fetchJSON(url).catch(()=>({results:[], suggestions:[]}));
444
  };
445
 
446
- // ========== عرض الاقتراحات ==========
447
  const renderSuggestions = (suggestions) => {
448
  suggestionsEl.innerHTML = '';
449
- if (!suggestions || !suggestions.length) return;
450
- const row = document.createElement('div');
451
- row.className = 'suggestions-row';
452
  suggestions.forEach(s => {
453
- const chip = document.createElement('button');
454
- chip.className = 'suggestion-chip';
455
- chip.textContent = s;
456
- chip.addEventListener('click', () => {
457
- inputEl.value = s;
458
- updateClearBtn();
459
- document.getElementById('search-form').dispatchEvent(new Event('submit'));
460
- });
461
  row.appendChild(chip);
462
  });
463
  suggestionsEl.appendChild(row);
464
  };
465
 
466
- // ========== عناوين الأقسام ==========
467
- const createSectionTitle = (text, tabLink) => {
468
- const h3 = document.createElement('h3'); h3.className='section-title';
469
- h3.innerHTML = `${text} <a href="?q=${encodeURIComponent(query)}&tab=${tabLink}">عرض الكل <i class="fa-solid fa-arrow-left"></i></a>`;
470
- return h3;
471
  };
472
 
473
- // ========== عرض نتائج الويب ==========
474
- const renderWebResults = (results) => {
475
- results.forEach(r => {
476
- const d = getDomain(r.url);
477
- const art = document.createElement('article'); art.className='search-result';
478
- art.innerHTML = `
479
- <div class="favicon-wrap"><img src="${faviconURL(d)}" onerror="this.style.display='none';this.nextElementSibling.style.display='flex'" /><i class="fa-solid fa-globe fallback-icon" style="display:none"></i></div>
480
- <div class="result-content">
481
- <div class="result-domain"><i class="fa-solid fa-link"></i> ${esc(d)}</div>
482
- <a class="result-title" href="${esc(r.url)}" target="_blank" title="${esc(r.title||'')}">${esc(truncateWords(r.title||'بدون عنوان', 12))}</a>
483
- <p class="result-snippet">${esc(truncateWords(r.content||'', 30))}</p>
484
- </div>
485
- <button class="copy-link-btn" data-url="${esc(r.url)}" title="نسخ الرابط"><i class="fa-regular fa-copy"></i></button>
486
- `;
487
- resultsEl.appendChild(art);
488
- });
489
  };
490
 
491
- // ========== عرض الصور ==========
492
- const renderImageCards = (images) => {
493
- const validImages = images.filter(r => extractBestImage(r));
494
- if(!validImages.length) return;
495
- const title = createSectionTitle('صور','images');
496
- resultsEl.appendChild(title);
497
  const grid = document.createElement('div'); grid.className='images-grid';
498
- validImages.forEach(r => {
499
- const raw = extractBestImage(r);
500
- const proxied = proxyImg(raw);
501
- const displayUrl = r.url || '';
502
  const card = document.createElement('div'); card.className='image-card';
503
- card.innerHTML = `
504
- <a class="image-card-link" href="${esc(displayUrl)}" target="_blank">
505
  <img src="${esc(proxied)}" loading="lazy" onerror="handleImageError(this)" alt="${esc(r.title||'')}" />
506
  </a>
507
  <div class="image-info">
508
- <div class="img-title" title="${esc(r.title||'صورة')}">${esc(truncateWords(r.title||'صورة', 8))}</div>
509
- <a class="image-url" href="${esc(displayUrl)}" target="_blank" title="${esc(displayUrl)}">${esc(truncateWords(displayUrl, 6))}</a>
510
- </div>
511
- `;
512
  grid.appendChild(card);
513
  });
514
- resultsEl.appendChild(grid);
515
  };
516
 
517
- // ========== عرض الفيديوهات ==========
518
- const renderVideoCards = (videos) => {
519
- const validVideos = videos.filter(r => r.thumbnail);
520
- if(!validVideos.length) return;
521
- const title = createSectionTitle('فيديوهات','videos');
522
- resultsEl.appendChild(title);
523
  const grid = document.createElement('div'); grid.className='videos-grid';
524
- validVideos.forEach(r => {
525
  const thumb = proxyImg(r.thumbnail);
526
- const displayUrl = r.url || '';
527
  const card = document.createElement('div'); card.className='video-card';
528
- card.innerHTML = `
529
- <a href="${esc(displayUrl)}" target="_blank" class="video-thumb-wrap">
530
  <img src="${esc(thumb)}" loading="lazy" onerror="handleVideoThumbError(this)" alt="${esc(r.title||'')}" />
531
  <div class="play-overlay"><i class="fa-solid fa-play"></i></div>
532
  </a>
533
  <div class="video-details">
534
- <a class="video-title" href="${esc(displayUrl)}" target="_blank" title="${esc(r.title||'فيديو')}">${esc(truncateWords(r.title||'فيديو', 10))}</a>
535
- <a class="video-url" href="${esc(displayUrl)}" target="_blank" title="${esc(displayUrl)}">${esc(truncateWords(displayUrl, 8))}</a>
536
- </div>
537
- `;
538
  grid.appendChild(card);
539
  });
540
- resultsEl.appendChild(grid);
541
  };
542
 
543
- // ========== البحث الرئيسي ==========
544
  const performSearch = async (q, tab) => {
 
545
  resultsEl.innerHTML = '';
546
  suggestionsEl.innerHTML = '';
547
- loadingEl.style.display = 'flex'; loadingEl.classList.add('spinning');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
548
  showAI(tab === 'all');
549
 
 
 
 
 
 
550
  try {
551
  if (tab === 'all') {
 
552
  const [imagesData, videosData] = await Promise.all([
553
  fetchCategory('images'),
554
  fetchCategory('videos')
@@ -557,87 +685,172 @@
557
  const suggestions = imagesData.suggestions || videosData.suggestions || [];
558
  if (suggestions.length) renderSuggestions(suggestions);
559
 
560
- const imageResults = (imagesData.results || []).slice(0, 3);
561
- if (imageResults.length) renderImageCards(imageResults);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
562
 
563
- const videoResults = (videosData.results || []).slice(0, 2);
564
- if (videoResults.length) renderVideoCards(videoResults);
 
 
 
565
 
566
- const generalData = await fetchCategory('general');
567
- const webResults = generalData.results || [];
568
  loadingEl.style.display = 'none';
 
569
 
570
- if (webResults.length) {
571
- renderWebResults(webResults);
572
- runAI(q, webResults);
573
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
574
  observe();
575
- } else {
576
- const data = await fetchCategory(tab);
577
- const results = data.results || [];
578
- const suggestions = data.suggestions || [];
579
- loadingEl.style.display = 'none';
580
 
581
- if (suggestions.length) renderSuggestions(suggestions);
582
- if (!results.length) { resultsEl.innerHTML = ''; return; }
583
-
584
- if (tab === 'images') {
585
- const validImages = results.filter(r => extractBestImage(r));
586
- if (validImages.length) {
587
- const grid = document.createElement('div'); grid.className='images-grid';
588
- validImages.forEach(r => {
589
- const raw = extractBestImage(r);
590
- const proxied = proxyImg(raw);
591
- const displayUrl = r.url || '';
592
- const card = document.createElement('div'); card.className='image-card';
593
- card.innerHTML = `
594
- <a class="image-card-link" href="${esc(displayUrl)}" target="_blank">
595
- <img src="${esc(proxied)}" loading="lazy" onerror="handleImageError(this)" alt="${esc(r.title||'')}" />
596
- </a>
597
- <div class="image-info">
598
- <div class="img-title" title="${esc(r.title||'صورة')}">${esc(truncateWords(r.title||'صورة', 8))}</div>
599
- <a class="image-url" href="${esc(displayUrl)}" target="_blank" title="${esc(displayUrl)}">${esc(truncateWords(displayUrl, 6))}</a>
600
- </div>
601
- `;
602
- grid.appendChild(card);
603
- });
604
- resultsEl.appendChild(grid);
605
- }
606
- } else if (tab === 'videos') {
607
- const validVideos = results.filter(r => r.thumbnail);
608
- if (validVideos.length) {
609
- const grid = document.createElement('div'); grid.className='videos-grid';
610
- validVideos.forEach(r => {
611
- const thumb = proxyImg(r.thumbnail);
612
- const displayUrl = r.url || '';
613
- const card = document.createElement('div'); card.className='video-card';
614
- card.innerHTML = `
615
- <a href="${esc(displayUrl)}" target="_blank" class="video-thumb-wrap">
616
- <img src="${esc(thumb)}" loading="lazy" onerror="handleVideoThumbError(this)" alt="${esc(r.title||'')}" />
617
- <div class="play-overlay"><i class="fa-solid fa-play"></i></div>
618
- </a>
619
- <div class="video-details">
620
- <a class="video-title" href="${esc(displayUrl)}" target="_blank" title="${esc(r.title||'فيديو')}">${esc(truncateWords(r.title||'فيديو', 10))}</a>
621
- <a class="video-url" href="${esc(displayUrl)}" target="_blank" title="${esc(displayUrl)}">${esc(truncateWords(displayUrl, 8))}</a>
622
- </div>
623
- `;
624
- grid.appendChild(card);
625
- });
626
- resultsEl.appendChild(grid);
627
- }
628
  } else {
629
- renderWebResults(results);
630
  }
631
- observe();
632
  }
 
633
  } catch(e) {
634
- loadingEl.style.display = 'none';
635
- resultsEl.innerHTML = '';
636
- suggestionsEl.innerHTML = '';
637
  }
638
  };
639
 
640
- // ========== التهيئة الأولية ==========
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
641
  if (query) {
642
  inputEl.value = query;
643
  updateClearBtn();
@@ -646,9 +859,11 @@
646
  loadingEl.style.display = 'block';
647
  loadingEl.classList.remove('spinning');
648
  loadingEl.textContent = 'أدخل كلمة البحث للبدء.';
 
 
649
  }
650
 
651
- // ========== مستمعو الأحداث ==========
652
  document.getElementById('search-form').addEventListener('submit', e => {
653
  e.preventDefault();
654
  const q = inputEl.value.trim();
@@ -669,16 +884,16 @@
669
  });
670
  });
671
 
672
- document.getElementById('ai-copy-desktop')?.addEventListener('click', ()=>navigator.clipboard.writeText(currentAItext));
673
- document.getElementById('ai-copy-mobile')?.addEventListener('click', ()=>navigator.clipboard.writeText(currentAItext));
674
 
675
- // نسخ رابط النتيجة
676
  document.addEventListener('click', (e) => {
677
- if (e.target.classList.contains('copy-link-btn')) {
678
- navigator.clipboard.writeText(e.target.dataset.url).then(() => {
679
- e.target.innerHTML = '<i class="fa-solid fa-check"></i>';
680
- setTimeout(() => { e.target.innerHTML = '<i class="fa-regular fa-copy"></i>'; }, 1500);
681
- }).catch(() => {});
 
682
  }
683
  });
684
 
 
68
  .brand:hover{transform:scale(1.03)}
69
  .brand-icon{width:36px;height:36px;background:linear-gradient(135deg,var(--accent),var(--accent-2));border-radius:10px;display:flex;align-items:center;justify-content:center;color:#fff;font-size:15px;flex-shrink:0;box-shadow:0 4px 14px var(--accent-glow)}
70
  .search-container{flex:1;max-width:580px;width:100%}
71
+
72
+ /* ========== صندوق البحث ========== */
73
  .search-bar{
74
  display:flex;align-items:center;background:var(--bg-input);border:2px solid var(--border);
75
  border-radius:var(--radius-pill);padding:4px 4px 4px 16px;transition:var(--transition);width:100%;
 
87
  .search-bar input::placeholder{color:var(--text-faint);opacity:0.7}
88
  .search-bar .clear-btn{
89
  background:none;border:none;color:var(--text-faint);cursor:pointer;
90
+ padding:6px 8px;font-size:14px;display:none;transition:color 0.2s;
91
  }
92
  .search-bar .clear-btn:hover{color:var(--accent)}
93
  .search-bar .clear-btn.visible{display:block}
94
+
95
+ /* ========== زر البحث الصوتي ========== */
96
+ .voice-btn{
97
+ background:none;border:none;color:var(--text-faint);cursor:pointer;
98
+ padding:6px 10px;font-size:15px;transition:var(--transition);position:relative;
99
+ display:flex;align-items:center;justify-content:center;
100
+ }
101
+ .voice-btn:hover{color:var(--accent)}
102
+ .voice-btn.listening{color:#e0427a;animation:pulse-mic 1s ease-in-out infinite}
103
+ @keyframes pulse-mic{0%,100%{transform:scale(1)}50%{transform:scale(1.25)}}
104
+
105
+ /* مؤشر الاستماع */
106
+ .voice-listening-indicator{
107
+ display:none;position:absolute;top:calc(100% + 10px);left:50%;transform:translateX(-50%);
108
+ background:var(--bg-card);border:2px solid var(--accent);border-radius:var(--radius-pill);
109
+ padding:8px 20px;font-size:13px;color:var(--accent);white-space:nowrap;
110
+ box-shadow:var(--shadow-hover);z-index:999;
111
+ align-items:center;gap:8px;
112
+ }
113
+ .voice-listening-indicator.show{display:flex}
114
+ .voice-waves{display:flex;gap:3px;align-items:center;height:16px}
115
+ .voice-wave{width:3px;background:var(--accent);border-radius:2px;animation:wave-bar 0.8s ease-in-out infinite}
116
+ .voice-wave:nth-child(1){height:6px;animation-delay:0s}
117
+ .voice-wave:nth-child(2){height:12px;animation-delay:0.15s}
118
+ .voice-wave:nth-child(3){height:16px;animation-delay:0.3s}
119
+ .voice-wave:nth-child(4){height:10px;animation-delay:0.45s}
120
+ .voice-wave:nth-child(5){height:6px;animation-delay:0.6s}
121
+ @keyframes wave-bar{0%,100%{transform:scaleY(0.5)}50%{transform:scaleY(1)}}
122
+
123
  .search-btn{
124
  background:linear-gradient(135deg,var(--accent),var(--accent-2));border:none;color:#fff;cursor:pointer;
125
  width:40px;height:36px;border-radius:var(--radius-pill);display:flex;align-items:center;
 
128
  }
129
  .search-btn:hover{opacity:0.9;transform:scale(1.06);box-shadow:0 4px 14px var(--accent-glow)}
130
  .search-btn:active{transform:scale(0.95)}
131
+ /* حالة تحميل زر البحث */
132
+ .search-btn.loading-btn{pointer-events:none;opacity:0.7}
133
+ .search-btn.loading-btn i{animation:spin 0.8s linear infinite}
134
+
135
  .nav-actions{display:flex;align-items:center;gap:8px}
136
  #theme-toggle{
137
  background:var(--bg-input);border:1.5px solid var(--border);color:var(--text-sub);cursor:pointer;
 
165
  #loading-text.spinning::before{content:'';display:inline-block;width:14px;height:14px;border:2px solid var(--border);border-top-color:var(--accent);border-radius:50%;animation:spin 0.8s linear infinite}
166
  @keyframes spin{to{transform:rotate(360deg)}}
167
 
168
+ /* ========== عداد النتائج ========== */
169
+ .results-count{font-size:12px;color:var(--text-faint);margin-bottom:14px;display:flex;align-items:center;gap:6px}
170
+ .results-count span{color:var(--accent);font-weight:600}
171
+
172
  /* ========== الاقتراحات ========== */
173
  .suggestions-row{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:18px}
174
  .suggestion-chip{
 
234
  .section-title a{color:var(--accent);text-decoration:none;font-size:14px;font-weight:500;margin-right:auto;transition:color 0.2s}
235
  .section-title a:hover{color:var(--accent-2)}
236
 
237
+ /* ========== مؤشر تحميل المزيد ========== */
238
+ .load-more-spinner{
239
+ display:none;text-align:center;padding:24px;color:var(--text-faint);font-size:13px;
240
+ align-items:center;justify-content:center;gap:8px;
241
+ }
242
+ .load-more-spinner.show{display:flex}
243
+ .load-more-spinner::before{content:'';width:18px;height:18px;border:2px solid var(--border);border-top-color:var(--accent);border-radius:50%;animation:spin 0.8s linear infinite;flex-shrink:0}
244
+
245
+ /* ========== نهاية النتائج ========== */
246
+ .end-of-results{
247
+ text-align:center;padding:30px 20px;color:var(--text-faint);font-size:13px;
248
+ display:none;align-items:center;justify-content:center;gap:8px;
249
+ }
250
+ .end-of-results.show{display:flex}
251
+ .end-divider{height:1px;background:var(--border);flex:1;max-width:80px}
252
+
253
  /* ========== مساعد الذكاء الاصطناعي ========== */
254
  .sidebar{width:100%;order:2;margin-top:12px}
255
  .ai-widget-wrap{position:relative;border-radius:var(--radius-card)}
 
276
  .empty-state{text-align:center;padding:60px 20px;color:var(--text-sub)}
277
  .empty-state i{font-size:40px;margin-bottom:16px;color:var(--text-faint);display:block}
278
  .empty-state p{font-size:15px;margin-top:8px}
 
 
279
 
280
  /* ========== زر العودة للأعلى ========== */
281
  .back-to-top{position:fixed;bottom:30px;right:30px;z-index:999;width:46px;height:46px;border-radius:50%;background:var(--accent);color:#fff;display:flex;align-items:center;justify-content:center;font-size:18px;cursor:pointer;opacity:0;visibility:hidden;transition:0.3s;box-shadow:0 6px 20px var(--accent-glow);border:none}
282
  .back-to-top.show{opacity:1;visibility:visible}
283
  .back-to-top:hover{transform:translateY(-3px)}
284
 
285
+ /* ========== تنبيه عدم دعم الصوت ========== */
286
+ .voice-unsupported-toast{
287
+ position:fixed;bottom:90px;left:50%;transform:translateX(-50%);
288
+ background:var(--bg-card);border:1.5px solid var(--accent-2);color:var(--accent-2);
289
+ padding:10px 20px;border-radius:var(--radius-pill);font-size:13px;
290
+ box-shadow:var(--shadow-hover);z-index:2000;opacity:0;pointer-events:none;
291
+ transition:opacity 0.3s;
292
+ }
293
+ .voice-unsupported-toast.show{opacity:1}
294
+
295
  @media(max-width:767px){
296
  .navbar{top:10px;border-radius:16px;padding:10px 14px}
297
  .tabs-bar{top:125px}
 
314
 
315
  <nav class="navbar">
316
  <a class="brand" href="index.html"><div class="brand-icon"><i class="fa-solid fa-bolt"></i></div><span>SurfGO</span></a>
317
+ <div class="search-container" style="position:relative">
318
  <form class="search-bar" id="search-form">
319
  <input id="main-input" placeholder="ابحث في الويب..." type="text" autocomplete="off" />
320
  <button type="button" class="clear-btn" id="clear-btn" aria-label="مسح النص"><i class="fa-solid fa-times"></i></button>
321
+ <!-- زر البحث الصوتي الجديد -->
322
+ <button type="button" class="voice-btn" id="voice-btn" aria-label="بحث صوتي" title="بحث صوتي">
323
+ <i class="fa-solid fa-microphone"></i>
324
+ </button>
325
+ <button class="search-btn" id="search-submit-btn" type="submit" aria-label="بحث"><i class="fa-solid fa-magnifying-glass"></i></button>
326
  </form>
327
+ <!-- مؤشر الاستماع -->
328
+ <div class="voice-listening-indicator" id="voice-indicator">
329
+ <div class="voice-waves">
330
+ <div class="voice-wave"></div><div class="voice-wave"></div><div class="voice-wave"></div>
331
+ <div class="voice-wave"></div><div class="voice-wave"></div>
332
+ </div>
333
+ جاري الاستماع…
334
+ </div>
335
  </div>
336
  <div class="nav-actions"><button id="theme-toggle" aria-label="تغيير السمة"></button></div>
337
  </nav>
 
357
  </div>
358
  </div>
359
  <div id="suggestions-container"></div>
360
+ <div id="results-count-bar"></div>
361
  <div id="results-container"></div>
362
+ <div class="load-more-spinner" id="load-more-spinner">جاري تحميل المزيد…</div>
363
+ <div class="end-of-results" id="end-of-results">
364
+ <div class="end-divider"></div>
365
+ <span><i class="fa-solid fa-check-circle" style="color:var(--accent);margin-left:5px"></i> تم عرض جميع النتائج</span>
366
+ <div class="end-divider"></div>
367
+ </div>
368
  </main>
369
  <aside class="sidebar" id="sidebar">
370
  <div class="ai-widget-wrap"><div class="ai-glow-ring"></div>
 
380
  </div>
381
 
382
  <button class="back-to-top" id="backToTop" aria-label="العودة للأعلى"><i class="fa-solid fa-arrow-up"></i></button>
383
+ <div class="voice-unsupported-toast" id="voice-toast">متصفحك لا يدعم البحث الصوتي</div>
384
 
385
  <script>
386
  (() => {
 
388
  const GEMINI_API = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent";
389
  const GEMINI_KEY = "";
390
 
391
+ // ===== إدارة السمة =====
392
  let currentTheme = 'light';
393
  const themeBtn = document.getElementById('theme-toggle');
394
  const setTheme = (t) => {
 
399
  setTheme('light');
400
  themeBtn.addEventListener('click', () => setTheme(currentTheme === 'dark' ? 'light' : 'dark'));
401
 
402
+ // ===== المتغيرات العامة =====
403
  const params = new URLSearchParams(window.location.search);
404
  let query = params.get('q') || '';
405
  let currentTab = params.get('tab') || 'all';
 
 
 
 
 
 
 
406
 
407
+ const inputEl = document.getElementById('main-input');
408
+ const resultsEl = document.getElementById('results-container');
409
+ const loadingEl = document.getElementById('loading-text');
410
+ const sidebarEl = document.getElementById('sidebar');
411
+ const aiMobileWrap = document.getElementById('ai-mobile-wrap');
412
+ const suggestionsEl= document.getElementById('suggestions-container');
413
+ const clearBtn = document.getElementById('clear-btn');
414
+ const submitBtn = document.getElementById('search-submit-btn');
415
+ const countBar = document.getElementById('results-count-bar');
416
+ const loadMoreEl = document.getElementById('load-more-spinner');
417
+ const endEl = document.getElementById('end-of-results');
418
+
419
+ // ===== Infinite Scroll State =====
420
+ let allWebResults = []; // كل نتائج الويب المجلوبة
421
+ let renderedCount = 0; // عدد ما تم عرضه
422
+ const PAGE_SIZE = 10; // كم نتيجة نعرض في كل دفعة
423
+ let isRendering = false;
424
+
425
+ // ===== إدارة التبويبات =====
426
  const setActiveTab = (tab) => {
427
  document.querySelectorAll('.tab-pill').forEach(b => b.classList.toggle('active', b.dataset.tab === tab));
428
  };
429
  setActiveTab(currentTab);
430
 
431
+ // ===== زر المسح =====
432
  const updateClearBtn = () => {
433
+ clearBtn.classList.toggle('visible', inputEl.value.length > 0);
 
 
 
 
434
  };
435
  inputEl.addEventListener('input', updateClearBtn);
436
+ clearBtn.addEventListener('click', () => { inputEl.value = ''; updateClearBtn(); inputEl.focus(); });
 
 
 
 
 
 
437
  inputEl.addEventListener('keydown', (e) => {
438
+ if (e.key === 'Escape' && inputEl.value.length > 0) { inputEl.value = ''; updateClearBtn(); inputEl.blur(); }
 
 
 
 
439
  });
440
 
441
+ // ===== دوال مساعدة =====
442
  const esc = (s) => s ? s.replace(/[&<>'"]/g, m => ({'&':'&amp;','<':'&lt;','>':'&gt;',"'":'&#39;','"':'&quot;'}[m]||m)) : '';
443
  const getDomain = (url) => { try { return new URL(url).hostname.replace('www.',''); } catch { return url||''; } };
444
  const proxyImg = (url) => url && !url.startsWith('data:') ? `https://wsrv.nl/?url=${encodeURIComponent(url)}&output=webp` : url;
445
  const faviconURL = (d) => `https://www.google.com/s2/favicons?domain=${d}&sz=32`;
446
  const extractBestImage = (r) => {
447
+ for (const c of [r.img_src, r.thumbnail_src, r.thumbnail, r.image]) {
 
 
448
  if (c && typeof c === 'string' && c.startsWith('http')) return c;
449
  }
450
  return null;
451
  };
 
 
452
  const truncateWords = (text, maxWords) => {
453
  if (!text) return '';
454
  const words = text.split(/\s+/).filter(w => w.length > 0);
 
456
  return words.slice(0, maxWords).join(' ') + '...';
457
  };
458
 
459
+ // ===== Intersection Observer للظهور =====
460
+ const observer = new IntersectionObserver(entries => entries.forEach(en => {
461
+ if (en.isIntersecting) en.target.classList.add('visible');
462
+ }), { threshold: 0.08 });
463
+ const observe = () => document.querySelectorAll('.search-result:not(.visible),.image-card:not(.visible),.video-card:not(.visible)').forEach(el => observer.observe(el));
464
+
465
+ // ===== Sentinel للـ Infinite Scroll =====
466
+ let sentinel = null;
467
+ const scrollObserver = new IntersectionObserver(entries => {
468
+ if (entries[0].isIntersecting && !isRendering) renderNextBatch();
469
+ }, { rootMargin: '300px' });
470
+
471
+ const attachSentinel = () => {
472
+ if (sentinel) sentinel.remove();
473
+ sentinel = document.createElement('div');
474
+ sentinel.id = 'scroll-sentinel';
475
+ resultsEl.appendChild(sentinel);
476
+ scrollObserver.observe(sentinel);
477
+ };
478
+
479
+ const detachSentinel = () => {
480
+ if (sentinel) { scrollObserver.unobserve(sentinel); sentinel.remove(); sentinel = null; }
481
+ };
482
+
483
+ // ===== عرض دفعة من نتائج الويب =====
484
+ const renderNextBatch = () => {
485
+ if (renderedCount >= allWebResults.length) {
486
+ detachSentinel();
487
+ loadMoreEl.classList.remove('show');
488
+ endEl.classList.add('show');
489
+ return;
490
+ }
491
+ isRendering = true;
492
+ loadMoreEl.classList.add('show');
493
+
494
+ const batch = allWebResults.slice(renderedCount, renderedCount + PAGE_SIZE);
495
+ batch.forEach(r => {
496
+ const d = getDomain(r.url);
497
+ const art = document.createElement('article'); art.className = 'search-result';
498
+ art.innerHTML = `
499
+ <div class="favicon-wrap">
500
+ <img src="${faviconURL(d)}" onerror="this.style.display='none';this.nextElementSibling.style.display='flex'" />
501
+ <i class="fa-solid fa-globe fallback-icon" style="display:none"></i>
502
+ </div>
503
+ <div class="result-content">
504
+ <div class="result-domain"><i class="fa-solid fa-link"></i> ${esc(d)}</div>
505
+ <a class="result-title" href="${esc(r.url)}" target="_blank" title="${esc(r.title||'')}">${esc(truncateWords(r.title||'بدون عنوان', 14))}</a>
506
+ <p class="result-snippet">${esc(truncateWords(r.content||'', 35))}</p>
507
+ </div>
508
+ <button class="copy-link-btn" data-url="${esc(r.url)}" title="نسخ الرابط"><i class="fa-regular fa-copy"></i></button>
509
+ `;
510
+ resultsEl.appendChild(art);
511
+ });
512
+
513
+ renderedCount += batch.length;
514
+ observe();
515
+
516
+ setTimeout(() => {
517
+ loadMoreEl.classList.remove('show');
518
+ isRendering = false;
519
+ if (renderedCount < allWebResults.length) {
520
+ attachSentinel();
521
+ } else {
522
+ endEl.classList.add('show');
523
+ }
524
+ }, 400);
525
+ };
526
+
527
+ // ===== الذكاء الاصطناعي =====
528
  let currentAItext = '';
529
  const aiSetLoading = () => {
530
  ['ai-answer-body','ai-answer-body-mobile'].forEach(id => { const el=document.getElementById(id); if(el){el.classList.add('loading');el.innerHTML='<div class="ai-skeleton"></div><div class="ai-skeleton"></div><div class="ai-skeleton"></div>'} });
 
535
  currentAItext = text;
536
  ['ai-answer-body','ai-answer-body-mobile'].forEach(id => { const el=document.getElementById(id); if(el){el.classList.remove('loading');el.textContent=text} });
537
  ['ai-copy-desktop','ai-copy-mobile'].forEach(id => { const b=document.getElementById(id); if(b)b.style.display='inline-flex' });
538
+ const chips = sources.slice(0,4).map(s=>`<a class="ai-source-chip" href="${s.url}" target="_blank"><i class="fa-solid fa-link"></i>${esc(getDomain(s.url))}</a>`).join('');
539
  ['ai-sources-desktop','ai-sources-mobile'].forEach(id => { const el=document.getElementById(id); if(el)el.innerHTML=chips });
540
  };
541
  const runAI = async (q, results) => {
 
547
  const data = await resp.json();
548
  aiUpdate(data.candidates?.[0]?.content?.parts?.[0]?.text||'لا توجد إجابة.', results.slice(0,4));
549
  return;
550
+ } catch(e){}
551
  }
552
  const best = results[0];
553
  const summary = best ? truncateWords(best.content, 35) : '';
 
558
  if(aiMobileWrap) aiMobileWrap.style.display = visible ? '' : 'none';
559
  };
560
 
561
+ // ===== معالجة أخطاء الصور =====
562
+ window.handleImageError = function(img) { const c=img.closest('.image-card'); if(c)c.remove(); };
563
+ window.handleVideoThumbError = function(img) { const c=img.closest('.video-card'); if(c)c.remove(); };
 
 
 
 
 
 
 
 
 
 
564
 
565
+ // ===== زر العودة للأعلى =====
566
  const backBtn = document.getElementById('backToTop');
567
  window.addEventListener('scroll', () => backBtn.classList.toggle('show', scrollY > 400));
568
  backBtn.addEventListener('click', () => scrollTo({top:0,behavior:'smooth'}));
569
 
570
+ // ===== جلب البيانات =====
571
  const fetchJSON = (url) => fetch(url, {signal: AbortSignal.timeout(12000)}).then(r=>r.json());
572
+ const fetchCategory = (cat, pageno=1) => {
573
+ const url = `${SEARXNG_URL}?q=${encodeURIComponent(query)}&format=json&categories=${cat}&language=ar&safesearch=1&pageno=${pageno}`;
574
+ return fetchJSON(url).catch(()=>({results:[],suggestions:[]}));
575
  };
576
 
577
+ // ===== عرض الاقتراحات =====
578
  const renderSuggestions = (suggestions) => {
579
  suggestionsEl.innerHTML = '';
580
+ if (!suggestions?.length) return;
581
+ const row = document.createElement('div'); row.className='suggestions-row';
 
582
  suggestions.forEach(s => {
583
+ const chip = document.createElement('button'); chip.className='suggestion-chip'; chip.textContent=s;
584
+ chip.addEventListener('click', () => { inputEl.value=s; updateClearBtn(); document.getElementById('search-form').dispatchEvent(new Event('submit')); });
 
 
 
 
 
 
585
  row.appendChild(chip);
586
  });
587
  suggestionsEl.appendChild(row);
588
  };
589
 
590
+ // ===== عرض عداد النتائج =====
591
+ const renderCount = (count) => {
592
+ countBar.innerHTML = count > 0
593
+ ? `<div class="results-count"><i class="fa-solid fa-circle-info"></i> تم العثور على <span>${count}</span> نتيجة</div>`
594
+ : '';
595
  };
596
 
597
+ // ===== عناوين الأقسام =====
598
+ const createSectionTitle = (text, tabLink) => {
599
+ const h3=document.createElement('h3'); h3.className='section-title';
600
+ h3.innerHTML=`${text} <a href="?q=${encodeURIComponent(query)}&tab=${tabLink}">عرض الكل <i class="fa-solid fa-arrow-left"></i></a>`;
601
+ return h3;
 
 
 
 
 
 
 
 
 
 
 
602
  };
603
 
604
+ // ===== عرض الصور =====
605
+ const renderImageGrid = (images, container, withTitle=false) => {
606
+ const valid = images.filter(r => extractBestImage(r));
607
+ if (!valid.length) return;
608
+ if (withTitle) container.appendChild(createSectionTitle('صور','images'));
 
609
  const grid = document.createElement('div'); grid.className='images-grid';
610
+ valid.forEach(r => {
611
+ const proxied = proxyImg(extractBestImage(r));
 
 
612
  const card = document.createElement('div'); card.className='image-card';
613
+ card.innerHTML=`
614
+ <a class="image-card-link" href="${esc(r.url||'')}" target="_blank">
615
  <img src="${esc(proxied)}" loading="lazy" onerror="handleImageError(this)" alt="${esc(r.title||'')}" />
616
  </a>
617
  <div class="image-info">
618
+ <div class="img-title" title="${esc(r.title||'صورة')}">${esc(truncateWords(r.title||'صورة',8))}</div>
619
+ <a class="image-url" href="${esc(r.url||'')}" target="_blank">${esc(getDomain(r.url||''))}</a>
620
+ </div>`;
 
621
  grid.appendChild(card);
622
  });
623
+ container.appendChild(grid);
624
  };
625
 
626
+ // ===== عرض الفيديوهات =====
627
+ const renderVideoGrid = (videos, container, withTitle=false) => {
628
+ const valid = videos.filter(r => r.thumbnail);
629
+ if (!valid.length) return;
630
+ if (withTitle) container.appendChild(createSectionTitle('فيديوهات','videos'));
 
631
  const grid = document.createElement('div'); grid.className='videos-grid';
632
+ valid.forEach(r => {
633
  const thumb = proxyImg(r.thumbnail);
 
634
  const card = document.createElement('div'); card.className='video-card';
635
+ card.innerHTML=`
636
+ <a href="${esc(r.url||'')}" target="_blank" class="video-thumb-wrap">
637
  <img src="${esc(thumb)}" loading="lazy" onerror="handleVideoThumbError(this)" alt="${esc(r.title||'')}" />
638
  <div class="play-overlay"><i class="fa-solid fa-play"></i></div>
639
  </a>
640
  <div class="video-details">
641
+ <a class="video-title" href="${esc(r.url||'')}" target="_blank">${esc(truncateWords(r.title||'فيديو',12))}</a>
642
+ <a class="video-url" href="${esc(r.url||'')}" target="_blank">${esc(getDomain(r.url||''))}</a>
643
+ </div>`;
 
644
  grid.appendChild(card);
645
  });
646
+ container.appendChild(grid);
647
  };
648
 
649
+ // ===== البحث الرئيسي =====
650
  const performSearch = async (q, tab) => {
651
+ // إعادة تعيين الحالة
652
  resultsEl.innerHTML = '';
653
  suggestionsEl.innerHTML = '';
654
+ countBar.innerHTML = '';
655
+ endEl.classList.remove('show');
656
+ loadMoreEl.classList.remove('show');
657
+ allWebResults = [];
658
+ renderedCount = 0;
659
+ isRendering = false;
660
+ detachSentinel();
661
+
662
+ loadingEl.style.display = 'flex';
663
+ loadingEl.classList.add('spinning');
664
+ loadingEl.textContent = 'جاري جلب النتائج…';
665
+
666
+ // حالة تحميل زر البحث
667
+ submitBtn.classList.add('loading-btn');
668
+ submitBtn.innerHTML = '<i class="fa-solid fa-circle-notch"></i>';
669
+
670
  showAI(tab === 'all');
671
 
672
+ const resetSubmitBtn = () => {
673
+ submitBtn.classList.remove('loading-btn');
674
+ submitBtn.innerHTML = '<i class="fa-solid fa-magnifying-glass"></i>';
675
+ };
676
+
677
  try {
678
  if (tab === 'all') {
679
+ // جلب الصور والفيديو معاً أول��ً
680
  const [imagesData, videosData] = await Promise.all([
681
  fetchCategory('images'),
682
  fetchCategory('videos')
 
685
  const suggestions = imagesData.suggestions || videosData.suggestions || [];
686
  if (suggestions.length) renderSuggestions(suggestions);
687
 
688
+ renderImageGrid((imagesData.results||[]).slice(0,4), resultsEl, true);
689
+ renderVideoGrid((videosData.results||[]).slice(0,3), resultsEl, true);
690
+ observe();
691
+
692
+ // جلب نتائج الويب — صفحات متعددة بدون حد
693
+ loadingEl.textContent = 'جاري جلب نتائج الويب…';
694
+
695
+ const [page1, page2, page3] = await Promise.all([
696
+ fetchCategory('general', 1),
697
+ fetchCategory('general', 2),
698
+ fetchCategory('general', 3),
699
+ ]);
700
+
701
+ // دمج كل النتائج وإزالة المكرر
702
+ const seen = new Set();
703
+ const mergeUnique = (arr) => (arr||[]).filter(r => {
704
+ if (!r.url || seen.has(r.url)) return false;
705
+ seen.add(r.url); return true;
706
+ });
707
 
708
+ allWebResults = [
709
+ ...mergeUnique(page1.results),
710
+ ...mergeUnique(page2.results),
711
+ ...mergeUnique(page3.results),
712
+ ];
713
 
 
 
714
  loadingEl.style.display = 'none';
715
+ resetSubmitBtn();
716
 
717
+ if (!allWebResults.length) {
718
+ resultsEl.innerHTML += `<div class="empty-state"><i class="fa-solid fa-magnifying-glass"></i><p>لا توجد نتائج لـ "<strong>${esc(q)}</strong>"</p></div>`;
719
+ return;
720
  }
721
+
722
+ // عرض عداد النتائج
723
+ renderCount(allWebResults.length);
724
+
725
+ // عرض الدفعة الأولى
726
+ renderNextBatch();
727
+
728
+ // تشغيل الذكاء الاصطناعي بناءً على أفضل النتائج
729
+ runAI(q, allWebResults.slice(0,6));
730
+
731
+ } else if (tab === 'images') {
732
+ const [p1, p2] = await Promise.all([fetchCategory('images',1), fetchCategory('images',2)]);
733
+ const seen = new Set();
734
+ const all = [...(p1.results||[]), ...(p2.results||[])].filter(r => {
735
+ if (!r.url || seen.has(r.url)) return false; seen.add(r.url); return true;
736
+ });
737
+ loadingEl.style.display='none'; resetSubmitBtn();
738
+ renderCount(all.length);
739
+ renderImageGrid(all, resultsEl, false);
740
  observe();
 
 
 
 
 
741
 
742
+ } else if (tab === 'videos') {
743
+ const [p1, p2] = await Promise.all([fetchCategory('videos',1), fetchCategory('videos',2)]);
744
+ const seen = new Set();
745
+ const all = [...(p1.results||[]), ...(p2.results||[])].filter(r => {
746
+ if (!r.url || seen.has(r.url)) return false; seen.add(r.url); return true;
747
+ });
748
+ loadingEl.style.display='none'; resetSubmitBtn();
749
+ renderCount(all.length);
750
+ renderVideoGrid(all, resultsEl, false);
751
+ observe();
752
+
753
+ } else if (tab === 'news') {
754
+ const data = await fetchCategory('news');
755
+ const results = data.results || [];
756
+ loadingEl.style.display='none'; resetSubmitBtn();
757
+ if (data.suggestions?.length) renderSuggestions(data.suggestions);
758
+ renderCount(results.length);
759
+ if (results.length) {
760
+ // عرض الأخبار كبطاقات ويب عادية
761
+ allWebResults = results;
762
+ renderedCount = 0;
763
+ renderNextBatch();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
764
  } else {
765
+ resultsEl.innerHTML = `<div class="empty-state"><i class="fa-solid fa-newspaper"></i><p>لا توجد أخبار حول "<strong>${esc(q)}</strong>"</p></div>`;
766
  }
 
767
  }
768
+
769
  } catch(e) {
770
+ loadingEl.style.display='none';
771
+ resetSubmitBtn();
772
+ resultsEl.innerHTML = `<div class="empty-state"><i class="fa-solid fa-triangle-exclamation"></i><p>حدث خطأ أثناء البحث. حاول مجدداً.</p></div>`;
773
  }
774
  };
775
 
776
+ // ===== البحث الصوتي =====
777
+ const voiceBtn = document.getElementById('voice-btn');
778
+ const voiceIndicator = document.getElementById('voice-indicator');
779
+ const voiceToast = document.getElementById('voice-toast');
780
+
781
+ const showToast = (msg) => {
782
+ voiceToast.textContent = msg;
783
+ voiceToast.classList.add('show');
784
+ setTimeout(() => voiceToast.classList.remove('show'), 3000);
785
+ };
786
+
787
+ const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
788
+
789
+ if (!SpeechRecognition) {
790
+ voiceBtn.title = 'البحث الصوتي غير مدعوم في هذا المتصفح';
791
+ voiceBtn.style.opacity = '0.4';
792
+ voiceBtn.addEventListener('click', () => showToast('متصفحك لا يدعم البحث الصوتي. جرب Chrome أو Edge.'));
793
+ } else {
794
+ const recognition = new SpeechRecognition();
795
+ recognition.lang = 'ar-SA';
796
+ recognition.interimResults = true;
797
+ recognition.maxAlternatives = 1;
798
+ recognition.continuous = false;
799
+
800
+ let isListening = false;
801
+
802
+ const startListening = () => {
803
+ if (isListening) { recognition.stop(); return; }
804
+ try {
805
+ recognition.start();
806
+ } catch(e) {
807
+ showToast('لا يمكن بدء الاستماع. تحقق من إذن الميكروفون.');
808
+ }
809
+ };
810
+
811
+ recognition.onstart = () => {
812
+ isListening = true;
813
+ voiceBtn.classList.add('listening');
814
+ voiceBtn.innerHTML = '<i class="fa-solid fa-microphone-slash"></i>';
815
+ voiceIndicator.classList.add('show');
816
+ };
817
+
818
+ recognition.onresult = (e) => {
819
+ const transcript = Array.from(e.results).map(r => r[0].transcript).join('');
820
+ inputEl.value = transcript;
821
+ updateClearBtn();
822
+ // إذا كانت النتيجة نهائية، نبحث تلقائياً
823
+ if (e.results[e.results.length - 1].isFinal) {
824
+ recognition.stop();
825
+ if (transcript.trim()) {
826
+ setTimeout(() => {
827
+ document.getElementById('search-form').dispatchEvent(new Event('submit'));
828
+ }, 300);
829
+ }
830
+ }
831
+ };
832
+
833
+ recognition.onerror = (e) => {
834
+ const msgs = {
835
+ 'no-speech': 'لم يُسمع أي كلام. حاول مجدداً.',
836
+ 'audio-capture': 'لا يمكن الوصول للميكروفون.',
837
+ 'not-allowed': 'تم رفض إذن الميكروفون.',
838
+ 'network': 'خطأ في الشبكة أثناء التعرف على الصوت.',
839
+ };
840
+ showToast(msgs[e.error] || 'خطأ في البحث الصوتي.');
841
+ };
842
+
843
+ recognition.onend = () => {
844
+ isListening = false;
845
+ voiceBtn.classList.remove('listening');
846
+ voiceBtn.innerHTML = '<i class="fa-solid fa-microphone"></i>';
847
+ voiceIndicator.classList.remove('show');
848
+ };
849
+
850
+ voiceBtn.addEventListener('click', startListening);
851
+ }
852
+
853
+ // ===== التهيئة =====
854
  if (query) {
855
  inputEl.value = query;
856
  updateClearBtn();
 
859
  loadingEl.style.display = 'block';
860
  loadingEl.classList.remove('spinning');
861
  loadingEl.textContent = 'أدخل كلمة البحث للبدء.';
862
+ submitBtn.classList.remove('loading-btn');
863
+ submitBtn.innerHTML = '<i class="fa-solid fa-magnifying-glass"></i>';
864
  }
865
 
866
+ // ===== مستمعو الأحداث =====
867
  document.getElementById('search-form').addEventListener('submit', e => {
868
  e.preventDefault();
869
  const q = inputEl.value.trim();
 
884
  });
885
  });
886
 
887
+ document.getElementById('ai-copy-desktop')?.addEventListener('click', () => navigator.clipboard.writeText(currentAItext));
888
+ document.getElementById('ai-copy-mobile')?.addEventListener('click', () => navigator.clipboard.writeText(currentAItext));
889
 
 
890
  document.addEventListener('click', (e) => {
891
+ const btn = e.target.closest('.copy-link-btn');
892
+ if (btn) {
893
+ navigator.clipboard.writeText(btn.dataset.url).then(() => {
894
+ btn.innerHTML = '<i class="fa-solid fa-check"></i>';
895
+ setTimeout(() => { btn.innerHTML = '<i class="fa-regular fa-copy"></i>'; }, 1500);
896
+ }).catch(()=>{});
897
  }
898
  });
899