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 = [