'use strict'; // Eagle's item API has no arbitrary custom fields. Keep plugin metadata in // userData, keyed by library + item ID, without editing Eagle metadata.json. const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); function identity(sourcePath, itemId) { const directory = path.dirname(path.resolve(sourcePath)); const match = path.basename(directory).match(/^(.+)\.info$/i); return match ? path.dirname(directory) + ':' + (itemId || match[1]) : path.resolve(sourcePath); } function createStore(userDataPath, sourcePath, itemId) { const key = crypto.createHash('sha256').update(identity(sourcePath, itemId)).digest('hex'); const directory = path.join(userDataPath, 'SpineViewer', 'preview-settings'); const file = path.join(directory, key + '.json'); return { file, read() { try { const data = JSON.parse(fs.readFileSync(file, 'utf8')); return typeof data.premultipliedAlpha === 'boolean' ? data.premultipliedAlpha : undefined; } catch (e) { if (e.code === 'ENOENT') return undefined; throw e; } }, // This method is called only by the user's checkbox change event. write(premultipliedAlpha) { if (typeof premultipliedAlpha !== 'boolean') throw new TypeError('预乘设置必须为布尔值'); fs.mkdirSync(directory, { recursive: true }); const temp = file + '.' + crypto.randomBytes(8).toString('hex') + '.tmp'; try { fs.writeFileSync(temp, JSON.stringify({ premultipliedAlpha }, null, 2) + '\n', { flag: 'wx' }); fs.renameSync(temp, file); } finally { try { fs.unlinkSync(temp); } catch (e) { if (e.code !== 'ENOENT') throw e; } } } }; } async function resolveDataPath(app) { try { const value = app && (app.userDataPath || (typeof app.getPath === 'function' && await app.getPath('userData'))); if (typeof value === 'string' && value) return value; } catch (e) { /* Some Eagle format-view windows reject app.getPath IPC. */ } const home = require('os').homedir(); if (process.platform === 'darwin') return path.join(home, 'Library', 'Application Support', 'Eagle'); if (process.platform === 'win32') return path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'Eagle'); return path.join(process.env.XDG_CONFIG_HOME || path.join(home, '.config'), 'Eagle'); } // 尾缀表是全局偏好(不是每个素材一份):同一台机器上所有素材、所有库共用。 // 查看器在加载资源前读取它,用户的增删都会立即落盘。 function createSuffixStore(userDataPath) { const directory = path.join(userDataPath, 'SpineViewer'); const file = path.join(directory, 'suffix-settings.json'); return { file, read() { try { const data = JSON.parse(fs.readFileSync(file, 'utf8')); return Array.isArray(data.suffixes) ? data.suffixes : []; } catch (e) { if (e.code === 'ENOENT') return []; throw e; } }, // 仅在用户通过下拉列表增删尾缀时调用。 write(suffixes) { if (!Array.isArray(suffixes)) throw new TypeError('尾缀设置必须为数组'); fs.mkdirSync(directory, { recursive: true }); const temp = file + '.' + crypto.randomBytes(8).toString('hex') + '.tmp'; try { fs.writeFileSync(temp, JSON.stringify({ suffixes }, null, 2) + '\n', { flag: 'wx' }); fs.renameSync(temp, file); } finally { try { fs.unlinkSync(temp); } catch (e) { if (e.code !== 'ENOENT') throw e; } } } }; } module.exports = { createStore, createSuffixStore, resolveDataPath };