- 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>
51 lines
2.7 KiB
CSS
51 lines
2.7 KiB
CSS
/*
|
|
* layout.css
|
|
* Top-level layout: global header spanning full width, two-column desktop
|
|
* layout (300px sidebar + flex main panel), mobile single-column default,
|
|
* and the contenteditable header span used for inline entity renaming.
|
|
*
|
|
* Breakpoint: ≥768px = desktop two-column; <768px = mobile accordion.
|
|
*/
|
|
|
|
/* ── Page wrapper (header + content area) ── */
|
|
.page-wrap{display:flex;flex-direction:column;min-height:100vh}
|
|
|
|
/* ── Global header ── */
|
|
.hdr{background:#1e3a5f;color:white;padding:10px 14px;display:flex;align-items:center;gap:8px;position:sticky;top:0;z-index:100;box-shadow:0 2px 6px rgba(0,0,0,.3);flex-shrink:0}
|
|
.hdr h1{font-size:.96rem;font-weight:600}
|
|
.hbtn{background:none;border:none;color:white;min-width:34px;min-height:34px;border-radius:50%;cursor:pointer;font-size:1rem;display:flex;align-items:center;justify-content:center;flex-shrink:0}
|
|
.hbtn:active{background:rgba(255,255,255,.2)}
|
|
|
|
/* ── AI active indicator (in global header) ── */
|
|
.ai-indicator{display:inline-flex;align-items:center;gap:5px;font-size:.75rem;color:rgba(255,255,255,.9);padding:2px 8px;border-radius:10px;background:rgba(255,255,255,.12)}
|
|
.ai-dot{width:7px;height:7px;border-radius:50%;background:#f59e0b;animation:pulse 1.2s ease-in-out infinite}
|
|
@keyframes pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(.8)}}
|
|
|
|
/* ── Mobile layout (default) ── */
|
|
.layout{display:flex;flex-direction:column;flex:1}
|
|
.sidebar{flex:1}
|
|
.main-panel{display:none}
|
|
|
|
/* ── Desktop layout ── */
|
|
@media(min-width:768px){
|
|
body{overflow:hidden}
|
|
.page-wrap{height:100vh;overflow:hidden}
|
|
.layout{flex-direction:row;flex:1;overflow:hidden}
|
|
.sidebar{width:300px;display:flex;flex-direction:column;border-right:1px solid #cbd5e1;overflow:hidden;flex-shrink:0}
|
|
.sidebar-body{flex:1;overflow-y:auto;padding:8px 10px 16px}
|
|
.main-panel{flex:1;display:flex;flex-direction:column;overflow:hidden;background:#e8eef5}
|
|
.main-hdr{background:#1e3a5f;color:white;padding:9px 14px;display:flex;align-items:center;gap:8px;flex-shrink:0}
|
|
.main-hdr h2{flex:1;font-size:.9rem;font-weight:500;opacity:.9;min-width:0}
|
|
.main-body{flex:1;overflow:auto;padding:14px}
|
|
}
|
|
|
|
/* ── Root detail panel ── */
|
|
.det-root{max-width:640px}
|
|
.ai-log-entry{border-bottom:1px solid #f1f5f9;padding:0 2px}
|
|
.ai-log-entry:last-child{border-bottom:none}
|
|
.ai-log-entry summary::-webkit-details-marker{display:none}
|
|
|
|
/* ── Detail header editable name ── */
|
|
.hdr-edit{display:block;outline:none;cursor:text;border-radius:3px;padding:1px 4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
.hdr-edit:focus{background:rgba(255,255,255,.15);white-space:normal;overflow:visible}
|