- log_thread.py: thread-safe ContextVar bridge so executor threads can log
individual LLM calls and archive searches back to the event loop
- ai_log.py: init_thread_logging(), notify_entity_update(); WS now pushes
entity_update messages when book data changes after any plugin or batch run
- batch.py: replace batch_pending.json with batch_queue SQLite table;
run_batch_consumer() reads queue dynamically so new books can be added
while batch is running; add_to_queue() deduplicates
- migrate.py: fix _migrate_v1 (clear-on-startup bug); add _migrate_v2 for
batch_queue table
- _client.py / archive.py / identification.py: wrap each LLM API call and
archive search with log_thread start/finish entries
- api.py: POST /api/batch returns {already_running, added}; notify_entity_update
after identify pipeline
- models.default.yaml: strengthen ai_identify confidence-scoring instructions;
warn against placeholder data
- detail-render.js: book log entries show clickable ID + spine thumbnail;
book spine/title images open full-screen popup
- events.js: batch-start handles already_running+added; open-img-popup action
- init.js: entity_update WS handler; image popup close listeners
- overlays.css / index.html: full-screen image popup overlay
- eslint.config.js: add new globals; fix no-redeclare/no-unused-vars for
multi-file global architecture; all lint errors resolved
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
39 lines
3.2 KiB
CSS
39 lines
3.2 KiB
CSS
/*
|
|
* overlays.css
|
|
* Fixed-position overlays that appear above all other content:
|
|
* - Toast notification (bottom-center slide-in)
|
|
* - Loading spinner and empty-state placeholder
|
|
* - Photo Queue overlay: full-screen mobile flow for photographing
|
|
* unidentified books in sequence (spine preview + camera button)
|
|
*/
|
|
|
|
/* ── Toast / loading ── */
|
|
.toast{position:fixed;bottom:16px;left:50%;transform:translateX(-50%) translateY(120px);background:#1e293b;color:white;padding:7px 15px;border-radius:6px;font-size:.82rem;transition:transform .25s;z-index:9999;pointer-events:none;white-space:nowrap}
|
|
.toast.on{transform:translateX(-50%) translateY(0)}
|
|
.loading{display:flex;align-items:center;justify-content:center;padding:30px;gap:8px;color:#64748b;font-size:.88rem}
|
|
.spinner{width:20px;height:20px;border:3px solid #e2e8f0;border-top-color:#2563eb;border-radius:50%;animation:spin .8s linear infinite;flex-shrink:0}
|
|
@keyframes spin{to{transform:rotate(360deg)}}
|
|
.empty{text-align:center;padding:36px 12px;color:#94a3b8}
|
|
.empty .ei{font-size:2.4rem;margin-bottom:7px}
|
|
|
|
/* ── Photo Queue Overlay ── */
|
|
#photo-queue-overlay{position:fixed;inset:0;background:#0f172a;z-index:200;flex-direction:column;color:white}
|
|
.pq-hdr{display:flex;align-items:center;gap:8px;padding:12px 14px;background:#1e3a5f;flex-shrink:0;box-shadow:0 2px 6px rgba(0,0,0,.3)}
|
|
.pq-hdr-title{flex:1;font-size:.9rem;font-weight:600;text-align:center}
|
|
.pq-spine-wrap{flex:1;display:flex;align-items:center;justify-content:center;padding:16px;min-height:0;flex-direction:column;gap:12px;overflow:hidden}
|
|
.pq-spine-img{max-width:100%;max-height:52vh;object-fit:contain;border-radius:8px;box-shadow:0 4px 20px rgba(0,0,0,.6)}
|
|
.pq-book-name{font-size:.85rem;color:#94a3b8;text-align:center;max-width:90%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
.pq-actions{display:flex;align-items:center;justify-content:center;gap:24px;padding:20px 16px;flex-shrink:0;background:#1e293b;border-top:1px solid rgba(255,255,255,.1)}
|
|
.pq-camera-btn{background:#2563eb;color:white;border:none;border-radius:50%;width:76px;height:76px;font-size:2rem;display:flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0;box-shadow:0 4px 16px rgba(37,99,235,.4)}
|
|
.pq-camera-btn:active{background:#1d4ed8;transform:scale(.94)}
|
|
.pq-skip-btn{background:rgba(255,255,255,.1);color:#cbd5e1;border:none;border-radius:8px;padding:12px 18px;font-size:.85rem;cursor:pointer;min-width:70px}
|
|
.pq-skip-btn:active{background:rgba(255,255,255,.2)}
|
|
.pq-processing{position:absolute;inset:0;background:rgba(15,23,42,.88);display:flex;align-items:center;justify-content:center;flex-direction:column;gap:10px;font-size:.9rem}
|
|
|
|
/* ── Image popup ── */
|
|
.img-popup{display:none;position:fixed;inset:0;background:rgba(0,0,0,.75);z-index:500;align-items:center;justify-content:center}
|
|
.img-popup.open{display:flex}
|
|
.img-popup-inner{position:relative;max-width:90vw;max-height:90vh}
|
|
.img-popup-inner img{max-width:90vw;max-height:90vh;object-fit:contain;border-radius:4px;display:block}
|
|
.img-popup-close{position:absolute;top:-14px;right:-14px;background:#fff;border:none;border-radius:50%;width:28px;height:28px;cursor:pointer;font-size:18px;line-height:28px;text-align:center;padding:0;box-shadow:0 2px 6px rgba(0,0,0,.3)}
|