diff --git a/plugins/hushreader/CHANGELOG.md b/plugins/hushreader/CHANGELOG.md index be4c5bb6..76ee9a34 100644 --- a/plugins/hushreader/CHANGELOG.md +++ b/plugins/hushreader/CHANGELOG.md @@ -1,6 +1,26 @@ # Changelog All notable changes to this project will be documented in this file. +## [1.3.3](https://github.com/me1dlinger/hushreader/releases/tag/v1.3.3) - 2026-06-19 + +### Added +- **自动检测系统深浅色模式**:在插件加载时,根据系统深浅色模式自动切换主题,支持手动切换主题 +- **资源管理器打开文件位置**:在书籍右键菜单新增"打开文件位置"选项,使用ztools.shellShowItemInFolder() +- **隐阅定时器**:新增设置项,打开定时器后可设置一个时长,隐阅时间到达之后自动关闭隐阅窗口并Toast提示用户 +- **备份和恢复所有阅读进度与配置**:备份和恢复所有阅读进度与配置。插件设置导出,所有书籍的元数据导出。实现设置导入,书籍数据导入 +- **多配置切换**:实现多配置切换、添加和删除(最少保留一个配置),导入的配置自动添加为新配置 + +### Changed +- **分离配置和书籍的导入导出**:将配置和书籍的导入导出分离,配置导入导出时仅和配置相关,书籍导入导出时仅和书籍数据相关 +- **定时器相关通知**:在隐阅界面弹出相应通知 + +### Fixed +- **配置无法导入的问题**:修复配置无法导入的问题 +- **优化代码** :减少不必要的性能开销 +- **系统主题自动切换**:修复系统主题发生变化时,systemDark 和 effectiveTheme 无法自动更新 +- **优化书籍导入流程**:在导入书籍的循环中,每次调用 bookStore.addBook(book) 都会触发一次同步的 save() 写入操作(保存到 dbStorage 或 localStorage)。如果用户导入的书籍数量较多,会产生大量连续的同步 I/O 写入 +- **JSON配置导入漏洞**:如果导入的备份 JSON 文件被恶意篡改,包含 __proto__ 或 constructor 等属性,可能会导致原型链污染(Prototype Pollution)漏洞 + ## [1.3.2](https://github.com/me1dlinger/hushreader/releases/tag/v1.3.2) - 2026-06-18 diff --git a/plugins/hushreader/README.md b/plugins/hushreader/README.md index 9789cae1..534623c2 100644 --- a/plugins/hushreader/README.md +++ b/plugins/hushreader/README.md @@ -10,14 +10,22 @@ *** +[更新日志](./CHANGELOG.md) + +*** + ## 功能一览 | 阅读体验 | 个性化 | 书架管理 | | :------------------ | :--------------- | :---------------- | -| 沉浸式悬浮窗口,可置于任意位置 | 背景透明度与整体透明度分离控制 | 支持按添加时间、书名、作者排序 | -| 滚轮翻页 / 快捷键翻页 / 自动翻页 | 自定义字体,支持添加系统字体 | 右键编辑元数据(标题、作者) | +| 沉浸式悬浮窗口,可置于任意位置 | 背景透明度与整体透明度分离控制 | 支持按添加时间、书名、作者、最近阅读排序 | +| 滚轮翻页 / 快捷键翻页 / 自动翻页 | 自定义字体,支持添加系统字体 | 右键编辑元数据(标题、作者、分类) | | 只显示完整行,文字不残缺 | 十六进制颜色输入 + 颜色选择器 | 阅读进度持久化,关闭再开继续读 | -| 文本预处理:压缩空行、清理空白 | 设置实时预览,取消可还原 | — | +| 文本预处理:压缩空行、清理空白 | 设置实时预览,取消可还原 | 拖拽导入 / 快捷文件导入(`导入书籍`) | +| 鼠标移出隐藏三模式(关闭/仅进度/全隐藏) | 亮色 / 暗色主题切换 | 书籍信息窗口(封面、简介、分类、阅读统计) | +| 鼠标移入显示延迟可调 | 列表书架模式 | 多选模式 + 批量重载/删除 | +| 百分比进度编辑跳转 | 窗口大小锁定 | 重载元数据 / 恢复封面 | +| 章节列表高亮当前进度 | — | 书籍分类筛选栏 | ## 截图 @@ -34,7 +42,8 @@ ### 安装 -下载release文件,在ZTools中导入 +- 已上架市场,ZTools插件市场搜索**隐阅盒**,点击**安装**按钮 +- 下载github-release文件,在ZTools**搜索框**完成导入 ### 开发 @@ -48,7 +57,7 @@ npm run build # 构建生产版本 在 ZTools 中输入以下关键词即可唤起: -`yyh` · `摸鱼阅读` · `隐阅盒` · `书架` · `hushreader` +`隐阅盒` · `hushreader` · `摸鱼阅读` · `书架` · `yyh` · `开始阅读` · `导入书籍`(支持拖入 txt/epub/mobi 文件) ## 技术细节 @@ -57,18 +66,27 @@ npm run build # 构建生产版本 阅读进度使用**字符偏移量**(`progressIndex`)而非页码保存,窗口大小或字体变化时进度不会丢失。 -**存储策略**:优先使用 `ztools.dbStorage`,回退到 `localStorage`。 +**存储策略**:轻量数据(书籍列表不含封面、阅读进度、配置)使用 `ztools.dbStorage`,回退到 `localStorage`;封面和章节内容使用 `ztools.db`(PouchDB 风格数据库),通过 `ztools.db.promises` API 管理 `cover_{bookId}`、`custom_cover_{bookId}`、`chapters_{bookId}` 等文档。 **保存时机**:翻页时 · 章节切换时 · 自动翻页 tick · 插件退出时 **每本书保存**: -| 字段 | 说明 | -| --------------- | -------- | -| `lastChapter` | 当前章节索引 | -| `progressIndex` | 章节内字符偏移量 | -| `lastReadAt` | 最后阅读时间戳 | -| `totalChapters` | 总章节数 | +| 字段 | 说明 | +| ------------------ | ----------- | +| `lastChapter` | 当前章节索引 | +| `progressIndex` | 章节内字符偏移量 | +| `lastReadAt` | 最后阅读时间戳 | +| `totalChapters` | 总章节数 | +| `firstReadAt` | 首次阅读时间戳 | +| `readingTimeMs` | 累计阅读时长(毫秒) | +| `readingSpeed` | 阅读速度(字/分钟) | +| `readingPercent` | 阅读百分比 | +| `updatedAt` | 更新时间戳 | +| `description` | 书籍简介 | +| `categories` | 分类数组 | +| `customCoverImage` | 自定义封面 | +| `fileModifiedAt` | 文件修改时间 | @@ -177,14 +195,14 @@ HTML/纯文本判断 → 分章解析或按 TXT 逻辑处理 ├── src/ │ ├── App.vue # 根组件 │ ├── main.ts # 应用入口 -│ ├── main.css # 应用样式 -│ ├── env.d.ts #环境变量类型定义 +│ ├── main.css # 应用样式 +│ ├── env.d.ts # 环境变量类型定义 │ ├── stores/ │ │ ├── books.ts # 书籍数据 + 持久化 │ │ ├── config.ts # 配置数据 + 持久化 │ │ └── reader.ts # 阅读器状态 + 分页 │ ├── utils/ -│ │ ├── db.ts # 数据库操作工具 +│ │ ├── db.ts # 数据库操作工具(封面/章节缓存) │ │ ├── txtParser.ts # TXT 解析 + 分页 + 预处理 │ │ ├── epubParser.ts # EPUB 解析 │ │ └── mobiParser.ts # MOBI 解析 + 加密检测 + 封面提取 @@ -192,17 +210,21 @@ HTML/纯文本判断 → 分章解析或按 TXT 逻辑处理 │ ├── Bookshelf/ # 书架组件 │ │ ├── index.vue │ │ ├── BookCard.vue -│ │ ├── ContextMenu.vue -│ │ ├── Modal.vue -│ │ └── Toast.vue +│ │ ├── BookInfoModal.vue # 书籍信息窗口 +│ │ ├── ContextMenu.vue # 右键菜单 +│ │ ├── Modal.vue # 通用弹窗 +│ │ ├── ThemeToggle.vue # 主题切换 +│ │ └── Toast.vue # Toast 提示 │ └── Settings/ # 设置面板 │ └── index.vue ├── .gitignore +├── CHANGELOG.md # 更新日志 ├── LICENSE ├── package.json ├── tsconfig.json ├── tsconfig.node.json -└── vite.config.ts +├── vite.config.ts +└── ztools.api.md # ZTools API 文档 ``` ## 开源协议 diff --git a/plugins/hushreader/package.json b/plugins/hushreader/package.json index 3d881799..2065aa5f 100644 --- a/plugins/hushreader/package.json +++ b/plugins/hushreader/package.json @@ -1,7 +1,7 @@ { "name": "hushreader", "private": true, - "version": "1.0.0", + "version": "1.3.3", "type": "module", "scripts": { "dev": "vite", diff --git a/plugins/hushreader/public/hushreader.html b/plugins/hushreader/public/hushreader.html index 8522bf52..000bc033 100644 --- a/plugins/hushreader/public/hushreader.html +++ b/plugins/hushreader/public/hushreader.html @@ -384,6 +384,77 @@ background: transparent; color: var(--hushreader-text-color); } + + .hushreader-timer { + position: absolute; + top: 4px; + left: 10px; + z-index: 4; + padding: 1px 6px; + border-radius: 3px; + font-size: 10px; + line-height: 1.3; + opacity: 0; + pointer-events: none; + color: var(--hushreader-text-color); + font-variant-numeric: tabular-nums; + transition: opacity 120ms ease; + } + + .show-timer .hushreader-timer { + opacity: 0.66; + } + + .hushreader-notification { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 50; + display: flex; + align-items: center; + justify-content: center; + background: rgba(0, 0, 0, 0.55); + backdrop-filter: blur(4px); + opacity: 0; + pointer-events: none; + transition: opacity 200ms ease; + } + + .hushreader-notification.visible { + opacity: 1; + pointer-events: auto; + } + + .hushreader-notification-box { + max-width: 80%; + padding: 10px 18px; + border-radius: 8px; + background: rgba(30, 31, 34, 0.92); + color: var(--hushreader-text-color); + font-size: 12px; + line-height: 1.5; + text-align: center; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); + } + + .hushreader-notification-box .notif-action { + display: inline-block; + margin-top: 8px; + padding: 3px 14px; + border: 1px solid rgba(255, 255, 255, 0.25); + border-radius: 4px; + background: transparent; + color: var(--hushreader-text-color); + font-size: 11px; + cursor: pointer; + transition: background 120ms ease; + } + + .hushreader-notification-box .notif-action:hover { + background: rgba(255, 255, 255, 0.1); + } @@ -396,18 +467,26 @@
+
+
+
+
+ +
+
diff --git a/plugins/hushreader/src/components/Bookshelf/ContextMenu.vue b/plugins/hushreader/src/components/Bookshelf/ContextMenu.vue index bad1a4ab..1b1b114b 100644 --- a/plugins/hushreader/src/components/Bookshelf/ContextMenu.vue +++ b/plugins/hushreader/src/components/Bookshelf/ContextMenu.vue @@ -6,6 +6,7 @@ const emit = defineEmits<{ 'book-info': [] 'chapter-list': [] 'change-path': [] + 'open-file-location': [] 'edit-metadata': [] 'reload-metadata': [] 'set-category': [] @@ -54,6 +55,10 @@ watch( 修改本地路径 +
  • + + 打开文件位置 +
  • 编辑元数据 diff --git a/plugins/hushreader/src/components/Bookshelf/ThemeToggle.vue b/plugins/hushreader/src/components/Bookshelf/ThemeToggle.vue index 65fca67e..dfe3beb9 100644 --- a/plugins/hushreader/src/components/Bookshelf/ThemeToggle.vue +++ b/plugins/hushreader/src/components/Bookshelf/ThemeToggle.vue @@ -1,35 +1,86 @@ diff --git a/plugins/hushreader/src/components/Bookshelf/index.vue b/plugins/hushreader/src/components/Bookshelf/index.vue index b03b928e..287c4532 100644 --- a/plugins/hushreader/src/components/Bookshelf/index.vue +++ b/plugins/hushreader/src/components/Bookshelf/index.vue @@ -153,6 +153,18 @@ function confirmPath() { toast('路径已更新', 'success') } +function openFileLocation(bookId: string) { + closeContextMenu() + const book = bookStore.books.find(b => b.id === bookId) + if (!book) return + try { + const result = (window as any).ztools?.shellShowItemInFolder?.(book.filePath) + if (!result) toast('无法打开文件位置', 'error') + } catch { + toast('无法打开文件位置', 'error') + } +} + // Category modal const showCategoryModal = ref(false) const categoryModalBookId = ref(null) @@ -946,10 +958,10 @@ const cfg = computed(() => configStore.config) + @open-file-location="openFileLocation(contextMenuBook!)" @edit-metadata="openMetadataModal(contextMenuBook!)" + @reload-metadata="openReloadMetadata(contextMenuBook!)" @set-category="openCategoryModal(contextMenuBook!)" + @set-cover="openCoverPicker(contextMenuBook!)" @restore-cover="openRestoreCover(contextMenuBook!)" + @delete="openDeleteModal(contextMenuBook!)" @close="closeContextMenu" /> diff --git a/plugins/hushreader/src/components/Settings/index.vue b/plugins/hushreader/src/components/Settings/index.vue index c901b9e9..4ef54bc8 100644 --- a/plugins/hushreader/src/components/Settings/index.vue +++ b/plugins/hushreader/src/components/Settings/index.vue @@ -1,12 +1,25 @@