// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // DONAU2SPACE // DEV ENTITY — Utilities // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ export const $ = (sel, ctx = document) => ctx.querySelector(sel); export const $$ = (sel, ctx = document) => [...ctx.querySelectorAll(sel)]; export const sleep = ms => new Promise(r => setTimeout(r, ms)); export const random = (min, max) => Math.random() * (max - min) + min; export const randInt = (min, max) => (random(min, max) | 0); export const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v)); export const lerp = (a, b, t) => a + (b - a) * t; export const pick = arr => arr[(Math.random() * arr.length) | 0]; export function esc(s) { const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return (s ?? '').replace(/[&<>"']/g, m => map[m]); } export function nowISO() { return new Date().toISOString().replace('T', ' ').replace('Z', ' UTC'); } export function timeGreeting() { const h = new Date().getHours(); if (h < 5) return 'Deep night. Perfect time for dev.'; if (h < 8) return 'Early bird mode. Coffee recommended.'; if (h < 12) return 'Morning session. Systems nominal.'; if (h < 14) return 'Midday. Hunger may affect judgment.'; if (h < 18) return 'Afternoon cycle. Productivity variable.'; if (h < 22) return 'Evening mode. Creative peak for some.'; return 'Late night. Here be dragons.'; } // ── Event Bus ────────────────────────────────── const listeners = {}; export const bus = { on(event, fn) { (listeners[event] = listeners[event] || []).push(fn); return () => this.off(event, fn); }, off(event, fn) { listeners[event] = (listeners[event] || []).filter(f => f !== fn); }, emit(event, data) { (listeners[event] || []).forEach(fn => { try { fn(data); } catch (e) { console.warn(`[bus:${event}]`, e); } }); } }; // ── Storage helpers ──────────────────────────── const PREFIX = 'd2s_'; export const store = { get(key, fallback = null) { try { const raw = localStorage.getItem(PREFIX + key); return raw !== null ? JSON.parse(raw) : fallback; } catch { return fallback; } }, set(key, val) { try { localStorage.setItem(PREFIX + key, JSON.stringify(val)); } catch {} }, remove(key) { try { localStorage.removeItem(PREFIX + key); } catch {} } }; // ── DOM helper ───────────────────────────────── export function el(tag, attrs = {}, children = []) { const node = document.createElement(tag); for (const [k, v] of Object.entries(attrs)) { if (k === 'className') node.className = v; else if (k === 'textContent') node.textContent = v; else if (k.startsWith('on')) node.addEventListener(k.slice(2).toLowerCase(), v); else node.setAttribute(k, v); } for (const c of children) { node.appendChild(typeof c === 'string' ? document.createTextNode(c) : c); } return node; }