theWitcher commited on
Commit
0a563e9
·
1 Parent(s): 91d24eb

add speaker

Browse files
Files changed (1) hide show
  1. index.html +163 -21
index.html CHANGED
@@ -537,6 +537,83 @@ https://chatgpt.com/c/67efa5ae-ab80-8005-a7d4-de3ced6ccec4
537
  }
538
  }
539
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
540
  // --- UI Feedback ---
541
  function showLoading() {
542
  if (loadingMessage) loadingMessage.style.display = 'block';
@@ -649,9 +726,9 @@ https://chatgpt.com/c/67efa5ae-ab80-8005-a7d4-de3ced6ccec4
649
  function renderTools() {
650
  hideLoading(); // Ensure loading message is hidden before rendering
651
  if (!toolsContainer || !toolsData || !Array.isArray(toolsData.tools)) {
652
- console.error("Cannot render tools: Missing container or invalid data.");
653
- showError("שגיאה בהצגת הכלים.");
654
- return;
655
  }
656
  const filteredTools = filterTools();
657
  toolsContainer.innerHTML = ''; // Clear previous content
@@ -664,27 +741,92 @@ https://chatgpt.com/c/67efa5ae-ab80-8005-a7d4-de3ced6ccec4
664
  } else {
665
  filteredTools.forEach(tool => {
666
  const toolCard = document.createElement('div');
667
- toolCard.className = `relative tool-card bg-white rounded-lg shadow-sm border border-gray-100 p-6 transition duration-300 ${tool.isFeatured ? 'ring-2 ring-blue-500' : ''}`;
668
- toolCard.innerHTML = `
669
- <div class="flex items-start mb-4">
670
- <div class="p-3 rounded-lg ${getCategoryColor(tool.category)} text-white mr-4 flex-shrink-0"> <i class="${tool.icon} text-xl"></i> </div>
671
- <div class="flex-grow">
672
- <h3 class="text-xl font-semibold">${tool.name}</h3>
673
- <span class="text-xs px-2 py-1 rounded-full ${getCategoryBadgeColor(tool.category)}">${getCategoryName(tool.category)}</span>
674
- </div>
675
- ${tool.isNew ? '<span class="absolute top-2 left-2 bg-green-100 text-green-800 text-xs font-medium px-2.5 py-0.5 rounded-full">חדש!</span>' : ''}
676
- </div>
677
- <p class="text-gray-700 mb-4 text-sm min-h-[60px]">${tool.description}</p>
678
- <div class="flex justify-between items-center mb-4">
679
- <div class="flex"> ${renderRatingStars(tool.rating)} </div>
 
 
 
 
 
680
  </div>
681
- <a href="${tool.url}" target="_blank" rel="noopener noreferrer" class="inline-block w-full text-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition ${tool.url === '#' ? 'opacity-50 cursor-not-allowed' : ''}">
682
- <i class="fas fa-external-link-alt ml-2"></i> ${tool.url !== '#' ? 'גישה לכלי' : 'אין קישור'}
683
- </a>`;
684
- toolsContainer.appendChild(toolCard);
 
 
 
 
 
 
 
 
 
685
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
686
  }
687
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
688
 
689
  function renderVideos() {
690
  // ... (renderVideos logic remains the same as before) ...
 
537
  }
538
  }
539
 
540
+ const speechSynthesis = window.speechSynthesis;
541
+ let currentUtterance = null;
542
+
543
+ // פונקציה להקראת טקסט בעברית
544
+ function speakText(text) {
545
+ // עצירת הקראה קודמת אם יש
546
+ if (speechSynthesis.speaking) {
547
+ speechSynthesis.cancel();
548
+
549
+ // אם זאת אותה הקראה שכבר פועלת, פשוט נעצור ונצא
550
+ if (currentUtterance && currentUtterance.text === text) {
551
+ currentUtterance = null;
552
+ return;
553
+ }
554
+ }
555
+
556
+ // יצירת הקראה חדשה
557
+ const utterance = new SpeechSynthesisUtterance(text);
558
+
559
+ // הגדרת השפה לעברית
560
+ utterance.lang = 'he-IL';
561
+
562
+ // בדיקת קולות זמינים ובחירת קול עברי
563
+ const voices = speechSynthesis.getVoices();
564
+
565
+
566
+ if (voices.length) {
567
+ const hebrewVoice = voices.find(voice => /he|hebrew|ivrit/i.test(voice.lang));
568
+
569
+ if (hebrewVoice) {
570
+ utterance.voice = hebrewVoice;
571
+ console.log("נבחר קול עברי:", hebrewVoice.name);
572
+ } else {
573
+ console.log("לא נמצא קול עברי, משתמש בקול ברירת מחדל");
574
+ }
575
+ // ננסה להשתמש בקול גוגל
576
+ const googleVoice = voices.find(voice =>
577
+ voice.name.includes('Google') &&
578
+ (voice.name.includes('US') || voice.name.includes('UK'))
579
+ );
580
+
581
+
582
+ if (googleVoice) {
583
+ utterance.voice = googleVoice;
584
+ }
585
+ } else {
586
+ // במקרה שהקולות עדיין לא נטענו, נגדיר מאזין חד-פעמי
587
+ speechSynthesis.addEventListener('voiceschanged', function loadVoice() {
588
+ const voices = speechSynthesis.getVoices();
589
+ const hebrewVoice = voices.find(voice => /he|hebrew|ivrit/i.test(voice.lang));
590
+ if (hebrewVoice) {
591
+ utterance.voice = hebrewVoice;
592
+ console.log("נבחר קול עברי (מדחייה):", hebrewVoice.name);
593
+ }
594
+ // הסרת המאזין אחרי הפעלה ראשונה
595
+ speechSynthesis.removeEventListener('voiceschanged', loadVoice);
596
+ }, { once: true });
597
+ }
598
+
599
+ // שמירת ההקראה הנוכחית
600
+ currentUtterance = utterance;
601
+
602
+ // טיפול באירוע סיום הקראה
603
+ utterance.onend = () => {
604
+ currentUtterance = null;
605
+
606
+ // עדכון כל הכפתורים למצב "לא מנגן"
607
+ document.querySelectorAll('.speak-button').forEach(btn => {
608
+ btn.innerHTML = '<i class="fas fa-volume-up"></i>';
609
+ btn.classList.remove('speaking');
610
+ });
611
+ };
612
+
613
+ // הפעלת ההקראה
614
+ speechSynthesis.speak(utterance);
615
+ }
616
+
617
  // --- UI Feedback ---
618
  function showLoading() {
619
  if (loadingMessage) loadingMessage.style.display = 'block';
 
726
  function renderTools() {
727
  hideLoading(); // Ensure loading message is hidden before rendering
728
  if (!toolsContainer || !toolsData || !Array.isArray(toolsData.tools)) {
729
+ console.error("Cannot render tools: Missing container or invalid data.");
730
+ showError("שגיאה בהצגת הכלים.");
731
+ return;
732
  }
733
  const filteredTools = filterTools();
734
  toolsContainer.innerHTML = ''; // Clear previous content
 
741
  } else {
742
  filteredTools.forEach(tool => {
743
  const toolCard = document.createElement('div');
744
+ toolCard.className = `relative tool-card bg-white rounded-lg shadow-sm border border-gray-100 p-6 transition duration-300 ${tool.isFeatured ? 'ring-2 ring-blue-500' : ''}`;
745
+
746
+ // הכנת הטקסט להקראה - שילוב של שם הכלי והתיאור שלו
747
+ const speakableText = `${tool.name}`;
748
+ // `${tool.name}. ${tool.description}`;
749
+
750
+ toolCard.innerHTML = `
751
+ <div class="flex items-start mb-4">
752
+ <div class="p-3 rounded-lg ${getCategoryColor(tool.category)} text-white mr-4 flex-shrink-0">
753
+ <i class="${tool.icon} text-xl"></i>
754
+ </div>
755
+ <div class="flex-grow">
756
+ <div class="flex justify-between items-start">
757
+ <h3 class="text-xl font-semibold">${tool.name}</h3>
758
+ <button class="speak-button p-2 text-blue-600 hover:text-blue-800 focus:outline-none"
759
+ onclick="event.stopPropagation(); speakText('${speakableText.replace(/'/g, "\\'")}')">
760
+ <i class="fas fa-volume-up"></i>
761
+ </button>
762
  </div>
763
+ <span class="text-xs px-2 py-1 rounded-full ${getCategoryBadgeColor(tool.category)}">${getCategoryName(tool.category)}</span>
764
+ </div>
765
+ ${tool.isNew ? '<span class="absolute top-2 left-2 bg-green-100 text-green-800 text-xs font-medium px-2.5 py-0.5 rounded-full">חדש!</span>' : ''}
766
+ </div>
767
+ <p class="text-gray-700 mb-4 text-sm min-h-[60px]">${tool.description}</p>
768
+ <div class="flex justify-between items-center mb-4">
769
+ <div class="flex"> ${renderRatingStars(tool.rating)} </div>
770
+ </div>
771
+ <a href="${tool.url}" target="_blank" rel="noopener noreferrer" class="inline-block w-full text-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition ${tool.url === '#' ? 'opacity-50 cursor-not-allowed' : ''}">
772
+ <i class="fas fa-external-link-alt ml-2"></i> ${tool.url !== '#' ? 'גישה לכלי' : 'אין קישור'}
773
+ </a>`;
774
+
775
+ toolsContainer.appendChild(toolCard);
776
  });
777
+
778
+ // הוספת סגנון CSS לכפתור הקראה פעיל
779
+ if (!document.getElementById('speakButtonStyle')) {
780
+ const style = document.createElement('style');
781
+ style.id = 'speakButtonStyle';
782
+ style.innerHTML = `
783
+ .speak-button.speaking { color: #4f46e5; animation: pulse 1.5s infinite; }
784
+ @keyframes pulse {
785
+ 0% { transform: scale(1); }
786
+ 50% { transform: scale(1.2); }
787
+ 100% { transform: scale(1); }
788
+ }
789
+ `;
790
+ document.head.appendChild(style);
791
+ }
792
  }
793
+ }
794
+
795
+ // פונקציה נוספת שמוחקת את ההקראה בעת עזיבת העמוד
796
+ function cleanupSpeech() {
797
+ if (speechSynthesis) {
798
+ speechSynthesis.cancel();
799
+ }
800
+ }
801
+
802
+ // הוספת אירוע לניקוי ההקראה בעת עזיבת העמוד
803
+ window.addEventListener('beforeunload', cleanupSpeech);
804
+
805
+ // עדכון אירוע הקלקה על כפתור הקראה - להוסיף מחלקת CSS ולשנות אייקון
806
+ document.addEventListener('click', function(e) {
807
+ if (e.target.closest('.speak-button')) {
808
+ const button = e.target.closest('.speak-button');
809
+
810
+ // החלפת האייקון והוספת מחלקת CSS מהבהבת
811
+ if (speechSynthesis.speaking && currentUtterance) {
812
+ // אם יש הקראה פעילה, נעדכן את כל הכפתורים
813
+ document.querySelectorAll('.speak-button').forEach(btn => {
814
+ btn.innerHTML = '<i class="fas fa-volume-up"></i>';
815
+ btn.classList.remove('speaking');
816
+ });
817
+
818
+ // ואז נעדכן את הכפתור הנוכחי אם ההקראה ממשיכה
819
+ if (!button.classList.contains('speaking')) {
820
+ button.innerHTML = '<i class="fas fa-volume-mute"></i>';
821
+ button.classList.add('speaking');
822
+ }
823
+ } else {
824
+ button.innerHTML = '<i class="fas fa-volume-up"></i>';
825
+ button.classList.remove('speaking');
826
+ }
827
+ }
828
+ });
829
+
830
 
831
  function renderVideos() {
832
  // ... (renderVideos logic remains the same as before) ...