From 96681194c7823c2b4e7d4bb4c91a190aace921c8 Mon Sep 17 00:00:00 2001 From: meidlinger Date: Wed, 24 Jun 2026 21:11:12 +0800 Subject: [PATCH 1/2] =?UTF-8?q?Update=20plugin=20=E9=9A=90=E9=98=85?= =?UTF-8?q?=E7=9B=92=20v1.4.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - feat: 初始化ZTools插件项目 - feat: 实现隐阅盒阅读器插件 - chore(gitignore): add ignore rules - chore: 相关标识重命名 - chore: add changelog - fix: 修复书架导入书籍异常未捕获、完善本地存储数据兼容逻辑 - docs: 更新README - docs: 重写README - feat: 发布1.1.0版本,新增多项功能并修复多个bug - fix(窗口拖动): 修复阅读窗口拖动拉伸卡顿问题 - chore: bump plugin version to 1.1.1 - chore: 重命名部分称呼 - feat(setting): add plain cover option, remove epub cache storage - feat: 新增MOBI电子书格式支持 - feat: 迁移封面和章节缓存到ztools.db - feat: 新增主题模式切换功能,优化深色主题样式 - chore: release v1.3.0, update plugin info and changelog - style: 优化窗口进度显示 - chore: 发布v1.3.1版本,更新功能与修复问题 - chore: bump plugin version to 1.3.1 - fix: 修复多个已知问题并优化部分功能 - style(settings): 优化设置项的封面提示文案 - fix(bookshelf): 修复纯色封面关闭后MOBI封面未恢复的问题 - docs: 更新README中的截图 - feat(bookshelf): 实现拖拽导入书籍功能 - feat(bookshelf): 增加快捷导入命令 - chore: update gitignore and changelog - feat(tips): 增加提示文本 ,更新版本标识 - feat: 新增多选批量操作、重载元数据和恢复封面功能 - style(plugin.json): 调整插件命令列表的排序顺序 - feat: 新增书籍信息弹窗与阅读数据追踪,优化元数据解析 - refactor: 完成bug修复 ### 详细变更 1. 修复MOBI封面解析:将Blob URL替换为可持久化的Base64格式,解决IndexedDB缓存失效问题 2. 优化书籍导入查重逻辑:通过文件名匹配避免重复导入同书籍 3. 重构阅读时长与速度统计:新增会话计时器避免跨会话闲置时间被计入,修复首页阅读时间丢失问题 4. 完善元数据加载容错:文件读取失败时不再静默清空原有元数据,抛出明确错误提示 5. 优化书籍信息编辑交互:新增分类批量添加、快捷键触发功能,修复分类选择UI缺陷 - docs: update README - release: bump version to 1.3.2 and update docs - feat: 发布v1.3.3版本,新增多项功能与优化 - release: bump version to 1.3.3 - refactor(settings): 分离配置和书籍的导入导出,修复配置导入问题 - fix: 修复多项问题并调整阅读定时器通知逻辑 - fix(Settings): 为新书籍数组添加类型声明,修复build错误 - feat: 发布v1.4.0版本,新增书签、已读标记等功能 - feat(bookshelf): 新增全文搜索跳转和批量设置分类功能 - feat(bookshelf): 新增视图切换按钮,增加书记格式标识,优化已读完标识的显示逻辑 --- plugins/hushreader/.gitignore | 4 +- plugins/hushreader/CHANGELOG.md | 20 + plugins/hushreader/package.json | 2 +- plugins/hushreader/public/hushreader.html | 37 +- plugins/hushreader/public/plugin.json | 2 +- plugins/hushreader/src/App.vue | 46 +- .../src/components/Bookshelf/BookCard.vue | 102 ++- .../src/components/Bookshelf/ContextMenu.vue | 18 +- .../src/components/Bookshelf/index.vue | 653 +++++++++++++++++- .../src/components/Settings/index.vue | 26 +- plugins/hushreader/src/stores/books.ts | 11 + plugins/hushreader/src/stores/config.ts | 4 + plugins/hushreader/src/stores/reader.ts | 6 +- plugins/hushreader/ztools.api.md | 543 --------------- 14 files changed, 873 insertions(+), 601 deletions(-) delete mode 100644 plugins/hushreader/ztools.api.md diff --git a/plugins/hushreader/.gitignore b/plugins/hushreader/.gitignore index eb8e7331..e0b713c9 100644 --- a/plugins/hushreader/.gitignore +++ b/plugins/hushreader/.gitignore @@ -1,4 +1,6 @@ node_modules dist **/*.zip -test \ No newline at end of file +test +功能说明文档.md +docs/ diff --git a/plugins/hushreader/CHANGELOG.md b/plugins/hushreader/CHANGELOG.md index 76ee9a34..c57ed9d9 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.4.0](https://github.com/me1dlinger/hushreader/releases/tag/v1.4.0) - 2026-06-23 + +### Added +- **已读完标记**:读完最后一章时自动标记为"已读完",书籍卡片封面右上角显示"已读完"角标 +- **标记为未读完**:右键菜单新增"标记为未读完"选项,可清除已读完状态(仅已读完书籍显示) +- **书架统计栏**:书架顶部新增统计栏,显示总书籍数、已读数、累计阅读时长 +- **书签功能**:隐阅窗口右键菜单新增"添加书签"选项,自动提取当前页首句作为书签内容 +- **书签快捷键**:隐阅窗口支持可配置的添加书签快捷键(默认 Shift+F) +- **书签列表**:书架书籍右键菜单新增"书签列表"选项,显示日期、阅读进度和内容预览 +- **书签跳转**:双击书签可打开隐阅窗口并跳转到书签位置 +- **书签删除**:书签列表中每条书签支持直接删除,无需确认 +- **销毁隐阅窗口**:新增可配置的销毁快捷键(默认 Shift+D),快速关闭隐阅窗口 +- **全文搜索跳转**:书籍右键菜单新增"搜索跳转"选项,可搜索全书内容,按句子展示结果,支持分页浏览、关键词高亮、双击跳转到对应位置 +- **批量设置分类**:多选模式下新增"批量设置分类"按钮,可统一为选中书籍设置分类 +- **卡片/列表视图切换**:书架头部新增视图切换按钮,支持卡片视图与列表视图一键切换,移除设置界面"其他设置"中的"列表书架模式"开关 + +### Fixed +- **阅读百分比无法达到100%**:修复翻到最后一页时百分比达不到100%的问题,最后一页使用页末字符偏移计算进度 + ## [1.3.3](https://github.com/me1dlinger/hushreader/releases/tag/v1.3.3) - 2026-06-19 ### Added diff --git a/plugins/hushreader/package.json b/plugins/hushreader/package.json index 2065aa5f..fe6a0b30 100644 --- a/plugins/hushreader/package.json +++ b/plugins/hushreader/package.json @@ -1,7 +1,7 @@ { "name": "hushreader", "private": true, - "version": "1.3.3", + "version": "1.4.0", "type": "module", "scripts": { "dev": "vite", diff --git a/plugins/hushreader/public/hushreader.html b/plugins/hushreader/public/hushreader.html index 000bc033..b6e48a0d 100644 --- a/plugins/hushreader/public/hushreader.html +++ b/plugins/hushreader/public/hushreader.html @@ -1015,6 +1015,8 @@ const binding = getKeyboardEventBinding(event) const prevPageKey = normalizeKeyBinding(latestSettings.prevPageKey, "ArrowLeft") const nextPageKey = normalizeKeyBinding(latestSettings.nextPageKey, "ArrowRight") + const addBookmarkKey = normalizeKeyBinding(latestSettings.addBookmarkKey, "Shift+F") + const destroyKey = normalizeKeyBinding(latestSettings.destroyKey, "Shift+D") if (binding && binding === nextPageKey) { event.preventDefault() @@ -1022,6 +1024,12 @@ } else if (binding && binding === prevPageKey) { event.preventDefault() sendCommand("prev") + } else if (binding && binding === addBookmarkKey) { + event.preventDefault() + sendCommand("add-bookmark") + } else if (binding && binding === destroyKey) { + event.preventDefault() + sendCommand("destroy") } }) @@ -1161,23 +1169,26 @@ document.addEventListener("contextmenu", (event) => { event.preventDefault() - if (!latestSettings.showHushreaderMeta) return - - const metaRect = metaNode.getBoundingClientRect() - const inMetaArea = event.clientX >= metaRect.left - 4 && event.clientX <= metaRect.right + 4 - && event.clientY >= metaRect.top - 4 && event.clientY <= metaRect.bottom + 4 - - if (!inMetaArea) return const items = [] - items.push({ label: "关闭隐阅窗口", command: "close-reader" }) - items.push({ label: "显示主窗口", command: "show-main" }) - if (isAutoPaging) { - items.push({ label: "停止自动翻页", command: "stop-auto" }) - } else { - items.push({ label: "开启自动翻页", command: "start-auto" }) + if (latestSettings.showHushreaderMeta) { + const metaRect = metaNode.getBoundingClientRect() + const inMetaArea = event.clientX >= metaRect.left - 4 && event.clientX <= metaRect.right + 4 + && event.clientY >= metaRect.top - 4 && event.clientY <= metaRect.bottom + 4 + if (inMetaArea) { + items.push({ label: "关闭隐阅窗口", command: "close-reader" }) + items.push({ label: "添加书签", command: "add-bookmark" }) + items.push({ label: "显示主窗口", command: "show-main" }) + if (isAutoPaging) { + items.push({ label: "停止自动翻页", command: "stop-auto" }) + } else { + items.push({ label: "开启自动翻页", command: "start-auto" }) + } + } } + if (items.length === 0) return + contextMenuNode.innerHTML = "" items.forEach((item) => { const btn = document.createElement("button") diff --git a/plugins/hushreader/public/plugin.json b/plugins/hushreader/public/plugin.json index 47e9bd2e..fed86cf4 100644 --- a/plugins/hushreader/public/plugin.json +++ b/plugins/hushreader/public/plugin.json @@ -4,7 +4,7 @@ "title": "隐阅盒", "description": "ZTools自己的摸鱼阅读,支持TXT/EPUB/MOBI格式,沉浸式阅读", "author": "meidlinger", - "version": "1.3.3", + "version": "1.4.0", "main": "index.html", "preload": "preload/services.js", "logo": "logo.png", diff --git a/plugins/hushreader/src/App.vue b/plugins/hushreader/src/App.vue index 8076d42b..fd33783d 100644 --- a/plugins/hushreader/src/App.vue +++ b/plugins/hushreader/src/App.vue @@ -3,7 +3,7 @@ import { computed, nextTick, onBeforeUnmount, onMounted, provide, ref, watch } f import Bookshelf from './components/Bookshelf/index.vue' import Toast from './components/Bookshelf/Toast.vue' import { useReaderStore } from './stores/reader' -import { useBookStore } from './stores/books' +import { useBookStore, type Bookmark } from './stores/books' import { useConfigStore } from './stores/config' import { parseTxt } from './utils/txtParser' import { parseEpub } from './utils/epubParser' @@ -208,6 +208,8 @@ function getHushreaderPayload(bounds = getHushreaderWindowBounds()) { bgOpacity: hushreaderCfg.value.bgOpacity, prevPageKey: hushreaderCfg.value.prevPageKey, nextPageKey: hushreaderCfg.value.nextPageKey, + addBookmarkKey: hushreaderCfg.value.addBookmarkKey, + destroyKey: hushreaderCfg.value.destroyKey, showHushreaderMeta: hushreaderCfg.value.showHushreaderMeta, progressMode: hushreaderCfg.value.progressMode, hideOnMouseLeave: hushreaderCfg.value.hideOnMouseLeave, @@ -466,6 +468,10 @@ function saveReadingProgress() { updates.lastSaveReadChars = Math.round(totalChars * (readerStore.readingPercent / 100)) } + if (readerStore.readingPercent >= 100 && !book.finishedAt) { + updates.finishedAt = now + } + (window as any).__hushreaderSessionLastActive = now bookStore.updateBook(book.id, updates) } @@ -629,9 +635,45 @@ function handleHushreaderCommand(command: HushreaderCommand) { else if (command === 'close') closePlugin() else if (command === 'auto') toggleAutoPaging() else if (command === 'close-reader') { isReaderHidden.value = true; blurHushreaderKeyboard() } + else if (command === 'destroy') { saveReadingProgress(); hushreaderActivated.value = false; stopReadingTimer(); hushreaderWindow?.close?.() } else if (command === 'show-main') { (window as any).ztools?.showMainWindow?.() } else if (command === 'stop-auto') { isAutoPaging.value = false; hushreaderCfg.value.autoFlipEnabled = false } else if (command === 'start-auto') { if (currentBook.value) { isAutoPaging.value = true; hushreaderCfg.value.autoFlipEnabled = true } } + else if (command === 'add-bookmark') { + const book = currentBook.value + if (book) { + const pageText = hushreaderLines.value.join('') + const boundaryRe = /[。!?…!?\.\」\』\)\】\」]/ + let start = 0 + const boundary = boundaryRe.exec(pageText) + if (boundary) { + start = boundary.index + boundary[0].length + while (start < pageText.length && /[\s\u3000]/.test(pageText[start])) start++ + } + if (start >= pageText.length) start = 0 + const rest = pageText.slice(start) + const sentenceEndRe = /[。!?…!?\.]/ + let text = '' + const endMatch = sentenceEndRe.exec(rest) + if (endMatch) { + text = rest.slice(0, endMatch.index + endMatch[0].length).trim() + } else { + text = rest.trim().slice(0, 50) + } + if (text.length > 50) text = text.slice(0, 50) + '...' + const bookmark: Bookmark = { + id: `bm_${Date.now()}_${Math.random().toString(36).slice(2)}`, + chapterIndex: readerStore.currentChapterIndex, + charIndex: readerStore.currentPageSlice.startIndex, + readingPercent: readerStore.readingPercent, + text, + createdAt: Date.now() + } + const existing = book.bookmarks ?? [] + bookStore.updateBook(book.id, { bookmarks: [...existing, bookmark] }) + toast('书签已添加', 'success') + } + } else if (command === 'notification-close') { pendingNotificationCallback?.(); pendingNotificationCallback = null } } @@ -708,6 +750,8 @@ watch( hushreaderCfg.value.bgOpacity, hushreaderCfg.value.prevPageKey, hushreaderCfg.value.nextPageKey, + hushreaderCfg.value.addBookmarkKey, + hushreaderCfg.value.destroyKey, hushreaderCfg.value.showHushreaderMeta, hushreaderCfg.value.progressMode, hushreaderCfg.value.hideOnMouseLeave, diff --git a/plugins/hushreader/src/components/Bookshelf/BookCard.vue b/plugins/hushreader/src/components/Bookshelf/BookCard.vue index 35869d3b..74eb45c4 100644 --- a/plugins/hushreader/src/components/Bookshelf/BookCard.vue +++ b/plugins/hushreader/src/components/Bookshelf/BookCard.vue @@ -35,7 +35,7 @@ function formatDate(ts: number) { function progressText(book: Book): string { if (!book.lastReadAt) return '' - if (book.readingPercent != null) { + if (book.readingPercent != null) { return `${book.readingPercent}%` } if (book.totalChapters && book.lastChapter != null) { @@ -47,36 +47,29 @@ function progressText(book: Book): string {