amauricunha commited on
Commit
2e62f01
·
verified ·
1 Parent(s): bef9f38

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +77 -37
index.html CHANGED
@@ -37,6 +37,20 @@
37
  .btn-secondary:hover {
38
  background-color: #d1d5db;
39
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  /* Styling for the Modal (Carousel Simulation) */
41
  .modal {
42
  display: none;
@@ -106,10 +120,15 @@
106
 
107
  <div class="card mb-6">
108
  <h2 class="text-xl font-semibold mb-4 text-gray-700">Your Text for Study</h2>
109
- <textarea id="textInput"
110
- placeholder="Paste the English text you want to listen to and study here. Double-click any word for translation and context!"
111
- class="w-full p-4 border border-gray-300 rounded-lg focus:ring-indigo-500 focus:border-indigo-500 h-64 resize-none text-gray-800"
112
- maxlength="5000"></textarea>
 
 
 
 
 
113
  <p class="text-sm text-right text-gray-500 mt-2">Characters: <span id="charCount">0</span>/5000</p>
114
  </div>
115
 
@@ -121,7 +140,6 @@
121
  <div>
122
  <label for="voiceSelector" class="block text-sm font-medium text-gray-700">Voice (gTTS)</label>
123
  <select id="voiceSelector" class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md shadow-sm">
124
- <!-- gTTS uses US accent by default; selector is for UX/Future APIs -->
125
  <option value="us_man">American Man</option>
126
  <option value="us_woman">American Woman</option>
127
  </select>
@@ -198,7 +216,7 @@
198
 
199
  <script>
200
  // Global variables
201
- const textInput = document.getElementById('textInput');
202
  const playButton = document.getElementById('playButton');
203
  const audioPlayer = document.getElementById('audioPlayer');
204
  const charCount = document.getElementById('charCount');
@@ -213,22 +231,51 @@
213
  let loopText = '';
214
  let lastAudioUrl = '';
215
 
216
- // --- Interface Management ---
 
 
 
 
 
217
 
218
- textInput.addEventListener('input', () => {
219
- const currentLength = textInput.value.length;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
  charCount.textContent = currentLength;
 
221
  // Limit input to max characters
222
  if (currentLength > MAX_CHARS) {
223
- textInput.value = textInput.value.substring(0, MAX_CHARS);
 
 
224
  }
225
  // Enable button if there is text
226
  playButton.disabled = currentLength === 0;
227
- // The logic for enabling loop button on selection will be handled below
228
  });
229
 
230
  // Update character count on load
231
- textInput.dispatchEvent(new Event('input'));
232
 
233
  speedSelector.addEventListener('change', () => {
234
  audioPlayer.playbackRate = parseFloat(speedSelector.value);
@@ -277,7 +324,7 @@
277
  function handlePlay() {
278
  isLooping = false;
279
  clearLoopButton.disabled = true;
280
- const textToPlay = textInput.value.trim();
281
  if (textToPlay) {
282
  fetchAudio(textToPlay);
283
  }
@@ -304,7 +351,7 @@
304
  audioPlayer.loop = false;
305
  audioPlayer.title = '';
306
  // Return to main audio (if available)
307
- if (textInput.value.trim()) {
308
  handlePlay();
309
  }
310
  }
@@ -318,7 +365,7 @@
318
  });
319
 
320
  // Enable Loop button if there is a selection
321
- textInput.addEventListener('select', () => {
322
  const selection = window.getSelection().toString().trim();
323
  loopSelectionButton.disabled = !selection;
324
  });
@@ -368,28 +415,21 @@
368
  }
369
 
370
  // Logic to open the Modal on double click
371
- textInput.addEventListener('dblclick', (event) => {
372
- const target = event.target;
373
- if (target.id === 'textInput') {
374
- const selection = window.getSelection();
375
- // Expand selection to the nearest word
376
- selection.modify('move', 'backward', 'word');
377
- selection.modify('extend', 'forward', 'word');
 
 
 
 
 
378
 
379
- const selectedWord = selection.toString().trim().replace(/[^a-zA-Z']/g, '');
380
-
381
- if (selectedWord) {
382
- const fullText = textInput.value;
383
- const index = fullText.indexOf(selectedWord);
384
- // Capture context (a few words before and after)
385
- const context = fullText.substring(Math.max(0, index - 50), index + selectedWord.length + 50);
386
-
387
- document.getElementById('wordSelected').textContent = selectedWord;
388
- openModal();
389
-
390
- // Start fetching explanation and translation
391
- fetchExplanationAndTranslation(selectedWord, context);
392
- }
393
  }
394
  });
395
 
@@ -440,4 +480,4 @@
440
  </script>
441
 
442
  </body>
443
- </html>
 
37
  .btn-secondary:hover {
38
  background-color: #d1d5db;
39
  }
40
+ /* Style for the Content Editable Editor */
41
+ #textEditor {
42
+ border: 1px solid #ccc;
43
+ padding: 1rem;
44
+ min-height: 16rem; /* h-64 equivalent */
45
+ border-radius: 0.5rem;
46
+ color: #1f2937;
47
+ line-height: 1.6;
48
+ outline: none;
49
+ overflow-y: auto;
50
+ /* Force English spellcheck */
51
+ -webkit-user-modify: read-write-plaintext-only;
52
+ spellcheck: true;
53
+ }
54
  /* Styling for the Modal (Carousel Simulation) */
55
  .modal {
56
  display: none;
 
120
 
121
  <div class="card mb-6">
122
  <h2 class="text-xl font-semibold mb-4 text-gray-700">Your Text for Study</h2>
123
+
124
+ <!-- CONTENT EDITABLE DIV replaces TEXTAREA -->
125
+ <div id="textEditor"
126
+ contenteditable="true"
127
+ spellcheck="true"
128
+ lang="en"
129
+ data-placeholder="Paste the English text you want to listen to and study here. Double-click any word for translation and context!"
130
+ class="w-full p-4 focus:ring-indigo-500 focus:border-indigo-500 text-gray-800"></div>
131
+
132
  <p class="text-sm text-right text-gray-500 mt-2">Characters: <span id="charCount">0</span>/5000</p>
133
  </div>
134
 
 
140
  <div>
141
  <label for="voiceSelector" class="block text-sm font-medium text-gray-700">Voice (gTTS)</label>
142
  <select id="voiceSelector" class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md shadow-sm">
 
143
  <option value="us_man">American Man</option>
144
  <option value="us_woman">American Woman</option>
145
  </select>
 
216
 
217
  <script>
218
  // Global variables
219
+ const textEditor = document.getElementById('textEditor'); // Renamed from textInput
220
  const playButton = document.getElementById('playButton');
221
  const audioPlayer = document.getElementById('audioPlayer');
222
  const charCount = document.getElementById('charCount');
 
231
  let loopText = '';
232
  let lastAudioUrl = '';
233
 
234
+ // --- Interface and Editor Management ---
235
+
236
+ function getEditorText() {
237
+ // Get text content, not HTML, to ensure clean input for TTS and AI
238
+ return textEditor.innerText.trim();
239
+ }
240
 
241
+ // Add placeholder functionality for contenteditable
242
+ function updatePlaceholder() {
243
+ const placeholder = textEditor.getAttribute('data-placeholder');
244
+ if (getEditorText() === '' && textEditor.innerHTML === '') {
245
+ textEditor.innerHTML = `<span style="color:#9ca3af">${placeholder}</span>`;
246
+ } else if (textEditor.innerText.includes(placeholder)) {
247
+ textEditor.innerHTML = '';
248
+ }
249
+ }
250
+
251
+ function removePlaceholder() {
252
+ const placeholderText = textEditor.getAttribute('data-placeholder');
253
+ if (textEditor.innerHTML.includes(placeholderText)) {
254
+ textEditor.innerHTML = '';
255
+ }
256
+ }
257
+
258
+ // Initial setup for placeholder
259
+ updatePlaceholder();
260
+ textEditor.addEventListener('focus', removePlaceholder);
261
+ textEditor.addEventListener('blur', updatePlaceholder);
262
+
263
+ textEditor.addEventListener('input', () => {
264
+ const currentLength = getEditorText().length;
265
  charCount.textContent = currentLength;
266
+
267
  // Limit input to max characters
268
  if (currentLength > MAX_CHARS) {
269
+ // Warning: This truncation is crude for contenteditable, user must manually delete
270
+ // Since this is study tool, we rely on the user to manage content length.
271
+ // Truncation logic is best left for the server, but we warn here.
272
  }
273
  // Enable button if there is text
274
  playButton.disabled = currentLength === 0;
 
275
  });
276
 
277
  // Update character count on load
278
+ textEditor.dispatchEvent(new Event('input'));
279
 
280
  speedSelector.addEventListener('change', () => {
281
  audioPlayer.playbackRate = parseFloat(speedSelector.value);
 
324
  function handlePlay() {
325
  isLooping = false;
326
  clearLoopButton.disabled = true;
327
+ const textToPlay = getEditorText();
328
  if (textToPlay) {
329
  fetchAudio(textToPlay);
330
  }
 
351
  audioPlayer.loop = false;
352
  audioPlayer.title = '';
353
  // Return to main audio (if available)
354
+ if (getEditorText()) {
355
  handlePlay();
356
  }
357
  }
 
365
  });
366
 
367
  // Enable Loop button if there is a selection
368
+ textEditor.addEventListener('mouseup', () => {
369
  const selection = window.getSelection().toString().trim();
370
  loopSelectionButton.disabled = !selection;
371
  });
 
415
  }
416
 
417
  // Logic to open the Modal on double click
418
+ textEditor.addEventListener('dblclick', (event) => {
419
+ // Check if the click occurred on a text node or an element inside the editor
420
+ const selectedWord = window.getSelection().toString().trim().replace(/[^a-zA-Z']/g, '');
421
+
422
+ if (selectedWord) {
423
+ const fullText = getEditorText();
424
+ const index = fullText.indexOf(selectedWord);
425
+ // Capture context (a few words before and after)
426
+ const context = fullText.substring(Math.max(0, index - 50), index + selectedWord.length + 50);
427
+
428
+ document.getElementById('wordSelected').textContent = selectedWord;
429
+ openModal();
430
 
431
+ // Start fetching explanation and translation
432
+ fetchExplanationAndTranslation(selectedWord, context);
 
 
 
 
 
 
 
 
 
 
 
 
433
  }
434
  });
435
 
 
480
  </script>
481
 
482
  </body>
483
+ </html>