Text Highlighter with TTS

Highlight text on any website and have it read using TTS with options for voice and speed.

  1. // ==UserScript==
  2. // @name Text Highlighter with TTS
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.2
  5. // @description Highlight text on any website and have it read using TTS with options for voice and speed.
  6. // @match *://*/*
  7. // @grant none
  8. // ==/UserScript==
  9.  
  10. (function() {
  11. 'use strict';
  12.  
  13. // Create GUI
  14. let gui = document.createElement('div');
  15. gui.style.position = 'fixed';
  16. gui.style.top = '20px';
  17. gui.style.left = '20px';
  18. gui.style.width = '300px';
  19. gui.style.height = '200px';
  20. gui.style.background = 'gray';
  21. gui.style.borderRadius = '10px';
  22. gui.style.boxShadow = '2px 2px 10px rgba(0, 0, 0, 0.5)';
  23. gui.innerHTML = `
  24. <div style="background-color: #ccc; padding: 10px; border-radius: 10px 10px 0 0;">
  25. <span style="font-size: 18px;">Highlighter</span>
  26. </div>
  27. <div style="padding: 10px;">
  28. <button id="readButton">Read Selected Text</button>
  29. <br><br>
  30. <label for="voiceSelect">Voice: </label>
  31. <select id="voiceSelect"></select>
  32. <br><br>
  33. <label for="speedRange">Speed: </label>
  34. <input type="range" id="speedRange" min="0.5" max="2" step="0.1" value="1">
  35. </div>
  36. `;
  37. document.body.appendChild(gui);
  38.  
  39. // Drag functionality
  40. gui.onmousedown = function(e) {
  41. let shiftX = e.clientX - gui.getBoundingClientRect().left;
  42. let shiftY = e.clientY - gui.getBoundingClientRect().top;
  43.  
  44. function moveAt(pageX, pageY) {
  45. gui.style.left = pageX - shiftX + 'px';
  46. gui.style.top = pageY - shiftY + 'px';
  47. }
  48.  
  49. function onMouseMove(e) {
  50. moveAt(e.pageX, e.pageY);
  51. }
  52.  
  53. document.addEventListener('mousemove', onMouseMove);
  54.  
  55. gui.onmouseup = function() {
  56. document.removeEventListener('mousemove', onMouseMove);
  57. gui.onmouseup = null;
  58. };
  59. };
  60.  
  61. gui.ontouchstart = function(e) {
  62. let shiftX = e.touches[0].clientX - gui.getBoundingClientRect().left;
  63. let shiftY = e.touches[0].clientY - gui.getBoundingClientRect().top;
  64.  
  65. function moveAt(pageX, pageY) {
  66. gui.style.left = pageX - shiftX + 'px';
  67. gui.style.top = pageY - shiftY + 'px';
  68. }
  69.  
  70. function onTouchMove(e) {
  71. moveAt(e.touches[0].pageX, e.touches[0].pageY);
  72. }
  73.  
  74. document.addEventListener('touchmove', onTouchMove);
  75.  
  76. gui.ontouchend = function() {
  77. document.removeEventListener('touchmove', onTouchMove);
  78. gui.ontouchend = null;
  79. };
  80. };
  81.  
  82. // Disable text selection while dragging
  83. gui.ondragstart = function() {
  84. return false;
  85. };
  86.  
  87. // TTS setup
  88. let synth = window.speechSynthesis;
  89. let voices = [];
  90.  
  91. function populateVoiceList() {
  92. voices = synth.getVoices();
  93. let voiceSelect = document.getElementById('voiceSelect');
  94. voiceSelect.innerHTML = '';
  95. voices.forEach((voice, index) => {
  96. let option = document.createElement('option');
  97. option.textContent = voice.name + ' (' + voice.lang + ')';
  98. option.value = index;
  99. voiceSelect.appendChild(option);
  100. });
  101. }
  102.  
  103. populateVoiceList();
  104. if (speechSynthesis.onvoiceschanged !== undefined) {
  105. speechSynthesis.onvoiceschanged = populateVoiceList;
  106. }
  107.  
  108. // Detect selected text
  109. let selectedText = '';
  110. document.addEventListener('selectionchange', () => {
  111. let selection = window.getSelection();
  112. if (selection && selection.rangeCount > 0) {
  113. selectedText = selection.toString().trim();
  114. }
  115. });
  116.  
  117. // Read selected text using TTS
  118. document.getElementById('readButton').addEventListener('click', () => {
  119. if (selectedText) {
  120. let utterThis = new SpeechSynthesisUtterance(selectedText);
  121. utterThis.voice = voices[document.getElementById('voiceSelect').value];
  122. utterThis.rate = document.getElementById('speedRange').value;
  123. synth.speak(utterThis);
  124. } else {
  125. alert('Please select some text first.');
  126. }
  127. });
  128. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址