Sternboard: free retro board, no signup (initial)
Servidor Fastify + Socket.IO + Drizzle/Postgres; front Vite+React (EN/ES, tema claro/oscuro). Tableros por columnas con plantillas, notas privadas/públicas, agrupación, votos configurables (multivoto), timer compartido con alarma, fases, action items, export Markdown/JSON e import, autodestrucción, gestión de columnas. Imagen única (front servido por el servidor) y CI que la construye y publica.
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
// Alarma corta generada con Web Audio (sin ficheros). Dos pitidos rápidos.
|
||||
let ctx: AudioContext | null = null;
|
||||
|
||||
export function playAlarm() {
|
||||
try {
|
||||
const Ctor = window.AudioContext || (window as unknown as { webkitAudioContext: typeof AudioContext }).webkitAudioContext;
|
||||
ctx = ctx ?? new Ctor();
|
||||
if (ctx.state === "suspended") void ctx.resume();
|
||||
const now = ctx.currentTime;
|
||||
for (let i = 0; i < 2; i++) {
|
||||
const osc = ctx.createOscillator();
|
||||
const gain = ctx.createGain();
|
||||
osc.type = "square";
|
||||
osc.frequency.value = 880;
|
||||
const t = now + i * 0.18;
|
||||
gain.gain.setValueAtTime(0.0001, t);
|
||||
gain.gain.exponentialRampToValueAtTime(0.25, t + 0.01);
|
||||
gain.gain.exponentialRampToValueAtTime(0.0001, t + 0.15);
|
||||
osc.connect(gain);
|
||||
gain.connect(ctx.destination);
|
||||
osc.start(t);
|
||||
osc.stop(t + 0.16);
|
||||
}
|
||||
} catch {
|
||||
// sin audio disponible: ignoramos
|
||||
}
|
||||
}
|
||||
|
||||
// Parsea "mm", "mm:ss" o "m.5" a segundos. Devuelve null si no es válido.
|
||||
export function parseTime(input: string): number | null {
|
||||
const s = input.trim();
|
||||
if (!s) return null;
|
||||
if (s.includes(":")) {
|
||||
const [m, sec] = s.split(":");
|
||||
const mm = Number(m);
|
||||
const ss = Number(sec);
|
||||
if (!Number.isFinite(mm) || !Number.isFinite(ss) || ss < 0 || ss >= 60) return null;
|
||||
const total = Math.round(mm * 60 + ss);
|
||||
return total > 0 ? total : null;
|
||||
}
|
||||
const mins = Number(s);
|
||||
if (!Number.isFinite(mins) || mins <= 0) return null;
|
||||
return Math.round(mins * 60);
|
||||
}
|
||||
Reference in New Issue
Block a user