asciitorium
Version:
an ASCII ui framework for web + cli
164 lines (163 loc) • 4.96 kB
JavaScript
export function resolveAlignment(align, parentWidth, parentHeight, childWidth, childHeight) {
let hAlign = 'left';
let vAlign = 'top';
if (typeof align === 'string') {
// Handle compound alignments like "top-left"
switch (align) {
case 'top-left':
vAlign = 'top';
hAlign = 'left';
break;
case 'top':
vAlign = 'top';
hAlign = 'center';
break;
case 'top-right':
vAlign = 'top';
hAlign = 'right';
break;
case 'left':
vAlign = 'middle';
hAlign = 'left';
break;
case 'center':
vAlign = 'middle';
hAlign = 'center';
break;
case 'right':
vAlign = 'middle';
hAlign = 'right';
break;
case 'bottom-left':
vAlign = 'bottom';
hAlign = 'left';
break;
case 'bottom':
vAlign = 'bottom';
hAlign = 'center';
break;
case 'bottom-right':
vAlign = 'bottom';
hAlign = 'right';
break;
default:
// fallback to top-left if unrecognized
vAlign = 'top';
hAlign = 'left';
break;
}
}
else if (typeof align === 'object' && align !== null) {
hAlign = align.x ?? 'left';
vAlign = align.y ?? 'top';
}
const padX = parentWidth - childWidth;
const padY = parentHeight - childHeight;
let x;
if (typeof hAlign === 'number')
x = hAlign;
else if (hAlign === 'center')
x = Math.floor(padX / 2);
else if (hAlign === 'right')
x = padX;
else
x = 0;
let y;
if (typeof vAlign === 'number')
y = vAlign;
else if (vAlign === 'middle')
y = Math.floor(padY / 2);
else if (vAlign === 'bottom')
y = padY;
else
y = 0;
return { x, y };
}
export function isState(v) {
return typeof v === 'object' && typeof v.subscribe === 'function';
}
export function isNodeEnvironment() {
return typeof process !== 'undefined' &&
typeof process.cpuUsage === 'function' &&
typeof process.memoryUsage === 'function';
}
export function isWebEnvironment() {
return typeof window !== 'undefined' && typeof document !== 'undefined';
}
export function isCPUSupported() {
return isNodeEnvironment();
}
export function isMemorySupported() {
return isNodeEnvironment() ||
(typeof performance !== 'undefined' && performance.memory);
}
export async function setupKeyboardHandling(handleKey) {
if (isWebEnvironment()) {
// Web environment
window.addEventListener('keydown', (event) => {
handleKey(event.key);
});
}
else {
// Terminal environment
const { default: readline } = await import('readline');
readline.emitKeypressEvents(process.stdin);
if (process.stdin.isTTY) {
process.stdin.setRawMode(true);
}
process.stdin.on('keypress', (_str, key) => {
const k = normalizeKey(key);
handleKey(k);
});
}
}
function normalizeKey(key) {
if (!key)
return '';
if (key.ctrl && key.name === 'c')
process.exit(); // Ctrl+C = quit
switch (key.name) {
case 'return':
return 'Enter';
case 'escape':
return 'Escape';
case 'backspace':
return 'Backspace';
case 'tab':
return 'Tab';
case 'up':
return 'ArrowUp';
case 'down':
return 'ArrowDown';
case 'left':
return 'ArrowLeft';
case 'right':
return 'ArrowRight';
default:
return key.sequence || '';
}
}
export function validateWebEnvironment() {
if (isWebEnvironment()) {
const screen = document.getElementById('screen');
if (!screen)
throw new Error('Missing #screen element for DOM renderer');
}
}
export async function loadAsciiAsset(relativePath) {
if (typeof window !== 'undefined' && typeof fetch !== 'undefined') {
// Web: use fetch from public path
return fetch(relativePath).then((res) => {
if (!res.ok)
throw new Error(`Failed to load ${relativePath}`);
return res.text();
});
}
else {
// Node: load from file system relative to the process CWD
const { readFile } = await import('fs/promises');
const { resolve } = await import('path');
const fullPath = resolve(process.cwd(), 'public', relativePath);
return readFile(fullPath, 'utf-8');
}
}