diff --git a/public/index.css b/public/index.css index 2a4d7da..282dc99 100644 --- a/public/index.css +++ b/public/index.css @@ -12,28 +12,39 @@ --card-text-color: #f8fafc; } -/* 顶部右侧滑块(打开方式) */ +/* 顶部右侧滑块(打开方式 + 本地背景) */ .top-right-controls { position: fixed; top: 16px; right: 16px; z-index: 1000; + display: flex; + gap: 10px; + align-items: center; } .ios-switch { position: relative; - display: inline-block; + display: inline-flex; + flex-direction: column; + align-items: center; + gap: 2px; width: 52px; - height: 28px; + height: 46px; cursor: pointer; } .ios-switch input { opacity: 0; width: 0; height: 0; + position: absolute; + top: 16px; } .ios-switch .slider { position: absolute; - inset: 0; + top: 0; + left: 0; + right: 0; + height: 28px; background: linear-gradient(135deg, #334155, #0f172a); border: 1px solid rgba(255,255,255,0.15); border-radius: 999px; @@ -283,26 +294,45 @@ body { -/* 换背景按钮 */ -.change-background-btn { +/* 换背景按钮容器(按钮 + 本地模式开关) */ +.bg-btn-wrapper { position: fixed; bottom: 2rem; right: 2rem; - background: rgba(255, 255, 255, 0.2); - border: 1px solid rgba(255, 255, 255, 0.3); - border-radius: 50px; - padding: 0.75rem 1.5rem; - color: var(--text-light); - font-size: 0.9rem; - cursor: pointer; - transition: all 0.3s ease; - backdrop-filter: blur(10px); + display: flex; + align-items: center; + gap: 12px; z-index: 1000; } +/* 换背景按钮(仅图标) */ +.change-background-btn { + background: transparent; + border: none; + border-radius: 50%; + width: auto; + height: auto; + padding: 2px; + color: var(--text-light); + font-size: 1.6rem; + line-height: 1; + cursor: pointer; + transition: all 0.2s ease; + backdrop-filter: none; + display: inline-flex; + align-items: center; + justify-content: center; +} + .change-background-btn:hover { - background: rgba(255, 255, 255, 0.3); - transform: translateY(-2px); + background: rgba(255, 255, 255, 0.2); + outline: 1px solid rgba(255, 255, 255, 0.3); + backdrop-filter: blur(10px); + padding: 2px; +} + +.change-background-btn::after { + content: '🎨'; } /* 响应式设计 */ diff --git a/public/index.js b/public/index.js index a99a9e4..160c3c0 100644 --- a/public/index.js +++ b/public/index.js @@ -4,6 +4,15 @@ class ToolboxHome { constructor() { this.currentTheme = 0; this.openInModal = true; + this.localBgMode = false; + this.localBgFiles = []; + this.localBgIndex = 0; + this.localRotationInterval = null; + // 从 localStorage 恢复状态 + try { + this.localBgMode = localStorage.getItem('tb_home_local_bg') === '1'; + this.openInModal = localStorage.getItem('tb_home_open_mode') !== '0'; + } catch {} this.themes = [ { name: '夜空星轨', @@ -263,11 +272,6 @@ class ToolboxHome { tabPages.appendChild(lowToolsPage); tabPages.appendChild(bookmarksPage); - // 创建换背景按钮 - const changeBgBtn = document.createElement('button'); - changeBgBtn.className = 'change-background-btn'; - changeBgBtn.innerHTML = '🎨 换背景'; - // 组装页面 mainContainer.appendChild(header); mainContainer.appendChild(tabBar); @@ -277,7 +281,6 @@ class ToolboxHome { document.body.appendChild(backgroundContainer); document.body.appendChild(effectsContainer); document.body.appendChild(mainContainer); - document.body.appendChild(changeBgBtn); // 顶部右侧滑块(弹窗/本地切换) const controls = document.createElement('div'); controls.className = 'top-right-controls'; @@ -288,7 +291,24 @@ class ToolboxHome { `; document.body.appendChild(controls); - this.ensureBgLoaded(0, true); + // 换背景按钮容器(图标按钮 + 本地模式开关,水平排列,放底部) + const bgBtnWrapper = document.createElement('div'); + bgBtnWrapper.className = 'bg-btn-wrapper'; + bgBtnWrapper.innerHTML = ` + + + `; + document.body.appendChild(bgBtnWrapper); + // 根据 localStorage 状态初始化背景模式 + if (this.localBgMode) { + this.startLocalBgRotation(); + } else { + this.ensureBgLoaded(0, true); + this.startThemeRotation(); + } const iconPicker = document.createElement('div'); iconPicker.className = 'icon-picker'; @@ -624,7 +644,7 @@ class ToolboxHome { setupEventListeners() { // 换背景按钮事件 document.querySelector('.change-background-btn').addEventListener('click', () => { - this.ensureBgLoaded(this.currentTheme, false); + this.nextTheme(); }); // 工具卡片点击效果 + 打开方式(委托方式,支持动态加载) @@ -648,10 +668,28 @@ class ToolboxHome { }); // 滑块切换事件 - const switchEl = document.getElementById('open-mode-switch'); - if (switchEl) { - switchEl.addEventListener('change', () => { - this.openInModal = !!switchEl.checked; + const localBgSwitch = document.getElementById('local-bg-switch'); + const openModeSwitch = document.getElementById('open-mode-switch'); + if (localBgSwitch) { + localBgSwitch.checked = this.localBgMode; + localBgSwitch.addEventListener('change', () => { + this.localBgMode = !!localBgSwitch.checked; + try { localStorage.setItem('tb_home_local_bg', this.localBgMode ? '1' : '0'); } catch {} + if (this.localBgMode) { + this.stopThemeRotation(); + this.startLocalBgRotation(); + } else { + this.stopLocalBgRotation(); + this.ensureBgLoaded(this.currentTheme, true); + this.startThemeRotation(); + } + }); + } + if (openModeSwitch) { + openModeSwitch.checked = this.openInModal; + openModeSwitch.addEventListener('change', () => { + this.openInModal = !!openModeSwitch.checked; + try { localStorage.setItem('tb_home_open_mode', this.openInModal ? '1' : '0'); } catch {} }); } @@ -1264,6 +1302,10 @@ class ToolboxHome { } nextTheme() { + if (this.localBgMode) { + this.nextLocalBg(); + return; + } this.currentTheme = (this.currentTheme + 1) % this.themes.length; this.applyTheme(this.currentTheme); @@ -1417,12 +1459,81 @@ class ToolboxHome { } startBackgroundRotation() { - // 每30秒自动切换背景 + this.startThemeRotation(); + } + + startThemeRotation() { + if (this.localBgMode) return; + this.stopThemeRotation(); this.rotationInterval = setInterval(() => { this.nextTheme(); }, 30000); } + stopThemeRotation() { + if (this.rotationInterval) { + clearInterval(this.rotationInterval); + this.rotationInterval = null; + } + } + + startLocalBgRotation() { + this.loadLocalBgFiles().then(() => { + this.stopLocalBgRotation(); + this.localRotationInterval = setInterval(() => { + this.nextLocalBg(); + }, 30000); + }); + } + + stopLocalBgRotation() { + if (this.localRotationInterval) { + clearInterval(this.localRotationInterval); + this.localRotationInterval = null; + } + } + + async loadLocalBgFiles() { + if (this.localBgFiles.length > 0) return this.localBgFiles; + try { + const r = await fetch('/api/home/bg/default-list?t=' + Date.now()); + const j = await r.json(); + if (j.ok && j.files.length) { + this.localBgFiles = j.files; + this.localBgIndex = 0; + // 加载第一张 + const imgs = document.querySelectorAll('.background-image'); + if (imgs.length) { + imgs.forEach(img => { img.src = `/home-bg/default-c21f969b/${this.localBgFiles[0]}?v=${Date.now()}`; }); + } + } + } catch {} + return this.localBgFiles; + } + + async nextLocalBg() { + if (!this.localBgFiles.length) { + await this.loadLocalBgFiles(); + if (!this.localBgFiles.length) return; + } + // 随机选取,避免重复当前 + let newIndex; + if (this.localBgFiles.length > 1) { + do { + newIndex = Math.floor(Math.random() * this.localBgFiles.length); + } while (newIndex === this.localBgIndex); + } else { + newIndex = 0; + } + this.localBgIndex = newIndex; + const file = this.localBgFiles[this.localBgIndex]; + const imgs = document.querySelectorAll('.background-image'); + if (imgs.length) { + imgs.forEach(img => { img.src = `/home-bg/default-c21f969b/${file}?v=${Date.now()}`; }); + } + this.showNotification(`本地背景: ${this.localBgIndex + 1}/${this.localBgFiles.length}`); + } + showNotification(message) { // 创建通知元素 const notification = document.createElement('div'); diff --git a/public/tools/zen_box/folk_mingyao_34_playlist.json b/public/tools/zen_box/folk_mingyao_34_playlist.json new file mode 100644 index 0000000..7646b7f --- /dev/null +++ b/public/tools/zen_box/folk_mingyao_34_playlist.json @@ -0,0 +1,194 @@ +[ + { + "title": "我记得", + "artist": "赵雷", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/01.mp3", + "cover": "" + }, + { + "title": "三里屯的夜", + "artist": "赵雷", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/02.mp3", + "cover": "" + }, + { + "title": "凌晨计程车", + "artist": "赵雷", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/03.mp3", + "cover": "" + }, + { + "title": "点歌的人", + "artist": "海来阿木", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/04.mp3", + "cover": "" + }, + { + "title": "西楼儿女", + "artist": "海来阿木", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/05.mp3", + "cover": "" + }, + { + "title": "诺言", + "artist": "海来阿木", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/06.mp3", + "cover": "" + }, + { + "title": "鲜花", + "artist": "回春丹", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/07.mp3", + "cover": "" + }, + { + "title": "初恋", + "artist": "回春丹", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/08.mp3", + "cover": "" + }, + { + "title": "泸沽湖", + "artist": "麻园诗人", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/09.mp3", + "cover": "" + }, + { + "title": "现在现在", + "artist": "麻园诗人", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/10.mp3", + "cover": "" + }, + { + "title": "少女", + "artist": "赵雷", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/12.mp3", + "cover": "" + }, + { + "title": "漠河舞厅", + "artist": "柳爽", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/13.mp3", + "cover": "" + }, + { + "title": "三生三幸", + "artist": "海来阿木", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/14.mp3", + "cover": "" + }, + { + "title": "美好事物", + "artist": "房东的猫", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/15.mp3", + "cover": "" + }, + { + "title": "总有一天你会出现在我身边", + "artist": "棱镜", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/16.mp3", + "cover": "" + }, + { + "title": "踏浪", + "artist": "棱镜", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/17.mp3", + "cover": "" + }, + { + "title": "带我去找夜生活", + "artist": "告五人", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/18.mp3", + "cover": "" + }, + { + "title": "我欢喜喜欢你", + "artist": "焦迈奇", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/19.mp3", + "cover": "" + }, + { + "title": "无名的人", + "artist": "毛不易", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/20.mp3", + "cover": "" + }, + { + "title": "空空", + "artist": "陈粒", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/21.mp3", + "cover": "" + }, + { + "title": "大梦", + "artist": "陈粒", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/23.mp3", + "cover": "" + }, + { + "title": "我曾", + "artist": "隔壁老樊", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/24.mp3", + "cover": "" + }, + { + "title": "多年以后", + "artist": "大欢", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/25.mp3", + "cover": "" + }, + { + "title": "从前说", + "artist": "小阿七", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/26.mp3", + "cover": "" + }, + { + "title": "那年年少", + "artist": "小阿七", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/27.mp3", + "cover": "" + }, + { + "title": "与我无关", + "artist": "阿冗", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/28.mp3", + "cover": "" + }, + { + "title": "把回忆拼好给你", + "artist": "王贰浪", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/29.mp3", + "cover": "" + }, + { + "title": "巡光", + "artist": "就是南方凯", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/30.mp3", + "cover": "" + }, + { + "title": "沦陷", + "artist": "王靖雯", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/31.mp3", + "cover": "" + }, + { + "title": "善变", + "artist": "王靖雯", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/32.mp3", + "cover": "" + }, + { + "title": "这一生关于你的风景", + "artist": "大欢", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/33.mp3", + "cover": "" + }, + { + "title": "别知己", + "artist": "海来阿木", + "src": "https://android-o1-images.oss-accelerate.aliyuncs.com/550e8400-e29b-41d4-a716-446655442082/external_storage/folk_music_2020s/34.mp3", + "cover": "" + } +] \ No newline at end of file diff --git a/src/server/data_gateway/skills/email_sender/email.db-shm b/src/server/data_gateway/skills/email_sender/email.db-shm index 2a9389f..97010bf 100644 Binary files a/src/server/data_gateway/skills/email_sender/email.db-shm and b/src/server/data_gateway/skills/email_sender/email.db-shm differ diff --git a/src/server/data_gateway/skills/email_sender/email.db-wal b/src/server/data_gateway/skills/email_sender/email.db-wal index 53363d6..b891fc5 100644 Binary files a/src/server/data_gateway/skills/email_sender/email.db-wal and b/src/server/data_gateway/skills/email_sender/email.db-wal differ diff --git a/src/server/index.js b/src/server/index.js index ddbeca9..086e394 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -6200,6 +6200,30 @@ app.get('/api/home/bg', async (req, res) => { } }) +app.get('/api/home/bg/default-list', (req, res) => { + try { + const baseDir = path.join(process.cwd(), 'public', 'home-bg', 'default-c21f969b') + if (!fs.existsSync(baseDir)) return res.json({ ok: true, files: [] }) + const files = fs.readdirSync(baseDir).filter(f => /\.(jpg|jpeg|png|webp)$/i.test(f)) + res.json({ ok: true, files }) + } catch (e) { + res.json({ ok: true, files: [] }) + } +}) + +app.get('/api/home/bg/default-random', (req, res) => { + try { + const baseDir = path.join(process.cwd(), 'public', 'home-bg', 'default-c21f969b') + if (!fs.existsSync(baseDir)) return res.json({ ok: false, error: 'dir not found' }) + const files = fs.readdirSync(baseDir).filter(f => /\.(jpg|jpeg|png|webp)$/i.test(f)) + if (!files.length) return res.json({ ok: false, error: 'no files' }) + const pick = files[Math.floor(Math.random() * files.length)] + res.json({ ok: true, url: `/home-bg/default-c21f969b/${pick}?v=${Date.now()}` }) + } catch (e) { + res.status(500).json({ ok: false, error: String(e.message || e) }) + } +}) + app.post('/api/home/bg/populate', async (req, res) => { try { const themes = [