mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-10-01 09:19:34 +05:30
feat(ascii-art): renderer controller for standalone window (font picker + copy/save/insert)
Amit Haridas
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* ASCII Art Generator — renderer controller for the standalone window.
|
||||
*
|
||||
* Drives the DOM in src/ascii-generator.html. Fetches the font list from
|
||||
* the main process once on mount, renders previews on input, and exposes
|
||||
* Insert / Copy / Save actions. Last-used font is restored from
|
||||
* electron-store (via main) and saved on every change.
|
||||
*
|
||||
* No bundler, no framework — pure DOM + window.electronAPI bridge.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
const api =
|
||||
window.electronAPI && window.electronAPI.generators && window.electronAPI.generators.ascii;
|
||||
const els = {
|
||||
textInput: document.getElementById('text-input'),
|
||||
fontPicker: document.getElementById('font-picker'),
|
||||
fontSearch: document.getElementById('font-search'),
|
||||
preview: document.getElementById('preview'),
|
||||
btnInsert: document.getElementById('btn-insert'),
|
||||
btnCopy: document.getElementById('btn-copy'),
|
||||
btnSave: document.getElementById('btn-save'),
|
||||
btnGenerate: document.getElementById('btn-generate'),
|
||||
warning: document.getElementById('ascii-warning'),
|
||||
};
|
||||
|
||||
let _fonts = [];
|
||||
let _currentFont = 'standard';
|
||||
let _previewText = '';
|
||||
let _searchDebounce = null;
|
||||
|
||||
function showWarning(msg) {
|
||||
if (!els.warning) return;
|
||||
els.warning.textContent = msg || '';
|
||||
els.warning.style.display = msg ? 'block' : 'none';
|
||||
}
|
||||
|
||||
function debounce(fn, ms) {
|
||||
let t = null;
|
||||
return function () {
|
||||
const args = arguments;
|
||||
clearTimeout(t);
|
||||
t = setTimeout(() => fn.apply(null, args), ms);
|
||||
};
|
||||
}
|
||||
|
||||
async function refreshPreview() {
|
||||
if (!api) return;
|
||||
const text = els.textInput ? els.textInput.value : 'HELLO';
|
||||
_previewText = text;
|
||||
try {
|
||||
const out = await api.generate({ text, font: _currentFont });
|
||||
els.preview.textContent = out;
|
||||
showWarning('');
|
||||
} catch (err) {
|
||||
els.preview.textContent = '';
|
||||
showWarning(`Failed to render: ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
function renderFontList(filter) {
|
||||
if (!els.fontPicker) return;
|
||||
const f = (filter || '').toLowerCase();
|
||||
const filtered = _fonts.filter((font) => {
|
||||
if (!f) return true;
|
||||
return font.label.toLowerCase().includes(f) || font.id.toLowerCase().includes(f);
|
||||
});
|
||||
els.fontPicker.innerHTML = '';
|
||||
for (const font of filtered.slice(0, 500)) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = font.id;
|
||||
opt.textContent = `${font.label} (${font.kind})`;
|
||||
if (font.id === _currentFont) opt.selected = true;
|
||||
els.fontPicker.appendChild(opt);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadFonts() {
|
||||
if (!api) return;
|
||||
try {
|
||||
_fonts = await api.listFonts();
|
||||
} catch {
|
||||
_fonts = [];
|
||||
showWarning('Could not load font list. Hand-coded fonts only.');
|
||||
}
|
||||
try {
|
||||
const last = await api.lastFont(null);
|
||||
if (last && _fonts.some((f) => f.id === last)) _currentFont = last;
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
renderFontList('');
|
||||
}
|
||||
|
||||
function wireEvents() {
|
||||
if (els.textInput) {
|
||||
els.textInput.addEventListener('input', debounce(refreshPreview, 200));
|
||||
}
|
||||
if (els.fontPicker) {
|
||||
els.fontPicker.addEventListener('change', () => {
|
||||
_currentFont = els.fontPicker.value;
|
||||
if (api) api.lastFont(_currentFont).catch(() => {});
|
||||
refreshPreview();
|
||||
});
|
||||
}
|
||||
if (els.fontSearch) {
|
||||
els.fontSearch.addEventListener('input', () => {
|
||||
clearTimeout(_searchDebounce);
|
||||
_searchDebounce = setTimeout(() => renderFontList(els.fontSearch.value), 100);
|
||||
});
|
||||
}
|
||||
if (els.btnGenerate) {
|
||||
els.btnGenerate.addEventListener('click', refreshPreview);
|
||||
}
|
||||
if (els.btnInsert) {
|
||||
els.btnInsert.addEventListener('click', () => {
|
||||
const wrapped =
|
||||
'```\n' + (_previewText ? document.getElementById('preview').textContent : '') + '\n```';
|
||||
if (window.electronAPI && window.electronAPI.send) {
|
||||
window.electronAPI.send('insert-generated-content', wrapped);
|
||||
window.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
if (els.btnCopy) {
|
||||
els.btnCopy.addEventListener('click', async () => {
|
||||
const text = els.preview.textContent;
|
||||
if (!text) return;
|
||||
try {
|
||||
await api.copy(text);
|
||||
showWarning('Copied to clipboard.');
|
||||
setTimeout(() => showWarning(''), 1500);
|
||||
} catch (err) {
|
||||
showWarning('Copy failed: ' + (err.message || err));
|
||||
}
|
||||
});
|
||||
}
|
||||
if (els.btnSave) {
|
||||
els.btnSave.addEventListener('click', async () => {
|
||||
const text = els.preview.textContent;
|
||||
if (!text) return;
|
||||
try {
|
||||
const result = await api.save({ text, defaultName: 'ascii-art.txt' });
|
||||
if (!result.canceled) {
|
||||
showWarning(`Saved to ${result.path}`);
|
||||
setTimeout(() => showWarning(''), 2000);
|
||||
}
|
||||
} catch (err) {
|
||||
showWarning('Save failed: ' + (err.message || err));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function bootstrap() {
|
||||
wireEvents();
|
||||
await loadFonts();
|
||||
await refreshPreview();
|
||||
}
|
||||
|
||||
window.ASCIIController = { bootstrap };
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', bootstrap);
|
||||
} else {
|
||||
bootstrap();
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user