-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.js
More file actions
142 lines (119 loc) · 4.77 KB
/
main.js
File metadata and controls
142 lines (119 loc) · 4.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Tell figlet.js where to load fonts from (unpkg CDN)
figlet.defaults({
fontPath: 'https://unpkg.com/figlet@1.7.0/fonts'
});
// Preload all fonts used in the select so switching feels instant
const FONTS = ['Big', 'Standard', 'Slant', 'Shadow', 'Small', 'Banner', 'ANSI Shadow'];
Promise.all(
FONTS.map(font => new Promise((resolve, reject) => {
figlet.preloadFonts([font], err => err ? reject(err) : resolve());
}))
).then(() => {
// Wire all controls to render
document.getElementById('main-text').addEventListener('input', render);
document.getElementById('subtitle-text').addEventListener('input', render);
document.getElementById('font-select').addEventListener('change', render);
document.getElementById('border-select').addEventListener('change', render);
document.getElementById('align-select').addEventListener('change', render);
render(); // initial render
}).catch(err => console.error('Font load error:', err));
let debounceTimer = null;
function render() {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => {
const mainText = document.getElementById('main-text').value;
const subtitle = document.getElementById('subtitle-text').value;
const font = document.getElementById('font-select').value;
const border = document.getElementById('border-select').value;
const align = document.getElementById('align-select').value;
const preview = document.getElementById('preview');
generateBanner(mainText, subtitle, font, border, align, result => {
preview.textContent = result;
});
}, 150);
}
// --- Banner generation ---
const BORDERS = {
box: { tl: '╔', tr: '╗', bl: '╚', br: '╝', h: '═', v: '║' },
ascii: { tl: '+', tr: '+', bl: '+', br: '+', h: '-', v: '|' },
none: null,
};
/**
* @param {string[]} lines - text lines to wrap (already aligned)
* @param {string} border - 'box' | 'ascii' | 'none'
* @param {string} align - 'left' | 'center' | 'right'
* @param {number} innerWidth - width of content area (excluding border chars)
* @returns {string}
*/
function applyBorder(lines, border, aligns, innerWidth) {
const b = BORDERS[border];
// Pad each line to innerWidth based on alignment
// aligns can be a single string (apply to all) or an array (per-line)
const padded = lines.map((line, i) => {
const align = Array.isArray(aligns) ? aligns[i] : aligns;
const len = line.length;
const gap = innerWidth - len;
if (align === 'left') return line + ' '.repeat(gap);
if (align === 'right') return ' '.repeat(gap) + line;
// center
const left = Math.floor(gap / 2);
const right = gap - left;
return ' '.repeat(left) + line + ' '.repeat(right);
});
if (!b) return padded.join('\n');
const top = b.tl + b.h.repeat(innerWidth + 2) + b.tr;
const bottom = b.bl + b.h.repeat(innerWidth + 2) + b.br;
const middle = padded.map(l => b.v + ' ' + l + ' ' + b.v);
return [top, ...middle, bottom].join('\n');
}
/**
* Generates the full banner string and passes it to callback.
* Uses figlet async API because font loading is async.
*/
function generateBanner(mainText, subtitle, font, border, align, callback) {
if (!mainText.trim()) {
callback('← type something above');
return;
}
figlet.text(mainText, { font }, (err, figletOutput) => {
if (err) {
callback('Error rendering font: ' + err.message);
return;
}
// figlet output lines (trim trailing whitespace per line)
const figletLines = figletOutput.split('\n').map(l => l.trimEnd());
// Build content lines: figlet art + optional subtitle
const contentLines = [...figletLines];
if (subtitle.trim()) {
contentLines.push(''); // blank spacer
contentLines.push(subtitle.trim());
}
// Inner width = longest line
const innerWidth = Math.max(...contentLines.map(l => l.length));
// figlet lines always left-aligned; only the subtitle uses the align control
const aligns = figletLines.map(() => 'left');
if (subtitle.trim()) {
aligns.push('left'); // blank spacer
aligns.push(align); // subtitle line
}
const result = applyBorder(contentLines, border, aligns, innerWidth);
callback(result);
});
}
// --- Copy to clipboard ---
document.getElementById('copy-btn').addEventListener('click', () => {
const text = document.getElementById('preview').textContent;
if (!text || text === '← type something above') return;
navigator.clipboard.writeText(text).then(() => {
const btn = document.getElementById('copy-btn');
const original = btn.textContent;
btn.textContent = 'Copied!';
btn.classList.add('copied');
setTimeout(() => {
btn.textContent = original;
btn.classList.remove('copied');
}, 1500);
}).catch(err => {
alert('Copy failed: ' + err.message);
});
});