mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-10-01 17:29:29 +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