diff --git a/CHANGELOG.md b/CHANGELOG.md index a967f32..56eb3b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Card-Tab 更新日志 +## [2026.06.19] - 个人定制版 + +### 新增功能 +- 增加登录后可见的私密备注,未登录公开响应会移除备注字段 +- 备注弹窗支持鼠标选中复制和一键复制 +- 分类导航改为 All / 单分类视图切换,并同步地址栏 hash +- 设置模式下增强拖拽排序,支持拖到分类空白区域和空分类 +- 添加重复 URL 时给出提醒,并允许继续添加 +- 增加 Archive Yellow 主题 + +### 移除功能 +- 移除原版天气组件、天气弹窗、城市搜索、IP 定位和天气代理接口 +- 不再需要 `WEATHER_API_KEY` 和 `WEATHER_API_HOST` 环境变量 + ## [2026.01.08] - 天气组件集成 移除"我的导航"标题,集成和风天气组件 @@ -9,10 +23,8 @@ - 支持IP自动定位和手动切换城市,提供智能缓存机制 - 城市搜索功能:快速搜索并查看不同城市天气 -### 配置说明 -- [申请和风天气 API Key](https://id.qweather.com/#/register)(合理设置API限制) -- 配置API Key到环境变量 `WEATHER_API_KEY` -- [控制台设置]( https://console.qweather.com/setting) 中复制 “API Host”, 配置到环境变量 `WEATHER_API_HOST` +### 定制版说明 +- 该天气功能已在 2026.06.19 个人定制版中移除 ## [2026.01.03] - 浏览器扩展重构升级 diff --git a/README.md b/README.md index 1614d53..b28ac4d 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,28 @@ -# Card-Tab 书签卡片式管理,进入管理模式可以自由移动书签位置,添加和删除书签,支持自定义网站分类,支持切换暗色主题 +# Card-Tab 书签卡片式管理,进入设置模式可以移动书签位置,添加和删除书签,支持自定义网站分类、私密备注和多主题切换 📋 **查看完整更新历史**: [CHANGELOG.md](./CHANGELOG.md) -### [当前版本] - 2026.01.08 +### [当前版本] - 2026.06.19 定制版 ## ✨ 核心功能 ### 🎨 界面设计 - **现代化视觉**:采用 Segoe UI 字体系统,米白色/深色双主题 - **卡片式布局**:美观的书签卡片,支持左侧装饰条和悬停效果 - **响应式设计**:完美适配桌面端和移动端设备 -- **暗色主题**:一键切换,护眼舒适的夜间模式 +- **多主题**:支持温暖浅色、Archive Yellow 和暗色主题 ### 🔍 搜索功能 - **多引擎搜索**:支持百度、必应、谷歌、DuckDuckGo - **书签搜索**:快速搜索已保存的书签名称和网址 -- **分类导航**:快捷按钮一键跳转到指定分类 -- **智能高亮**:自动高亮当前可见分类 +- **分类导航**:支持 All 和单分类视图切换,地址栏 hash 可恢复当前分类 ### 📚 书签管理 - **分类管理**:自定义分类,支持重命名和排序 -- **卡片编辑**:修改名称、网址、描述和自定义图标 +- **卡片编辑**:修改名称、网址、描述、私密备注和自定义图标 - **私密书签**:登录后可见的私密书签功能 -- **拖拽排序**:直观的拖拽操作调整书签顺序 +- **私密备注**:登录后通过备注图标查看,可选中复制或一键复制 +- **拖拽排序**:设置模式下支持分类内排序和跨分类移动 +- **重复提醒**:添加重复 URL 时提示已存在分类,并允许手动继续添加 ### 🔐 安全特性 - **JWT验证**:基于Token的身份验证机制 @@ -30,11 +31,10 @@ - **权限控制**:公开/私密书签分级管理 ### 📱 用户体验 -- **自定义图标**:支持图片URL链接,个性化书签外观 +- **自定义图标**:支持图片URL链接,留空时自动获取网站图标 - **悬停提示**:鼠标悬停显示书签详细描述 - **返回顶部**:便捷的页面导航功能 - **加载动画**:流畅的交互反馈和视觉效果 -- **实时天气**:显示当前城市温度和天气状况,点击查看3天天气预报,支持城市切换和智能定位 ### 注意:如果你已经部署过第一版(20240902)导航,更新workes代码后将无法看到之前保存的书签,需重新添加书签,望知悉! diff --git a/docs/superpowers/plans/2026-06-19-card-tab-custom.md b/docs/superpowers/plans/2026-06-19-card-tab-custom.md new file mode 100644 index 0000000..8fb516c --- /dev/null +++ b/docs/superpowers/plans/2026-06-19-card-tab-custom.md @@ -0,0 +1,73 @@ +# Card-Tab Customization Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Customize Card-Tab for private notes, tab-style category views, safer drag/drop, duplicate warnings, a warm archive theme, and removal of the weather feature. + +**Architecture:** Keep the project as a single Cloudflare Worker file to preserve the original deployment model. Use small helper functions inside `workers.js`, avoid broad refactors, and add a dependency-free Node verification script for behavior checks that can run locally. + +**Tech Stack:** Cloudflare Workers JavaScript, browser DOM APIs, local Node.js verification. + +--- + +### Task 1: Add Local Verification + +**Files:** +- Create: `test/customization.test.mjs` +- Modify: `workers.js` + +- [x] Add a Node script that reads `workers.js` and asserts the agreed customization requirements. +- [x] Run the script before implementation and verify it fails on missing customization. +- [x] Run the script after each implementation slice. + +### Task 2: Remove Weather + +**Files:** +- Modify: `workers.js` +- Modify: `README.md` +- Modify: `CHANGELOG.md` + +- [x] Remove the weather mini widget, modal, frontend weather code, styles, and `/api/weather/*` Worker routes. +- [x] Remove README and changelog instructions for `WEATHER_API_KEY` and `WEATHER_API_HOST`. +- [x] Verify `node --check workers.js` and `node test/customization.test.mjs`. + +### Task 3: Private Notes + +**Files:** +- Modify: `workers.js` + +- [x] Add `privateNote` to add/edit dialogs and link objects. +- [x] Redact `privateNote` from unauthenticated `/api/getLinks` responses. +- [x] Add a logged-in note icon and modal with selectable text and copy button. +- [x] Preserve `privateNote` during drag/drop saves. +- [x] Verify syntax and customization tests. + +### Task 4: Category Tab Views + +**Files:** +- Modify: `workers.js` + +- [x] Replace category scroll behavior with `All` plus category view switching. +- [x] Sync selected view to the URL hash and restore it on reload. +- [x] Keep settings mode compatible with `All` for cross-category management. +- [x] Verify syntax and customization tests. + +### Task 5: Drag/Drop and Duplicate Warning + +**Files:** +- Modify: `workers.js` + +- [x] Keep drag/drop available only in settings mode. +- [x] Support category container drop targets, including empty categories. +- [x] Preserve all original link fields when saving order. +- [x] Warn when adding a duplicate normalized URL and allow the user to continue. +- [x] Verify syntax and customization tests. + +### Task 6: Archive Yellow Theme + +**Files:** +- Modify: `workers.js` + +- [x] Add a three-state theme model: warm light, archive yellow, dark. +- [x] Keep the existing dark theme behavior while allowing the archive palette. +- [x] Verify syntax and customization tests. diff --git a/test/customization.test.mjs b/test/customization.test.mjs new file mode 100644 index 0000000..a438c51 --- /dev/null +++ b/test/customization.test.mjs @@ -0,0 +1,33 @@ +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; + +const source = readFileSync(new URL('../workers.js', import.meta.url), 'utf8'); + +function assertAbsent(pattern, message) { + assert.equal(pattern.test(source), false, message); +} + +function assertPresent(pattern, message) { + assert.equal(pattern.test(source), true, message); +} + +assertAbsent(/WEATHER_API|weather-mini|\/api\/weather/, 'weather feature should be removed'); +assertAbsent(/hitokoto|search-engine-select|search-container|search-button/, 'top quote and search bar should be removed'); + +assertPresent(/privateNote/, 'private notes should be represented in link data'); +assertPresent(/sanitizeLinkForPublic|redactPrivateNote|privateNote:\s*undefined/, 'public responses should redact private notes'); +assertPresent(/note-modal|showNoteModal|copyPrivateNote/, 'private note modal and copy behavior should exist'); +assertPresent(/\/api\/favicon|resolveAutoFaviconUrl/, 'automatic favicon proxy should exist'); +assertAbsent(/faviconextractor|api\.iowen/, 'old external favicon services should not be used'); + +assertPresent(/currentCategoryView/, 'category tab view state should exist'); +assertPresent(/setCategoryView/, 'category navigation should switch views instead of scrolling'); +assertPresent(/location\.hash|history\.replaceState/, 'category view should sync to the URL hash'); + +assertPresent(/drop-target|setupDropContainers|card-container[^]*dragover/, 'category containers should be drop targets'); +assertPresent(/\.\.\.originalLink|Object\.assign\(\{\},\s*originalLink/, 'drag saves should preserve all link fields'); + +assertPresent(/findDuplicateLink|duplicate/i, 'duplicate URL warning should exist'); +assertPresent(/archive-theme|Archive Yellow|archive yellow/i, 'archive yellow theme should exist'); +assertPresent(/exportJsonBackup|导出JSON/, 'JSON export should exist'); +assertPresent(/triggerJsonImport|handleJsonImport|json-import-input/, 'JSON import should exist'); diff --git a/workers.js b/workers.js index 84aaade..7e4f424 100644 --- a/workers.js +++ b/workers.js @@ -23,6 +23,11 @@ const HTML_CONTENT = ` color: #e3e3e3; } + body.archive-theme { + background-color: #f4e7bf; + color: #2f2a1f; + } + /* 固定元素样式 */ .fixed-elements { position: fixed; @@ -33,7 +38,7 @@ const HTML_CONTENT = ` z-index: 1000; padding: 10px; transition: all 0.3s ease; - height: 150px; + height: 110px; box-shadow: none; /* 移除阴影 */ } @@ -42,6 +47,11 @@ const HTML_CONTENT = ` box-shadow: none; /* 移除阴影 */ } + body.archive-theme .fixed-elements { + background-color: #f4e7bf; + box-shadow: none; + } + /* 分类快捷按钮容器样式移至搜索栏内 */ .category-button { @@ -68,6 +78,12 @@ const HTML_CONTENT = ` box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); } + body.archive-theme .category-button { + background-color: #fff9e8; + color: #7a5419; + box-shadow: 0 2px 4px rgba(91, 63, 26, 0.12); + } + .category-button:hover { background-color: #43b883; color: white; @@ -91,6 +107,13 @@ const HTML_CONTENT = ` color: white; } + body.archive-theme .category-button:hover, + body.archive-theme .category-button.active { + background-color: #b7791f; + color: #fff9e8; + border-bottom-color: #7a5419; + } + /* 分类按钮悬停样式 */ .fixed-elements h3 { @@ -109,36 +132,6 @@ const HTML_CONTENT = ` color: #e3e3e3; } - /* 一言模块样式 */ - #hitokoto { - margin: 5px 0 15px; - font-size: 14px; - color: #888; - font-style: italic; - max-width: 600px; - margin-left: auto; - margin-right: auto; - transition: all 0.3s ease; - } - - #hitokoto a { - color: #43b883; - text-decoration: none; - transition: all 0.3s ease; - } - - #hitokoto a:hover { - color: #35a674; - } - - body.dark-theme #hitokoto { - color: #a0a0a0; - } - - body.dark-theme #hitokoto a { - color: #5d7fb9; - } - /* 中心内容样式 */ .center-content { position: absolute; @@ -623,10 +616,12 @@ const HTML_CONTENT = ` .remove-btn { order: 2; } .category-btn { order: 3; } .remove-category-btn { order: 4; } + .export-btn { order: 5; } + .import-btn { order: 6; } /* 主要内容区域样式 */ .content { - margin-top: 170px; + margin-top: 125px; padding: 10px; max-width: 1600px; margin-left: auto; @@ -638,114 +633,6 @@ const HTML_CONTENT = ` opacity: 0.6; } - /* 搜索栏样式 */ - .search-container { - margin-top: 10px; - display: flex; - flex-direction: column; - align-items: center; - width: 100%; - } - - .search-bar { - display: flex; - justify-content: center; - margin-bottom: 10px; - width: 100%; - max-width: 600px; - margin-left: auto; - margin-right: auto; - border-radius: 8px; - overflow: hidden; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); - border: 1px solid #e0e0e0; - transition: all 0.3s ease; - } - - .search-bar:focus-within { - box-shadow: 0 3px 12px rgba(0, 0, 0, 0.1); - border-color: #43b883; - } - - .search-bar select { - border: none; - background-color: #f4f7fa; - padding: 10px 15px; - font-size: 14px; - color: #43b883; - width: 120px; - outline: none; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-image: url('data:image/svg+xml;utf8,'); - background-repeat: no-repeat; - background-position: right 10px center; - cursor: pointer; - transition: all 0.3s ease; - border-radius: 0; - } - - /* 下拉菜单样式 */ - select option { - background-color: #fff; - color: #333; - padding: 10px; - font-size: 14px; - white-space: nowrap; - overflow: visible; - } - - /* 暗色主题搜索栏样式 */ - body.dark-theme .search-bar { - border-color: #323642; - background-color: #1e2128; - } - - body.dark-theme .search-bar select { - background-color: #252830; - color: #5d7fb9; - background-image: url('data:image/svg+xml;utf8,'); - } - - body.dark-theme .search-bar input { - background-color: #252830; - color: #e3e3e3; - } - - body.dark-theme .search-bar button { - background-color: #5d7fb9; - } - - body.dark-theme select option { - background-color: #252830; - color: #e3e3e3; - white-space: nowrap; - overflow: visible; - } - - .search-bar input { - flex: 1; - border: none; - padding: 10px 15px; - font-size: 14px; - background-color: #fff; - outline: none; - } - - .search-bar button { - border: none; - background-color: #43b883; - color: white; - padding: 0 20px; - cursor: pointer; - transition: background-color 0.3s; - } - - .search-bar button:hover { - background-color: #35a674; - } - /* 分类按钮容器样式 - 移至固定元素区域内 */ .category-buttons-container { display: flex; @@ -897,12 +784,28 @@ const HTML_CONTENT = ` transition: all 0.3s ease; } - #dialog-box input:focus, #dialog-box select:focus { + #dialog-box input:focus, + #dialog-box select:focus, + #dialog-box textarea:focus { border-color: #43b883; box-shadow: 0 0 0 2px rgba(67, 184, 131, 0.2); outline: none; } + #dialog-box textarea { + width: 100%; + min-height: 76px; + padding: 8px; + margin-bottom: 15px; + border: 1px solid #e0e0e0; + border-radius: 5px; + box-sizing: border-box; + resize: vertical; + font-family: inherit; + font-size: 14px; + line-height: 1.5; + } + #dialog-box label { display: block; margin-bottom: 5px; @@ -940,7 +843,8 @@ const HTML_CONTENT = ` } body.dark-theme #dialog-box input, - body.dark-theme #dialog-box select { + body.dark-theme #dialog-box select, + body.dark-theme #dialog-box textarea { background-color: #323642; color: #e3e3e3; border-color: #444; @@ -1034,6 +938,12 @@ const HTML_CONTENT = ` max-width: 1600px; } + .card-container.drop-target { + outline: 2px dashed #b7791f; + outline-offset: 6px; + border-radius: 8px; + } + .card { background-color: white; border-radius: 8px; @@ -1057,6 +967,12 @@ const HTML_CONTENT = ` box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); } + body.archive-theme .card { + background-color: #fff9e8; + border-left-color: #b7791f; + box-shadow: 0 3px 10px rgba(91, 63, 26, 0.12); + } + @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } @@ -1106,6 +1022,14 @@ const HTML_CONTENT = ` color: #a0a0a0; } + body.archive-theme .card-title { + color: #2f2a1f; + } + + body.archive-theme .card-url { + color: #766b58; + } + .private-tag { background-color: #ff9800; color: white; @@ -1182,229 +1106,13 @@ const HTML_CONTENT = ` background-color: #5d7fb9; } - /* ========== 天气组件样式 ========== */ - .weather-mini { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 4px 10px; - background: rgba(67, 184, 131, 0.1); - border-radius: 16px; - cursor: pointer; - transition: all 0.2s ease; - font-size: 13px; - margin-left: 12px; - vertical-align: middle; - } - .weather-mini:hover { - background: rgba(67, 184, 131, 0.2); - transform: translateY(-1px); - } - .weather-mini .weather-icon { font-size: 16px; } - .weather-mini .weather-temp { font-weight: 600; color: #333; } - .weather-mini .weather-city { color: #666; font-size: 12px; } - .weather-mini .weather-loading { color: #999; font-size: 12px; } - - body.dark-theme .weather-mini { - background: rgba(93, 127, 185, 0.15); - } - body.dark-theme .weather-mini:hover { - background: rgba(93, 127, 185, 0.25); - } - body.dark-theme .weather-mini .weather-temp { color: #e3e3e3; } - body.dark-theme .weather-mini .weather-city { color: #aaa; } - - /* 天气弹窗 */ - .weather-modal { - display: none; - position: fixed; - top: 0; left: 0; right: 0; bottom: 0; - background: rgba(0,0,0,0.5); - z-index: 2000; - justify-content: center; - align-items: center; - } - .weather-modal.show { display: flex; } - .weather-modal-content { - background: #fff; - border-radius: 16px; - padding: 20px; - width: 90%; - max-width: 360px; - box-shadow: 0 10px 40px rgba(0,0,0,0.2); - animation: weatherModalIn 0.25s ease; - } - @keyframes weatherModalIn { - from { opacity: 0; transform: scale(0.9) translateY(-20px); } - to { opacity: 1; transform: scale(1) translateY(0); } - } - .weather-modal-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 16px; - padding-bottom: 12px; - border-bottom: 1px solid #eee; - } - .weather-modal-title { font-size: 16px; font-weight: 600; color: #333; } - .weather-modal-close { - background: none; border: none; - font-size: 20px; cursor: pointer; - color: #999; padding: 0; line-height: 1; - } - .weather-modal-close:hover { color: #333; } - - /* 城市搜索 */ - .weather-search { - position: relative; - margin-bottom: 16px; - } - .weather-search input { - width: 100%; - padding: 10px 12px; - border: 1px solid #ddd; - border-radius: 8px; - font-size: 14px; - outline: none; - box-sizing: border-box; - } - .weather-search input:focus { border-color: #43b883; } - .weather-search-results { - position: absolute; - top: 100%; - left: 0; right: 0; - background: #fff; - border: 1px solid #ddd; - border-radius: 8px; - margin-top: 4px; - max-height: 200px; - overflow-y: auto; - display: none; - z-index: 10; - } - .weather-search-results.show { display: block; } - .weather-search-item { - padding: 10px 12px; - cursor: pointer; - border-bottom: 1px solid #eee; - } - .weather-search-item:last-child { border-bottom: none; } - .weather-search-item:hover { background: #f5f5f5; } - .weather-search-item-name { font-weight: 500; font-size: 14px; } - .weather-search-item-path { font-size: 12px; color: #999; margin-top: 2px; } - - /* 定位模式切换 */ - .weather-mode-switch { - display: flex; - gap: 8px; - margin-bottom: 16px; - } - .weather-mode-btn { - flex: 1; - padding: 8px 12px; - border: 1px solid #ddd; - border-radius: 8px; - background: #fff; - font-size: 13px; - cursor: pointer; - transition: all 0.2s; - display: flex; - align-items: center; - justify-content: center; - gap: 4px; - } - .weather-mode-btn:hover { border-color: #43b883; } - .weather-mode-btn.active { - background: #43b883; - border-color: #43b883; - color: #fff; - } - body.dark-theme .weather-mode-btn { - background: #3a3a3a; - border-color: #555; - color: #e3e3e3; - } - body.dark-theme .weather-mode-btn:hover { border-color: #43b883; } - body.dark-theme .weather-mode-btn.active { - background: #43b883; - border-color: #43b883; - color: #fff; - } - - /* 当前天气 */ - .weather-current { - text-align: center; - padding: 16px 0; - border-bottom: 1px solid #eee; - margin-bottom: 16px; - } - .weather-current-icon { font-size: 48px; margin-bottom: 8px; } - .weather-current-temp { font-size: 36px; font-weight: 300; color: #333; } - .weather-current-desc { font-size: 16px; color: #666; margin: 4px 0; } - .weather-current-detail { font-size: 13px; color: #999; } - - /* 天气预报 */ - .weather-forecast { - display: flex; - justify-content: space-between; - gap: 8px; - } - .weather-forecast-item { - flex: 1; - text-align: center; - padding: 12px 8px; - background: #f8f9fa; - border-radius: 10px; - } - .weather-forecast-day { font-size: 13px; font-weight: 500; color: #333; margin-bottom: 6px; } - .weather-forecast-icon { font-size: 24px; margin: 6px 0; } - .weather-forecast-temp { font-size: 12px; color: #666; } - .weather-forecast-temp .high { color: #e74c3c; } - .weather-forecast-temp .low { color: #3498db; } - - /* 天气未配置状态 */ - .weather-not-configured { - text-align: center; - padding: 40px 20px; - color: #999; - font-size: 14px; - } - .weather-search input:disabled { - background: #f5f5f5; - cursor: not-allowed; - color: #999; - } - body.dark-theme .weather-not-configured { color: #666; } - body.dark-theme .weather-search input:disabled { - background: #2a2e38; - color: #666; - } - - /* 天气弹窗暗色主题 */ - body.dark-theme .weather-modal-content { background: #1e2128; } - body.dark-theme .weather-modal-header { border-bottom-color: #333; } - body.dark-theme .weather-modal-title { color: #e3e3e3; } - body.dark-theme .weather-modal-close { color: #888; } - body.dark-theme .weather-modal-close:hover { color: #e3e3e3; } - body.dark-theme .weather-search input { background: #2a2e38; border-color: #444; color: #e3e3e3; } - body.dark-theme .weather-search input:focus { border-color: #5d7fb9; } - body.dark-theme .weather-search-results { background: #2a2e38; border-color: #444; } - body.dark-theme .weather-search-item:hover { background: #333; } - body.dark-theme .weather-search-item { border-bottom-color: #444; } - body.dark-theme .weather-current { border-bottom-color: #333; } - body.dark-theme .weather-current-temp { color: #e3e3e3; } - body.dark-theme .weather-current-desc { color: #aaa; } - body.dark-theme .weather-forecast-item { background: #2a2e38; } - body.dark-theme .weather-forecast-day { color: #e3e3e3; } - body.dark-theme .weather-forecast-temp { color: #aaa; } - /* 响应式设计 */ @media (max-width: 480px) { .fixed-elements { position: fixed; /* 恢复固定定位,确保分类按钮位置正确 */ padding: 8px 12px 5px 12px; /* 紧凑的内边距 */ height: auto; - min-height: 140px; /* 增加最小高度,确保有足够空间 */ + min-height: 110px; /* 保留分类按钮所需空间 */ box-shadow: none; /* 移除阴影 */ } @@ -1412,14 +1120,6 @@ const HTML_CONTENT = ` box-shadow: none; /* 移除阴影 */ } - /* 移动端一言样式调整 - 紧凑显示 */ - #hitokoto { - margin: 3px 0 6px 0; /* 紧凑的上下边距 */ - font-size: 12px; /* 减小字体 */ - line-height: 1.3; /* 紧凑行高 */ - padding: 0 8px; /* 左右内边距 */ - } - .category-buttons-container { width: 100%; max-width: none; @@ -1446,7 +1146,7 @@ const HTML_CONTENT = ` } .content { - margin-top: 150px; /* 增加顶部边距,适配更高的固定元素 */ + margin-top: 120px; /* 适配更紧凑的顶部区域 */ margin-bottom: 100px; /* 为底部的分类按钮和版权信息留出空间 */ padding: 15px; /* 保持内边距 */ transition: opacity 0.3s ease; @@ -1465,32 +1165,6 @@ const HTML_CONTENT = ` opacity: 0.6; } - /* 移动端搜索容器样式 */ - .search-container { - margin-top: 15px; /* 增加上边距,与右上角按钮拉开距离 */ - } - - .search-bar { - flex-wrap: nowrap; - max-width: 320px; /* 限制移动端搜索栏宽度 */ - width: 90%; /* 相对宽度 */ - margin: 6px auto 8px auto; /* 居中显示 */ - } - - .search-bar select { - width: 80px; /* 缩小选择框宽度,参考佬友修改版 */ - flex: 0 0 auto; - font-size: 12px; /* 减小字体以适应更小宽度 */ - } - - .search-bar input { - flex: 1; - } - - .search-bar button { - flex: 0 0 auto; - } - .admin-controls input, .admin-controls button { height: 36px; @@ -1963,6 +1637,46 @@ const HTML_CONTENT = ` background: #e74c3c; } + .note-btn { + background: #b7791f; + } + + .note-modal-content { + max-width: 560px; + width: min(92vw, 560px); + } + + .private-note-meta { + margin-bottom: 12px; + color: #766b58; + font-size: 13px; + word-break: break-all; + } + + .private-note-content { + min-height: 120px; + max-height: 45vh; + overflow: auto; + padding: 14px; + border: 1px solid #d8c28a; + border-radius: 6px; + background: #fff9e8; + color: #2f2a1f; + line-height: 1.7; + white-space: pre-wrap; + user-select: text; + } + + body.dark-theme .private-note-meta { + color: #9ba8bd; + } + + body.dark-theme .private-note-content { + background: #1e2128; + color: #e3e3e3; + border-color: #3a4050; + } + /* 自定义提示框样式 */ #custom-tooltip { position: absolute; @@ -2009,26 +1723,7 @@ const HTML_CONTENT = `
+ @@ -2108,8 +1820,10 @@ const HTML_CONTENT = ` + + - +