Add per-request AI logging, DB batch queue, WS entity updates, and UI polish
- 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>
This commit is contained in:
@@ -7,35 +7,53 @@
|
||||
* S — main UI state (tree data, selection, loading flags)
|
||||
* _plugins — plugin manifest populated from GET /api/config
|
||||
* _batchState — current batch-processing progress
|
||||
* _batchPollTimer — setInterval handle for batch polling
|
||||
* _batchWs — active WebSocket for batch push notifications (null when idle)
|
||||
* _bnd — live boundary-canvas state (written by canvas-boundary.js,
|
||||
* read by detail-render.js)
|
||||
* _photoQueue — photo queue session state (written by photo.js,
|
||||
* read by events.js)
|
||||
*/
|
||||
|
||||
/* exported S */
|
||||
|
||||
// ── Main UI state ───────────────────────────────────────────────────────────
|
||||
let S = {
|
||||
const S = {
|
||||
tree: null,
|
||||
expanded: new Set(),
|
||||
selected: null, // {type:'cabinet'|'shelf'|'book', id}
|
||||
_photoTarget: null, // {type, id}
|
||||
_loading: {}, // {`${pluginId}:${entityId}`: true}
|
||||
_cropMode: null, // {type, id} while crop UI is active
|
||||
selected: null, // {type:'cabinet'|'shelf'|'book', id}
|
||||
_photoTarget: null, // {type, id}
|
||||
_loading: {}, // {`${pluginId}:${entityId}`: true}
|
||||
_cropMode: null, // {type, id} while crop UI is active
|
||||
};
|
||||
|
||||
// ── Plugin registry ─────────────────────────────────────────────────────────
|
||||
let _plugins = []; // populated from GET /api/config
|
||||
// eslint-disable-next-line prefer-const
|
||||
let _plugins = []; // populated from GET /api/config
|
||||
|
||||
// ── Batch processing state ──────────────────────────────────────────────────
|
||||
let _batchState = {running: false, total: 0, done: 0, errors: 0, current: ''};
|
||||
let _batchPollTimer = null;
|
||||
const _batchState = { running: false, total: 0, done: 0, errors: 0, current: '' };
|
||||
// eslint-disable-next-line prefer-const
|
||||
let _batchWs = null;
|
||||
|
||||
// ── Boundary canvas live state ───────────────────────────────────────────────
|
||||
// Owned by canvas-boundary.js; declared here so detail-render.js can read it
|
||||
// without a circular load dependency.
|
||||
// eslint-disable-next-line prefer-const
|
||||
let _bnd = null; // {wrap,img,canvas,axis,boundaries[],pluginResults{},selectedPlugin,segments[],nodeId,nodeType}
|
||||
|
||||
// ── Photo queue session state ────────────────────────────────────────────────
|
||||
// Owned by photo.js; declared here so events.js can read/write it.
|
||||
// eslint-disable-next-line prefer-const
|
||||
let _photoQueue = null; // {books:[...], index:0, processing:false}
|
||||
|
||||
// ── AI blocks visibility ─────────────────────────────────────────────────────
|
||||
// Per-book override map. If bookId is absent the default rule applies:
|
||||
// show when not user_approved, hide when user_approved.
|
||||
const _aiBlocksVisible = {}; // {bookId: true|false}
|
||||
|
||||
// ── AI request log ───────────────────────────────────────────────────────────
|
||||
// Populated from /ws/ai-log on page load.
|
||||
// eslint-disable-next-line prefer-const
|
||||
let _aiLog = []; // AiLogEntry[] — ring buffer, oldest first
|
||||
// eslint-disable-next-line prefer-const
|
||||
let _aiLogWs = null; // active WebSocket for AI log push (never closed)
|
||||
|
||||
Reference in New Issue
Block a user