// 多个 Eagle 插件窗口共用 localStorage,用它协调唯一的活动监听实例。 (function () { 'use strict'; const fs = require('fs'); const path = require('path'); const KEY = 'spine-thumbnail-active-instance'; const STALE_MS = 3500; function createFileStorage(file) { return { file, getItem(key) { try { const data = JSON.parse(fs.readFileSync(file, 'utf8')); return typeof data[key] === 'string' ? data[key] : null; } catch (error) { return null; } }, setItem(key, value) { let data = {}; try { const existing = JSON.parse(fs.readFileSync(file, 'utf8')); if (existing && typeof existing === 'object') data = existing; } catch (error) { /* first write or damaged file */ } data[key] = String(value); fs.mkdirSync(path.dirname(file), { recursive: true }); const temp = file + '.' + process.pid + '.' + Date.now() + '.' + Math.random().toString(16).slice(2) + '.tmp'; fs.writeFileSync(temp, JSON.stringify(data), 'utf8'); fs.renameSync(temp, file); }, }; } function readState(storage) { try { const state = JSON.parse(storage.getItem(KEY)); if (!state || typeof state.id !== 'string' || !Number.isFinite(state.updatedAt)) return null; return state; } catch (error) { return null; } } function writeState(storage, id, visible, now) { const state = { id, visible, updatedAt: now }; try { storage.setItem(KEY, JSON.stringify(state)); } catch (error) { /* 无本地存储时仍可运行 */ } return state; } function createCoordinator(options) { const storage = options.storage; const id = options.id; const visible = options.isVisible || (() => true); const hide = options.hide || (() => {}); const now = options.now || Date.now; const staleMs = options.staleMs || STALE_MS; const intervalMs = options.intervalMs || 1000; let active = false; let started = false; let hiddenForDuplicate = false; let timer = null; function claim() { writeState(storage, id, visible(), now()); hiddenForDuplicate = false; if (!active) { active = true; if (options.onActiveChanged) options.onActiveChanged(true); } } function evaluate() { const state = readState(storage); const currentVisible = visible(); const canClaim = !state || state.id === id || now() - state.updatedAt > staleMs; const nextActive = canClaim; if (nextActive) { claim(); } else if (currentVisible && !hiddenForDuplicate) { hiddenForDuplicate = true; try { hide(); } catch (error) { /* 隐藏失败不影响主窗口 */ } } if (nextActive !== active) { active = nextActive; if (options.onActiveChanged) options.onActiveChanged(active); } return active; } function handleVisibility() { if (visible()) claim(); else evaluate(); } function start() { if (started) return active; started = true; const state = readState(storage); const canStart = visible() || !state || state.id === id || now() - state.updatedAt > staleMs; if (canStart) claim(); else evaluate(); timer = setInterval(evaluate, intervalMs); return active; } function stop() { if (timer) clearInterval(timer); timer = null; started = false; } return { start, stop, evaluate, handleVisibility, isActive: () => active }; } const api = { createCoordinator, createFileStorage, KEY, STALE_MS }; if (typeof window !== 'undefined') window.SpineThumbInstanceCoordinator = api; else if (typeof module !== 'undefined' && module.exports) module.exports = api; })();