nzgnzg73 commited on
Commit
76e40ac
·
verified ·
1 Parent(s): a7ef04e

Upload voice to text.html

Browse files
Files changed (1) hide show
  1. Speech to text/voice to text.html +255 -64
Speech to text/voice to text.html CHANGED
@@ -622,6 +622,11 @@
622
 
623
  // Cursor position
624
  var cursorPosition = 0;
 
 
 
 
 
625
 
626
  // 36 Years in milliseconds
627
  var RETENTION_TIME = 1135296000000;
@@ -648,9 +653,15 @@
648
  var editor = document.getElementById('textEditor');
649
  editor.addEventListener('input', updateCounts);
650
  editor.addEventListener('input', scheduleDraftSave);
 
651
  editor.addEventListener('click', saveCursorPosition);
652
  editor.addEventListener('keyup', saveCursorPosition);
653
  editor.addEventListener('keydown', saveCursorPosition);
 
 
 
 
 
654
 
655
  // Initialize undo/redo
656
  saveToHistoryStack();
@@ -717,38 +728,7 @@
717
  }
718
 
719
  if (final) {
720
- var editor = document.getElementById('textEditor');
721
- var text = editor.value;
722
-
723
- // Insert at cursor position
724
- var beforeCursor = text.substring(0, cursorPosition);
725
- var afterCursor = text.substring(cursorPosition);
726
-
727
- // Add space before if needed
728
- if (beforeCursor.length > 0 && !beforeCursor.endsWith(' ') && !beforeCursor.endsWith('\n')) {
729
- final = ' ' + final;
730
- }
731
-
732
- // Add space after if needed
733
- if (final.length > 0 && !final.endsWith(' ') && !final.endsWith('\n')) {
734
- final = final + ' ';
735
- }
736
-
737
- editor.value = beforeCursor + final + afterCursor;
738
-
739
- // Move cursor to after inserted text
740
- cursorPosition = beforeCursor.length + final.length;
741
- editor.selectionStart = cursorPosition;
742
- editor.selectionEnd = cursorPosition;
743
-
744
- updateCounts();
745
- saveToHistoryStack();
746
- scheduleDraftSave();
747
-
748
- // Scroll to cursor position
749
- if (editor.value.length > 0) {
750
- editor.scrollTop = editor.scrollHeight * (cursorPosition / editor.value.length);
751
- }
752
  }
753
  };
754
 
@@ -790,32 +770,47 @@
790
  }
791
 
792
  function toggleMic() {
793
- if (!recognition) {
794
- recognition = initSpeech();
795
- }
796
-
797
- if (!recognition) return;
798
 
799
- if (isRecording) {
800
- isRecording = false;
801
- try {
802
- recognition.stop();
803
- } catch(e) {}
804
- updateMicUI(false);
805
- showToast("⏹️ Recording stopped");
806
- } else {
807
- try {
808
- recognition.start();
809
- showToast("🎤 Recording... Speak now!");
810
- } catch(e) {
811
- console.error(e);
812
- showToast("⚠️ Error starting microphone");
813
- // Try re-initializing
814
  recognition = initSpeech();
815
- setTimeout(function() {
816
- try { recognition.start(); } catch(e2) {}
817
- }, 500);
818
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
819
  }
820
  }
821
 
@@ -943,8 +938,10 @@
943
  editor.value = payload.text;
944
  currentTextState = payload.text;
945
  cursorPosition = payload.text.length;
 
946
  editor.selectionStart = cursorPosition;
947
  editor.selectionEnd = cursorPosition;
 
948
 
949
  if (payload.lang === 'ur-PK' || payload.lang === 'en-US' || payload.lang === 'en-PK') {
950
  setLang(payload.lang);
@@ -1058,27 +1055,215 @@
1058
  document.getElementById('redoBtn').disabled = redoStack.length === 0;
1059
  }
1060
 
1061
- // --- CURSOR POSITION TRACKING ---
 
 
 
 
 
 
 
 
 
 
 
1062
  function saveCursorPosition() {
1063
  var editor = document.getElementById('textEditor');
1064
- cursorPosition = editor.selectionStart;
1065
- document.getElementById('cursorPos').textContent = cursorPosition;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1066
  }
1067
 
1068
  function insertAtCursor(text) {
1069
  var editor = document.getElementById('textEditor');
1070
- var beforeCursor = editor.value.substring(0, cursorPosition);
1071
- var afterCursor = editor.value.substring(cursorPosition);
1072
-
 
 
 
 
 
 
 
 
1073
  editor.value = beforeCursor + text + afterCursor;
1074
  cursorPosition = beforeCursor.length + text.length;
1075
- editor.selectionStart = cursorPosition;
1076
- editor.selectionEnd = cursorPosition;
1077
-
 
 
1078
  updateCounts();
1079
  saveToHistoryStack();
1080
  scheduleDraftSave();
1081
  }
 
 
 
 
1082
 
1083
  // --- SHARE FUNCTIONALITY ---
1084
  function shareText() {
@@ -1298,6 +1483,10 @@
1298
  saveToHistoryStack();
1299
  scheduleDraftSave();
1300
  cursorPosition = item.text.length;
 
 
 
 
1301
  }
1302
  }
1303
 
@@ -1396,6 +1585,8 @@
1396
  saveToHistoryStack();
1397
  clearDraftText();
1398
  cursorPosition = 0;
 
 
1399
  showToast("🗑️ Text cleared");
1400
  }
1401
 
 
622
 
623
  // Cursor position
624
  var cursorPosition = 0;
625
+ var cursorEndPosition = 0;
626
+ var editorScrollTop = 0;
627
+ var editorScrollLeft = 0;
628
+ var speechInsertLock = false;
629
+ var micTransitionLock = false;
630
 
631
  // 36 Years in milliseconds
632
  var RETENTION_TIME = 1135296000000;
 
653
  var editor = document.getElementById('textEditor');
654
  editor.addEventListener('input', updateCounts);
655
  editor.addEventListener('input', scheduleDraftSave);
656
+ editor.addEventListener('input', saveCursorPosition);
657
  editor.addEventListener('click', saveCursorPosition);
658
  editor.addEventListener('keyup', saveCursorPosition);
659
  editor.addEventListener('keydown', saveCursorPosition);
660
+ editor.addEventListener('select', saveCursorPosition);
661
+ editor.addEventListener('mouseup', saveCursorPosition);
662
+ editor.addEventListener('touchend', saveCursorPosition);
663
+ editor.addEventListener('scroll', rememberEditorScrollPosition);
664
+ editor.addEventListener('blur', saveCursorPosition);
665
 
666
  // Initialize undo/redo
667
  saveToHistoryStack();
 
728
  }
729
 
730
  if (final) {
731
+ insertSpeechTextAtSavedCursor(final);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
732
  }
733
  };
734
 
 
770
  }
771
 
772
  function toggleMic() {
773
+ if (micTransitionLock) return;
774
+ micTransitionLock = true;
 
 
 
775
 
776
+ try {
777
+ saveCursorPosition();
778
+
779
+ if (!recognition) {
 
 
 
 
 
 
 
 
 
 
 
780
  recognition = initSpeech();
 
 
 
781
  }
782
+
783
+ if (!recognition) {
784
+ micTransitionLock = false;
785
+ return;
786
+ }
787
+
788
+ if (isRecording) {
789
+ isRecording = false;
790
+ try {
791
+ recognition.stop();
792
+ } catch(e) {}
793
+ updateMicUI(false);
794
+ showToast("⏹️ Recording stopped");
795
+ setTimeout(function() { micTransitionLock = false; }, 250);
796
+ } else {
797
+ try {
798
+ recognition.start();
799
+ showToast("🎤 Recording... Speak now!");
800
+ setTimeout(function() { micTransitionLock = false; }, 250);
801
+ } catch(e) {
802
+ console.error(e);
803
+ showToast("⚠️ Error starting microphone");
804
+ recognition = initSpeech();
805
+ setTimeout(function() {
806
+ try { recognition.start(); } catch(e2) {}
807
+ micTransitionLock = false;
808
+ }, 500);
809
+ }
810
+ }
811
+ } catch(error) {
812
+ console.error(error);
813
+ micTransitionLock = false;
814
  }
815
  }
816
 
 
938
  editor.value = payload.text;
939
  currentTextState = payload.text;
940
  cursorPosition = payload.text.length;
941
+ cursorEndPosition = cursorPosition;
942
  editor.selectionStart = cursorPosition;
943
  editor.selectionEnd = cursorPosition;
944
+ saveCursorPosition();
945
 
946
  if (payload.lang === 'ur-PK' || payload.lang === 'en-US' || payload.lang === 'en-PK') {
947
  setLang(payload.lang);
 
1055
  document.getElementById('redoBtn').disabled = redoStack.length === 0;
1056
  }
1057
 
1058
+ // ═══════════════════════════════════════════════════════════════════════════════
1059
+ // کرسر اور اسکرول لاک سسٹم - جاوا اسکرپٹ
1060
+ // CURSOR AND SCROLL LOCK SYSTEM - JAVASCRIPT
1061
+ // ═══════════════════════════════════════════════════════════════════════════════
1062
+ function rememberEditorScrollPosition() {
1063
+ var editor = document.getElementById('textEditor');
1064
+ if (!editor) return;
1065
+
1066
+ editorScrollTop = editor.scrollTop || 0;
1067
+ editorScrollLeft = editor.scrollLeft || 0;
1068
+ }
1069
+
1070
  function saveCursorPosition() {
1071
  var editor = document.getElementById('textEditor');
1072
+ if (!editor) return;
1073
+
1074
+ cursorPosition = typeof editor.selectionStart === 'number' ? editor.selectionStart : cursorPosition;
1075
+ cursorEndPosition = typeof editor.selectionEnd === 'number' ? editor.selectionEnd : cursorPosition;
1076
+ editorScrollTop = editor.scrollTop || 0;
1077
+ editorScrollLeft = editor.scrollLeft || 0;
1078
+
1079
+ var cursorLabel = document.getElementById('cursorPos');
1080
+ if (cursorLabel) cursorLabel.textContent = cursorPosition;
1081
+ }
1082
+
1083
+ function getTextareaCaretPixelTop(editor, caretPos) {
1084
+ if (!editor) return 0;
1085
+
1086
+ var style = window.getComputedStyle(editor);
1087
+ var mirror = document.createElement('div');
1088
+ var marker = document.createElement('span');
1089
+ var properties = [
1090
+ 'boxSizing', 'width', 'height', 'overflowX', 'overflowY', 'borderTopWidth',
1091
+ 'borderRightWidth', 'borderBottomWidth', 'borderLeftWidth', 'paddingTop',
1092
+ 'paddingRight', 'paddingBottom', 'paddingLeft', 'fontStyle', 'fontVariant',
1093
+ 'fontWeight', 'fontStretch', 'fontSize', 'fontSizeAdjust', 'lineHeight',
1094
+ 'fontFamily', 'textAlign', 'textTransform', 'textIndent', 'textDecoration',
1095
+ 'letterSpacing', 'wordSpacing', 'tabSize', 'MozTabSize', 'direction',
1096
+ 'whiteSpace', 'wordBreak', 'overflowWrap'
1097
+ ];
1098
+
1099
+ mirror.style.position = 'absolute';
1100
+ mirror.style.visibility = 'hidden';
1101
+ mirror.style.left = '-99999px';
1102
+ mirror.style.top = '0';
1103
+ mirror.style.whiteSpace = 'pre-wrap';
1104
+ mirror.style.wordBreak = 'break-word';
1105
+ mirror.style.overflowWrap = 'break-word';
1106
+
1107
+ properties.forEach(function(prop) {
1108
+ try { mirror.style[prop] = style[prop]; } catch (error) {}
1109
+ });
1110
+
1111
+ mirror.style.width = editor.offsetWidth + 'px';
1112
+ mirror.textContent = editor.value.substring(0, caretPos);
1113
+ marker.textContent = editor.value.substring(caretPos, caretPos + 1) || '.';
1114
+ mirror.appendChild(marker);
1115
+ document.body.appendChild(mirror);
1116
+
1117
+ var top = marker.offsetTop;
1118
+ document.body.removeChild(mirror);
1119
+ return top;
1120
+ }
1121
+
1122
+ function keepCaretVisibleWithoutJump(editor, caretPos) {
1123
+ if (!editor) return;
1124
+
1125
+ var style = window.getComputedStyle(editor);
1126
+ var lineHeight = parseFloat(style.lineHeight);
1127
+ if (!lineHeight || isNaN(lineHeight)) {
1128
+ lineHeight = Math.max(18, parseFloat(style.fontSize) * 1.6);
1129
+ }
1130
+
1131
+ var paddingTop = parseFloat(style.paddingTop) || 0;
1132
+ var paddingBottom = parseFloat(style.paddingBottom) || 0;
1133
+ var caretTop = getTextareaCaretPixelTop(editor, caretPos);
1134
+ var visibleTop = editor.scrollTop + paddingTop;
1135
+ var visibleBottom = editor.scrollTop + editor.clientHeight - paddingBottom - lineHeight;
1136
+
1137
+ if (caretTop < visibleTop) {
1138
+ editor.scrollTop = Math.max(0, caretTop - paddingTop - lineHeight);
1139
+ } else if (caretTop > visibleBottom) {
1140
+ editor.scrollTop = Math.max(0, caretTop - editor.clientHeight + paddingBottom + (lineHeight * 2));
1141
+ }
1142
+
1143
+ editorScrollTop = editor.scrollTop || 0;
1144
+ editorScrollLeft = editor.scrollLeft || 0;
1145
+ }
1146
+
1147
+ function restoreEditorViewportAndCaret(startPos, endPos, keepFocus, scrollMode) {
1148
+ var editor = document.getElementById('textEditor');
1149
+ if (!editor) return;
1150
+
1151
+ var safeStart = Math.max(0, Math.min(startPos, editor.value.length));
1152
+ var safeEnd = Math.max(0, Math.min(endPos, editor.value.length));
1153
+
1154
+ editor.selectionStart = safeStart;
1155
+ editor.selectionEnd = safeEnd;
1156
+ editor.scrollTop = editorScrollTop;
1157
+ editor.scrollLeft = editorScrollLeft;
1158
+
1159
+ if (scrollMode === 'keep-caret-visible') {
1160
+ keepCaretVisibleWithoutJump(editor, safeStart);
1161
+ }
1162
+
1163
+ if (keepFocus) {
1164
+ try {
1165
+ editor.focus({ preventScroll: true });
1166
+ } catch (error) {
1167
+ editor.focus();
1168
+ editor.scrollTop = editorScrollTop;
1169
+ editor.scrollLeft = editorScrollLeft;
1170
+ if (scrollMode === 'keep-caret-visible') {
1171
+ keepCaretVisibleWithoutJump(editor, safeStart);
1172
+ }
1173
+ }
1174
+ }
1175
+
1176
+ requestAnimationFrame(function() {
1177
+ editor.selectionStart = safeStart;
1178
+ editor.selectionEnd = safeEnd;
1179
+ editor.scrollTop = editorScrollTop;
1180
+ editor.scrollLeft = editorScrollLeft;
1181
+
1182
+ if (scrollMode === 'keep-caret-visible') {
1183
+ keepCaretVisibleWithoutJump(editor, safeStart);
1184
+ }
1185
+ });
1186
+ }
1187
+
1188
+ function normalizeInsertedSpeechText(text, beforeText, afterText) {
1189
+ var cleanText = (text || '').replace(/\s+/g, ' ').trim();
1190
+ if (!cleanText) return '';
1191
+
1192
+ if (beforeText.length > 0 && !/[\s\n]$/.test(beforeText)) {
1193
+ cleanText = ' ' + cleanText;
1194
+ }
1195
+
1196
+ if (afterText.length > 0 && !/^[\s\n.,!?;:،۔؟]/.test(afterText)) {
1197
+ cleanText = cleanText + ' ';
1198
+ } else if (afterText.length === 0) {
1199
+ cleanText = cleanText + ' ';
1200
+ }
1201
+
1202
+ return cleanText;
1203
+ }
1204
+
1205
+ function insertSpeechTextAtSavedCursor(text) {
1206
+ if (speechInsertLock) return;
1207
+ speechInsertLock = true;
1208
+
1209
+ try {
1210
+ var editor = document.getElementById('textEditor');
1211
+ if (!editor) return;
1212
+
1213
+ var savedScrollTop = editorScrollTop;
1214
+ var savedScrollLeft = editorScrollLeft;
1215
+ var start = Math.max(0, Math.min(cursorPosition, editor.value.length));
1216
+ var end = Math.max(start, Math.min(cursorEndPosition || start, editor.value.length));
1217
+ var beforeCursor = editor.value.substring(0, start);
1218
+ var afterCursor = editor.value.substring(end);
1219
+ var finalText = normalizeInsertedSpeechText(text, beforeCursor, afterCursor);
1220
+
1221
+ if (!finalText) return;
1222
+
1223
+ editorScrollTop = savedScrollTop;
1224
+ editorScrollLeft = savedScrollLeft;
1225
+ editor.value = beforeCursor + finalText + afterCursor;
1226
+
1227
+ cursorPosition = beforeCursor.length + finalText.length;
1228
+ cursorEndPosition = cursorPosition;
1229
+
1230
+ restoreEditorViewportAndCaret(cursorPosition, cursorPosition, true, 'keep-caret-visible');
1231
+ updateCounts();
1232
+ saveToHistoryStack();
1233
+ scheduleDraftSave();
1234
+ } finally {
1235
+ speechInsertLock = false;
1236
+ }
1237
  }
1238
 
1239
  function insertAtCursor(text) {
1240
  var editor = document.getElementById('textEditor');
1241
+ if (!editor) return;
1242
+
1243
+ saveCursorPosition();
1244
+
1245
+ var savedScrollTop = editorScrollTop;
1246
+ var savedScrollLeft = editorScrollLeft;
1247
+ var start = Math.max(0, Math.min(cursorPosition, editor.value.length));
1248
+ var end = Math.max(start, Math.min(cursorEndPosition || start, editor.value.length));
1249
+ var beforeCursor = editor.value.substring(0, start);
1250
+ var afterCursor = editor.value.substring(end);
1251
+
1252
  editor.value = beforeCursor + text + afterCursor;
1253
  cursorPosition = beforeCursor.length + text.length;
1254
+ cursorEndPosition = cursorPosition;
1255
+ editorScrollTop = savedScrollTop;
1256
+ editorScrollLeft = savedScrollLeft;
1257
+
1258
+ restoreEditorViewportAndCaret(cursorPosition, cursorPosition, true, 'keep-caret-visible');
1259
  updateCounts();
1260
  saveToHistoryStack();
1261
  scheduleDraftSave();
1262
  }
1263
+ // ═══════════════════════════════════════════════════════════════════════════════
1264
+ // ختم کرسر اور اسکرول لاک سسٹم - جاوا اسکرپٹ
1265
+ // END CURSOR AND SCROLL LOCK SYSTEM - JAVASCRIPT
1266
+ // ═══════════════════════════════════════════════════════════════════════════════
1267
 
1268
  // --- SHARE FUNCTIONALITY ---
1269
  function shareText() {
 
1483
  saveToHistoryStack();
1484
  scheduleDraftSave();
1485
  cursorPosition = item.text.length;
1486
+ cursorEndPosition = cursorPosition;
1487
+ document.getElementById('textEditor').selectionStart = cursorPosition;
1488
+ document.getElementById('textEditor').selectionEnd = cursorPosition;
1489
+ saveCursorPosition();
1490
  }
1491
  }
1492
 
 
1585
  saveToHistoryStack();
1586
  clearDraftText();
1587
  cursorPosition = 0;
1588
+ cursorEndPosition = 0;
1589
+ saveCursorPosition();
1590
  showToast("🗑️ Text cleared");
1591
  }
1592