- Estante: entradas desde el menú, la librería y la ficha; orden reciente o A–Z; foto del estante, arte de lomos y portadas guardados en el dispositivo; sin conexión muestra solo lo guardado. - Notas de 40 letras sin cortar palabras, lomo curvo, sombra en las portadas de frente y lomos con logo sin el título. - Pósit cuadrado con el capítulo y la página por la que va el usuario; paso al lector sin pantalla de carga. - Giroscopio: integra rotationRate (sin los saltos de β/γ con el dispositivo vertical), tope suave, menos sensibilidad al caminar, lado en horizontal según la gravedad, suavizado por tiempo y más rango en el estante y el libro. - Laboratorio del giroscopio en Ajustes (temporal, solo desarrollo) para grabar los sensores. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
38 lines
1.7 KiB
JavaScript
38 lines
1.7 KiB
JavaScript
// Laboratorio del giroscopio (solo para desarrollo; se borrará junto con public/gyrolab.js y su botón en Ajustes).
|
|
// Guarda las grabaciones de los sensores en DATA_DIR/gyrolab/, una por paso del guion.
|
|
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
|
|
const MAX_BYTES = 4 * 1024 * 1024
|
|
const SESSION = /^[a-z0-9]{4,24}$/, STEP = /^[a-z0-9-]{2,24}$/
|
|
|
|
export function setupGyroLab({ DATA_DIR, HttpError, json }) {
|
|
const dir = path.join(DATA_DIR, 'gyrolab')
|
|
async function readRaw(req) {
|
|
let size = 0
|
|
const chunks = []
|
|
for await (const c of req) {
|
|
size += c.length
|
|
if (size > MAX_BYTES) throw new HttpError(413, 'Grabación demasiado grande')
|
|
chunks.push(c)
|
|
}
|
|
try { return JSON.parse(Buffer.concat(chunks).toString()) } catch { throw new HttpError(400, 'JSON inválido') }
|
|
}
|
|
async function route(p, method, req, res, user) {
|
|
if (p !== '/api/gyrolab') return false
|
|
if (method === 'GET') {
|
|
const mine = fs.existsSync(dir) ? fs.readdirSync(dir).filter(f => f.startsWith(`u${user.id}-`)) : []
|
|
return json(res, 200, { files: mine.sort() })
|
|
}
|
|
if (method !== 'POST') throw new HttpError(405, 'Método no permitido')
|
|
const body = await readRaw(req)
|
|
if (!SESSION.test(body?.session || '') || !STEP.test(body?.step || '')) throw new HttpError(400, 'Sesión o paso no válidos')
|
|
if (!Array.isArray(body.ori) || !Array.isArray(body.mot)) throw new HttpError(400, 'Faltan lecturas')
|
|
fs.mkdirSync(dir, { recursive: true })
|
|
const name = `u${user.id}-${body.session}-${body.step}.json`
|
|
fs.writeFileSync(path.join(dir, name), JSON.stringify({ ...body, user: user.username, savedAt: Date.now() }))
|
|
return json(res, 200, { ok: true, file: name })
|
|
}
|
|
return { route }
|
|
}
|