/* * state.js * All mutable application state — loaded first so every other module * can read and write these globals without forward-reference issues. * * Provides: * S — main UI state (tree data, selection, loading flags) * _plugins — plugin manifest populated from GET /api/config * _batchState — current batch-processing progress * _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 ─────────────────────────────────────────────────────────── 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 }; // ── Plugin registry ───────────────────────────────────────────────────────── // eslint-disable-next-line prefer-const let _plugins = []; // populated from GET /api/config // ── Batch processing state ────────────────────────────────────────────────── 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)