SmartTyping AI – Multi-Language Typing & Smart Editor
Size:
शब्द: 0
अक्षर: 0
बिना स्पेस के: 0
हिन्दी ट्रांसलिटरेशन चालू
Action Done
`;
document.execCommand('insertHTML', false, table);
});
// Insert Link
document.getElementById('btnInsertLink').addEventListener('click', () => {
const url = prompt('URL दर्ज करें (उदा. https://google.com):');
if (url) document.execCommand('createLink', false, url);
});
// Copy All
document.getElementById('btnCopy').addEventListener('click', () => {
const text = editor.innerText.trim();
if (!text) return showToast('कैनवास खाली है!');
navigator.clipboard.writeText(text).then(() => showToast('टेक्स्ट कॉपी हो गया!'));
});
// Print Only Canvas Cleanly
document.getElementById('btnPrint').addEventListener('click', () => {
hideSuggestions();
window.print();
});
// Clear Canvas
document.getElementById('btnClear').addEventListener('click', () => {
if (confirm('क्या आप पूरा टेक्स्ट हटाना चाहते हैं?')) {
editor.innerHTML = '';
updateStats();
hideSuggestions();
showToast('कैनवास साफ़ हो गया!');
}
});
// Download TXT
document.getElementById('btnDownloadTxt').addEventListener('click', () => {
const blob = new Blob([editor.innerText], { type: 'text/plain;charset=utf-8' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = `SmartTyping_${Date.now()}.txt`;
a.click();
});
// Download DOC
document.getElementById('btnDownloadDoc').addEventListener('click', () => {
const html = `
${editor.innerHTML}`;
const blob = new Blob(['\ufeff' + html], { type: 'application/msword' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = `SmartTyping_${Date.now()}.doc`;
a.click();
});
// ----------------------------------------------------
// Floating Transliteration Popup (Google Suggestions 1-6)
// ----------------------------------------------------
async function fetchSuggestions(word) {
try {
const res = await fetch(`https://inputtools.google.com/request?text=${encodeURIComponent(word)}&itc=${currentLangCode}&num=5`);
const data = await res.json();
if (data && data[0] === 'SUCCESS' && data[1] && data[1][0] && data[1][0][1]) {
return [...data[1][0][1], word]; // Adds original English word as last option
}
} catch (err) {
console.warn('API error', err);
}
return [word];
}
function hideSuggestions() {
suggestionBox.style.display = 'none';
suggestionBox.innerHTML = '';
activeSuggestions = [];
targetWordRange = null;
currentWordText = '';
}
function renderSuggestions(list, rect) {
activeSuggestions = list;
activeIndex = 0;
suggestionBox.innerHTML = '';
list.slice(0, 6).forEach((item, idx) => {
const div = document.createElement('div');
div.className = `sugg-item ${idx === 0 ? 'active' : ''}`;
div.innerHTML = `
${idx + 1}.${item}`;
div.addEventListener('mousedown', (e) => {
e.preventDefault();
selectSuggestion(idx);
});
suggestionBox.appendChild(div);
});
// Position popup near cursor
const editorRect = editor.getBoundingClientRect();
const top = (rect ? rect.bottom : editorRect.top + 40) + window.scrollY + 4;
const left = Math.min((rect ? rect.left : editorRect.left + 20) + window.scrollX, window.innerWidth - 220);
suggestionBox.style.top = `${top}px`;
suggestionBox.style.left = `${left}px`;
suggestionBox.style.display = 'block';
}
function selectSuggestion(index) {
const chosen = activeSuggestions[index] || currentWordText;
if (targetWordRange) {
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(targetWordRange);
document.execCommand('insertText', false, chosen + ' ');
}
hideSuggestions();
updateStats();
}
// Real-time Input Listeners for Suggestions
editor.addEventListener('keyup', async (e) => {
if (!transliterationToggle.checked) return hideSuggestions();
// Skip keys handled in keydown
if (['ArrowDown', 'ArrowUp', 'Enter', 'Escape'].includes(e.key)) return;
const sel = window.getSelection();
if (!sel.isCollapsed || !sel.focusNode) return hideSuggestions();
const node = sel.focusNode;
if (node.nodeType !== Node.TEXT_NODE) return hideSuggestions();
const textBefore = node.textContent.substring(0, sel.focusOffset);
const match = textBefore.match(/([A-Za-z]+)$/);
if (match) {
const word = match[1];
currentWordText = word;
const range = document.createRange();
range.setStart(node, match.index);
range.setEnd(node, sel.focusOffset);
targetWordRange = range;
const rect = range.getBoundingClientRect();
const list = await fetchSuggestions(word);
if (list && list.length > 0) {
renderSuggestions(list, rect);
}
} else {
hideSuggestions();
}
});
editor.addEventListener('keydown', (e) => {
if (suggestionBox.style.display === 'block' && activeSuggestions.length > 0) {
// Navigate with Down / Up Arrow
if (e.key === 'ArrowDown') {
e.preventDefault();
activeIndex = (activeIndex + 1) % Math.min(activeSuggestions.length, 6);
updateActiveItem();
return;
}
if (e.key === 'ArrowUp') {
e.preventDefault();
activeIndex = (activeIndex - 1 + Math.min(activeSuggestions.length, 6)) % Math.min(activeSuggestions.length, 6);
updateActiveItem();
return;
}
// Select with Number keys 1-6
if (/^[1-6]$/.test(e.key)) {
const num = parseInt(e.key, 10) - 1;
if (num < activeSuggestions.length) {
e.preventDefault();
selectSuggestion(num);
return;
}
}
// Select with Space or Enter
if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault();
selectSuggestion(activeIndex);
return;
}
// Close with Escape
if (e.key === 'Escape') {
hideSuggestions();
return;
}
}
});
function updateActiveItem() {
const items = suggestionBox.querySelectorAll('.sugg-item');
items.forEach((it, idx) => {
it.classList.toggle('active', idx === activeIndex);
});
}
// Close suggestion box on outside click
document.addEventListener('mousedown', (e) => {
if (!suggestionBox.contains(e.target) && e.target !== editor) {
hideSuggestions();
}
});
// ----------------------------------------------------
// Voice Typing (Inserts at Current Cursor Position)
// ----------------------------------------------------
const btnVoice = document.getElementById('btnVoice');
const voiceLabel = document.getElementById('voiceLabel');
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (SpeechRecognition) {
const recognition = new SpeechRecognition();
recognition.continuous = true;
recognition.interimResults = false;
let isRecording = false;
recognition.onstart = () => {
isRecording = true;
btnVoice.classList.add('mic-recording');
voiceLabel.innerText = 'बोलिए...';
};
recognition.onend = () => {
isRecording = false;
btnVoice.classList.remove('mic-recording');
voiceLabel.innerText = 'Voice Typing';
};
recognition.onresult = (evt) => {
let text = '';
for (let i = evt.resultIndex; i < evt.results.length; i++) {
if (evt.results[i].isFinal) {
text += evt.results[i][0].transcript + ' ';
}
}
if (text) {
insertAtCursor(text);
}
};
btnVoice.addEventListener('click', () => {
if (!isRecording) {
const currentSelectedOption = languageSelect.options[languageSelect.selectedIndex];
const voiceLang = currentSelectedOption.getAttribute('data-lang') || 'hi';
recognition.lang = `${voiceLang}-IN`;
recognition.start();
} else {
recognition.stop();
}
});
} else {
btnVoice.addEventListener('click', () => showToast('ब्राउज़र वॉइस टाइपिंग को सपोर्ट नहीं करता।'));
}
// ----------------------------------------------------
// Utilities Tabs Switcher
// ----------------------------------------------------
document.querySelectorAll('.util-tab').forEach(tab => {
tab.addEventListener('click', () => {
document.querySelectorAll('.util-tab').forEach(t => {
t.className = 'util-tab px-4 py-2.5 text-xs font-semibold text-slate-500 hover:text-slate-800 dark:hover:text-slate-200';
});
tab.className = 'util-tab px-4 py-2.5 text-xs font-semibold border-b-2 border-brand-600 text-brand-600 dark:text-brand-300';
['tabTranslate', 'tabSpeech', 'tabKrutidev'].forEach(id => {
document.getElementById(id).classList.add('hidden');
});
document.getElementById(tab.getAttribute('data-target')).classList.remove('hidden');
});
});
// Text to Speech
document.getElementById('btnPlaySpeech').addEventListener('click', () => {
const txt = editor.innerText.trim();
if (!txt) return showToast('पढ़ने के लिए कोई टेक्स्ट नहीं है!');
window.speechSynthesis.cancel();
const ut = new SpeechSynthesisUtterance(txt);
ut.lang = 'hi-IN';
ut.rate = parseFloat(document.getElementById('speechRate').value) || 1;
window.speechSynthesis.speak(ut);
});
document.getElementById('btnStopSpeech').addEventListener('click', () => window.speechSynthesis.cancel());
// Language Converter / Translator Utility
document.getElementById('btnLoadFromEditor').addEventListener('click', () => {
document.getElementById('translateInput').value = editor.innerText.trim();
});
document.getElementById('btnDoConvert').addEventListener('click', async () => {
const text = document.getElementById('translateInput').value.trim();
const targetLang = document.getElementById('targetConvertLang').value;
if (!text) return showToast('कृपया टेक्स्ट दर्ज करें!');
try {
showToast('कन्वर्ट हो रहा है...');
const res = await fetch(`https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}`);
const data = await res.json();
let result = '';
if (data && data[0]) {
data[0].forEach(part => { if (part[0]) result += part[0]; });
}
document.getElementById('translateOutput').value = result;
showToast('कन्वर्शन पूर्ण!');
} catch (err) {
showToast('अनुवाद सर्वर से कनेक्ट नहीं हो सका');
}
});
document.getElementById('btnAppendToEditor').addEventListener('click', () => {
const txt = document.getElementById('translateOutput').value;
if (txt) {
insertAtCursor('\n' + txt);
showToast('एडिटर में जोड़ दिया गया!');
}
});
document.getElementById('btnCopyConverted').addEventListener('click', () => {
const txt = document.getElementById('translateOutput').value;
if (txt) {
navigator.clipboard.writeText(txt);
showToast('कॉपी हो गया!');
}
});
// Krutidev to Unicode Quick Engine
const krutiMap = {
'k': 'ा', 'l': 'ी', 'f': 'ि', 'h': 'ी', 's': 'े', 'S': 'ै',
'q': 'ु', 'w': 'ू', 'e': 'म', 'r': 'त', 't': 'ज', 'y': 'ल',
'u': 'न', 'i': 'प', 'o': 'व', 'p': 'च', 'd': 'क', 'g': 'ह',
'j': 'र', 'm': 'स', 'b': 'द', 'v': 'अ', 'x': 'ग', 'c': 'ब'
};
document.getElementById('btnConvertKruti').addEventListener('click', () => {
const inp = document.getElementById('krutiInput').value;
let out = '';
for (let ch of inp) out += krutiMap[ch] || ch;
document.getElementById('unicodeOutput').value = out;
});
document.getElementById('btnSendKrutiToEditor').addEventListener('click', () => {
const val = document.getElementById('unicodeOutput').value;
if (val) insertAtCursor(val);
});
});