Estante 3D: entradas, orden A–Z, sin conexión, pósit y giroscopio calibrado

- 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>
This commit is contained in:
2026-11-06 21:12:58 -06:00
co-authored by Claude Opus 5.5
parent e40127265f
commit d7400f27ad
8 changed files with 539 additions and 108 deletions
+4 -4
View File
@@ -5,15 +5,15 @@ export function setupShelf({ db, json, isBookId, downloadCover, MY_DOWNLOADS })
function works(userId) {
const out = new Map()
const add = (id, fields) => {
const w = out.get(id) || { id, title: '', cover: null, last: 0, chapterId: null, page: 0, lang: null }
const w = out.get(id) || { id, title: '', cover: null, last: 0, chapterId: null, chapterNum: null, page: 0, lang: null }
w.title ||= fields.title || ''
w.cover ||= fields.cover || null
w.last = Math.max(w.last, fields.at || 0)
if (fields.chapterId) Object.assign(w, { chapterId: fields.chapterId, page: fields.page || 0, lang: fields.lang || null })
if (fields.chapterId) Object.assign(w, { chapterId: fields.chapterId, chapterNum: fields.chapterNum ?? null, page: fields.page || 0, lang: fields.lang || null })
out.set(id, w)
}
for (const r of db.prepare('SELECT manga_id, manga_title, cover, chapter_id, page, lang, updated_at FROM progress WHERE user_id = ?').all(userId)) {
add(r.manga_id, { title: r.manga_title, cover: r.cover, chapterId: r.chapter_id, page: r.page, lang: r.lang, at: r.updated_at })
for (const r of db.prepare('SELECT manga_id, manga_title, cover, chapter_id, chapter_num, page, lang, updated_at FROM progress WHERE user_id = ?').all(userId)) {
add(r.manga_id, { title: r.manga_title, cover: r.cover, chapterId: r.chapter_id, chapterNum: r.chapter_num, page: r.page, lang: r.lang, at: r.updated_at })
}
for (const r of db.prepare('SELECT manga_id, title, cover, last_new_at, created_at FROM follows WHERE user_id = ? AND active = 1').all(userId)) {
add(r.manga_id, { title: r.title, cover: r.cover, at: r.last_new_at || r.created_at })