// ==================== 核心框架:插件加载 & 标星置顶 & 自动描述下载 & 全部玩法菜单 ==================== const STORAGE_KEY = 'toolbox_starred_plugins'; function getStarredPlugins() { try { const data = localStorage.getItem(STORAGE_KEY); return data ? JSON.parse(data) : []; } catch { return []; } } function saveStarredPlugins(list) { localStorage.setItem(STORAGE_KEY, JSON.stringify(list)); } function toggleStar(pluginId) { const starred = getStarredPlugins(); const index = starred.indexOf(pluginId); if (index === -1) starred.push(pluginId); else starred.splice(index, 1); saveStarredPlugins(starred); return starred; } const pluginRegistry = []; let currentPlugin = null; function registerPlugin(plugin) { pluginRegistry.push(plugin); } function showToast(msg, duration = 2500) { const container = document.getElementById('toastContainer'); const toast = document.createElement('div'); toast.className = 'toast'; toast.textContent = msg; container.appendChild(toast); setTimeout(() => { if (toast.parentNode) toast.parentNode.removeChild(toast); }, duration + 400); } function sortPluginsByStar(plugins) { const starred = getStarredPlugins(); const starredP = [], normalP = []; plugins.forEach(p => { if (starred.includes(p.id)) starredP.push(p); else normalP.push(p); }); return [...starredP, ...normalP]; } function buildUI() { const navContainer = document.getElementById('navTabs'); const panelsContainer = document.getElementById('panelsContainer'); navContainer.innerHTML = ''; panelsContainer.innerHTML = ''; const sortedPlugins = sortPluginsByStar(pluginRegistry); const starred = getStarredPlugins(); sortedPlugins.forEach((plugin) => { const isStarred = starred.includes(plugin.id); const btn = document.createElement('button'); btn.className = 'nav-tab' + (isStarred ? ' starred' : ''); btn.dataset.pluginId = plugin.id; btn.innerHTML = ` ${isStarred ? '⭐' : '☆'} ${plugin.icon} ${plugin.name} ${plugin.badge ? `${plugin.badge}` : ''} `; btn.addEventListener('click', (e) => { if (e.target.classList.contains('star-btn') || e.target.closest('.star-btn')) return; switchToPlugin(plugin.id); closeDropdown(); }); const starBtn = btn.querySelector('.star-btn'); starBtn.addEventListener('click', (e) => { e.stopPropagation(); toggleStar(plugin.id); buildUI(); if (currentPlugin && currentPlugin.id === plugin.id) switchToPlugin(plugin.id); showToast(getStarredPlugins().includes(plugin.id) ? '⭐ 已置顶' : '已取消置顶'); }); navContainer.appendChild(btn); const panel = document.createElement('div'); panel.className = 'panel'; panel.id = `panel-${plugin.id}`; panelsContainer.appendChild(panel); }); if (sortedPlugins.length > 0) switchToPlugin(sortedPlugins[0].id); buildDropdown(); } function switchToPlugin(pluginId) { if (currentPlugin && currentPlugin.destroy) currentPlugin.destroy(); document.querySelectorAll('.panel').forEach(p => p.classList.remove('active')); document.querySelectorAll('.nav-tab').forEach(t => t.classList.remove('active')); const plugin = pluginRegistry.find(p => p.id === pluginId); if (!plugin) return; const panel = document.getElementById(`panel-${pluginId}`); panel.classList.add('active'); const tabBtn = document.querySelector(`.nav-tab[data-plugin-id="${pluginId}"]`); if (tabBtn) tabBtn.classList.add('active'); panel.innerHTML = ''; plugin.render(panel); currentPlugin = plugin; observePanelForDownloadableImages(panel); } function loadPlugins() { const loadPromises = PLUGIN_LIST.map(item => { return new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = item.script; script.onload = resolve; script.onerror = () => reject(new Error(`加载失败: ${item.script}`)); document.head.appendChild(script); }); }); Promise.all(loadPromises) .then(() => buildUI()) .catch(err => { console.error(err); showToast('⚠️ 部分插件加载失败'); buildUI(); }); } window.addEventListener('DOMContentLoaded', loadPlugins); // ==================== 全部玩法下拉菜单 ==================== function buildDropdown() { const dropdown = document.getElementById('pluginDropdown'); if (!dropdown) return; dropdown.innerHTML = ''; const starred = getStarredPlugins(); pluginRegistry.forEach(plugin => { const isStarred = starred.includes(plugin.id); const item = document.createElement('div'); item.className = 'plugin-dropdown-item'; item.innerHTML = ` ${isStarred ? '⭐' : '☆'} ${plugin.icon} ${plugin.name} ${plugin.badge ? `${plugin.badge}` : ''} `; item.addEventListener('click', (e) => { if (e.target.classList.contains('star-btn')) return; switchToPlugin(plugin.id); closeDropdown(); }); const starBtn = item.querySelector('.star-btn'); starBtn.addEventListener('click', (e) => { e.stopPropagation(); toggleStar(plugin.id); buildUI(); if (currentPlugin && currentPlugin.id === plugin.id) switchToPlugin(plugin.id); showToast(getStarredPlugins().includes(plugin.id) ? '⭐ 已置顶' : '已取消置顶'); }); dropdown.appendChild(item); }); } function closeDropdown() { const dropdown = document.getElementById('pluginDropdown'); if (dropdown) dropdown.classList.remove('show'); } document.addEventListener('click', (e) => { const moreBtn = document.getElementById('navMoreBtn'); const dropdown = document.getElementById('pluginDropdown'); if (!moreBtn || !dropdown) return; if (moreBtn.contains(e.target)) { dropdown.classList.toggle('show'); } else if (!dropdown.contains(e.target)) { dropdown.classList.remove('show'); } }); // ==================== 统一描述下载 ==================== if (!CanvasRenderingContext2D.prototype.roundRect) { CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r) { if (typeof r === 'number') r = { tl: r, tr: r, br: r, bl: r }; this.beginPath(); this.moveTo(x + r.tl, y); this.lineTo(x + w - r.tr, y); this.quadraticCurveTo(x + w, y, x + w, y + r.tr); this.lineTo(x + w, y + h - r.br); this.quadraticCurveTo(x + w, y + h, x + w - r.br, y + h); this.lineTo(x + r.bl, y + h); this.quadraticCurveTo(x, y + h, x, y + h - r.bl); this.lineTo(x, y + r.tl); this.quadraticCurveTo(x, y, x + r.tl, y); this.closePath(); }; } function addDescriptionToImage(imageDataUrl, description) { return new Promise((resolve, reject) => { if (!description || description.trim() === '') { resolve(imageDataUrl); return; } const img = new Image(); img.onload = () => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0); const fontSize = Math.max(12, Math.floor(img.width / 40)); ctx.font = `bold ${fontSize}px "PingFang SC", "Microsoft YaHei", sans-serif`; const text = description.trim(); const textWidth = ctx.measureText(text).width; const paddingX = fontSize * 0.6; const paddingY = fontSize * 0.4; const bgX = paddingX; const bgY = canvas.height - fontSize - paddingY * 2; const bgWidth = textWidth + paddingX * 2; const bgHeight = fontSize + paddingY * 2; ctx.fillStyle = 'rgba(0,0,0,0.65)'; ctx.beginPath(); ctx.roundRect(bgX, bgY, bgWidth, bgHeight, fontSize * 0.3); ctx.fill(); ctx.fillStyle = '#ffffff'; ctx.textBaseline = 'bottom'; ctx.textAlign = 'left'; ctx.fillText(text, bgX + paddingX, canvas.height - paddingY); resolve(canvas.toDataURL('image/png')); }; img.onerror = reject; img.src = imageDataUrl; }); } function injectDownloadUI(container) { if (container.querySelector('.auto-download-ui')) return null; const img = container.querySelector('img'); if (!img) return null; const uiContainer = document.createElement('div'); uiContainer.className = 'auto-download-ui'; uiContainer.style.marginTop = '12px'; const descInput = document.createElement('input'); descInput.type = 'text'; descInput.placeholder = '添加描述(显示在图片左下角)'; descInput.style.cssText = 'width:100%; max-width:320px; padding:8px 12px; border-radius:20px; border:1px solid var(--border); background:var(--surface2); color:var(--text); font-size:0.85rem; outline:none;'; const downloadBtn = document.createElement('button'); downloadBtn.className = 'btn btn-accent'; downloadBtn.textContent = '⬇️ 下载PNG(含描述)'; downloadBtn.style.marginTop = '8px'; downloadBtn.addEventListener('click', async () => { const imageUrl = img.src; if (!imageUrl) { showToast('暂无图片可下载'); return; } const desc = descInput.value; const finalUrl = await addDescriptionToImage(imageUrl, desc); const a = document.createElement('a'); a.href = finalUrl; a.download = 'image.png'; a.click(); }); uiContainer.appendChild(descInput); uiContainer.appendChild(downloadBtn); container.appendChild(uiContainer); return { descInput, downloadBtn }; } function observePanelForDownloadableImages(panel) { if (panel._downloadObserver) panel._downloadObserver.disconnect(); const observer = new MutationObserver(() => { const areas = panel.querySelectorAll('.result-area.show, .result-area[style*="display: block"]'); areas.forEach(area => injectDownloadUI(area)); }); observer.observe(panel, { childList: true, subtree: true, attributes: true, attributeFilter: ['class', 'style'] }); panel._downloadObserver = observer; const existingAreas = panel.querySelectorAll('.result-area.show, .result-area[style*="display: block"]'); existingAreas.forEach(area => injectDownloadUI(area)); }