import {render, styles, resolveFont} from './renderer.js'; import {translations} from './translations.js'; import {setupPWA} from './pwa.js'; const text = document.getElementById('text'); const style = document.getElementById('style'); const output = document.getElementById('output'); const status = document.getElementById('status'); const language = document.getElementById('language'); const theme = document.getElementById('theme'); let lang = document.documentElement.lang; let statusKey = ''; const t = key => translations[lang][key]; const showStatus = key => { statusKey = key; status.textContent = key ? t(key) : ''; }; const savePreference = (key, value) => { try { localStorage.setItem(`ascii-pwa-${key}`, value); } catch { /* Storage may be disabled. */ } }; function translate() { document.documentElement.lang = lang; language.value = lang; document.querySelectorAll('[data-i18n]').forEach(element => { element.textContent = t(element.dataset.i18n); }); output.setAttribute('aria-label', t('outputLabel')); document.querySelector('.logo').setAttribute('aria-label', t('homeLabel')); updateFontNavigation(); showStatus(statusKey); } let drawVersion = 0; let drawTimer; function updateFontNavigation() { const next = styles[(styles.indexOf(style.value) + 1) % styles.length]; document.getElementById('next-font').textContent = t('nextFont').replace('{name}', next); } async function draw() { const version = ++drawVersion; const copy = document.getElementById('copy'); const download = document.getElementById('download'); copy.disabled = true; download.disabled = true; try { const result = await render(text.value, style.value); if (version !== drawVersion) return; output.textContent = result; showStatus(''); } catch { if (version !== drawVersion) return; output.textContent = ''; showStatus('renderFailed'); } document.getElementById('copy').disabled = !output.textContent.trim(); document.getElementById('download').disabled = !output.textContent.trim(); } // Explicit shared URLs take precedence; a normal visit always opens the homepage. const params = new URL(window.location.href).searchParams; style.replaceChildren(...styles.map(font => { const option = document.createElement('option'); option.value = font; option.textContent = font; return option; })); style.value = 'Standard'; try { const savedFont = localStorage.getItem('ascii-pwa-font'); if (savedFont) style.value = resolveFont(savedFont); } catch { /* Use the default when saved preferences are unavailable. */ } if (params.has('text')) text.value = params.get('text').slice(0, 500); if (params.has('font')) style.value = resolveFont(params.get('font')); savePreference('font', style.value); theme.value = document.documentElement.dataset.theme; translate(); draw(); document.getElementById('render-form').addEventListener('submit', event => { event.preventDefault(); draw(); }); style.addEventListener('change', () => { clearTimeout(drawTimer); savePreference('font', style.value); updateFontNavigation(); draw(); }); text.addEventListener('input', () => { clearTimeout(drawTimer); ++drawVersion; document.getElementById('copy').disabled = true; document.getElementById('download').disabled = true; drawTimer = setTimeout(draw, 150); }); for (const [id, direction] of [['prev-font', -1], ['next-font', 1]]) { document.getElementById(id).addEventListener('click', () => { const index = (styles.indexOf(style.value) + direction + styles.length) % styles.length; style.value = styles[index]; style.dispatchEvent(new Event('change')); }); } language.addEventListener('change', () => { lang = language.value; savePreference('language', lang); translate(); }); theme.addEventListener('change', () => { document.documentElement.dataset.theme = theme.value; savePreference('theme', theme.value); }); function copyText(value) { const field = document.createElement('textarea'); const focused = document.activeElement; field.value = value; field.readOnly = true; field.style.cssText = 'position:fixed;left:-9999px;top:0;font-size:16px'; document.body.append(field); try { field.focus({preventScroll: true}); field.select(); field.setSelectionRange(0, field.value.length); if (!document.execCommand('copy')) throw new Error('Copy failed'); } finally { field.remove(); focused?.focus({preventScroll: true}); } } document.getElementById('copy').addEventListener('click', () => { try { copyText(output.textContent); showStatus('copied'); } catch { const range = document.createRange(); range.selectNodeContents(output); window.getSelection().removeAllRanges(); window.getSelection().addRange(range); showStatus('copyFailed'); } }); document.getElementById('download').addEventListener('click', () => { const url = URL.createObjectURL(new Blob([output.textContent + '\n'], {type: 'text/plain;charset=utf-8'})); const link = document.createElement('a'); link.href = url; link.download = 'ascii-art.txt'; document.body.append(link); link.click(); link.remove(); setTimeout(() => URL.revokeObjectURL(url), 1000); showStatus('downloaded'); }); document.getElementById('share').addEventListener('click', () => { const url = new URL('./', window.location.href); url.searchParams.set('text', text.value); url.searchParams.set('font', style.value); try { copyText(url.href); showStatus('linkCopied'); } catch { window.history.replaceState(null, '', url); showStatus('linkFailed'); } }); setupPWA({ failed() { showStatus('offlineFailed'); }, });