From c294b68e963f64dde8cb6fd80e9f767d6a5f5d5d Mon Sep 17 00:00:00 2001 From: lofyer Date: Thu, 6 Aug 2026 18:37:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=86=E7=B1=BB=E8=AE=A1=E6=95=B0?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E6=8B=AC=E5=8F=B7=E6=A0=B7=E5=BC=8F=E5=B9=B6?= =?UTF-8?q?=E5=8F=91=E5=B8=83=20v2.1.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- package-lock.json | 4 ++-- package.json | 2 +- .../electron/library-notes.integration.js | 16 ++++++++++++---- src/_test/main.test.js | 2 +- src/_test/sources.test.js | 2 +- src/_test/ui.test.js | 18 ++++++++++++++---- src/sources/http.js | 2 +- src/ui/index.html | 4 ++-- src/ui/style.css | 15 ++++++++++++++- src/ui/views/library.js | 7 ++++--- 11 files changed, 53 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 0182565..a572509 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ DRM 保护的 MOBI/AZW/AZW3、KFX、Topaz 以及损坏或不兼容的文件不 2. 双击目录中的 `PeopleLib.exe`。 3. 保留整个程序目录,不要只移动 exe。用户数据默认保存在程序同级的 `data/`。 -当前版本为 **2.1.2**。可在「设置」中手动检查更新,也可启用启动时自动检查。检测到新版本后,应用会打开对应的 GitHub Release 下载页,更新前请退出旧版本并覆盖程序文件,`data/` 目录无需替换。 +当前版本为 **2.1.3**。可在「设置」中手动检查更新,也可启用启动时自动检查。检测到新版本后,应用会打开对应的 GitHub Release 下载页,更新前请退出旧版本并覆盖程序文件,`data/` 目录无需替换。 ## 开发 diff --git a/package-lock.json b/package-lock.json index 95541c1..d980ead 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "peoplelib", - "version": "2.1.2", + "version": "2.1.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "peoplelib", - "version": "2.1.2", + "version": "2.1.3", "license": "MIT", "dependencies": { "foliate-js": "1.0.1", diff --git a/package.json b/package.json index 5cf415c..a488a5d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "peoplelib", - "version": "2.1.2", + "version": "2.1.3", "description": "多源开放文献、电子书与本地书库客户端", "main": "main.js", "author": "peoplelib", diff --git a/src/_test/electron/library-notes.integration.js b/src/_test/electron/library-notes.integration.js index b2de48d..edc0378 100644 --- a/src/_test/electron/library-notes.integration.js +++ b/src/_test/electron/library-notes.integration.js @@ -579,10 +579,18 @@ app.whenReady().then(async () => { const shelves = Array.from(document.querySelectorAll('#libraryShelfList .library-filter')); const research = shelves.find((button) => button.textContent.includes('研究书架')); const review = shelves.find((button) => button.textContent.includes('待复核书架')); - return all.querySelector('.library-filter-count').textContent === '3' - && uncategorized.querySelector('.library-filter-count').textContent === '1' - && research.querySelector('.library-filter-count').textContent === '1' - && review.querySelector('.library-filter-count').textContent === '1'; + const actionButtons = Array.from( + document.querySelectorAll('.library-shelf-actions .notes-icon-btn') + ); + return all.textContent.trim() === '全部书籍(3)' + && uncategorized.textContent.trim() === '未分类(1)' + && research.textContent.trim() === '研究书架(1)' + && review.textContent.trim() === '待复核书架(1)' + && actionButtons.every((button) => { + const style = getComputedStyle(button); + return style.backgroundColor === 'rgba(0, 0, 0, 0)' + && style.borderTopColor === 'rgba(0, 0, 0, 0)'; + }); })()`) ); check( diff --git a/src/_test/main.test.js b/src/_test/main.test.js index 5ef3d43..9d5269f 100644 --- a/src/_test/main.test.js +++ b/src/_test/main.test.js @@ -73,7 +73,7 @@ test('下载校验协议,拒绝 file:// 等非 http(s)', () => { test('下载请求使用可识别且带项目地址的 User-Agent', () => { assert.match(mainSrc, /UA:\s*DL_UA[\s\S]+require\('\.\/src\/sources\/http'\)/); const httpSrc = fs.readFileSync(path.join(__dirname, '..', 'sources', 'http.js'), 'utf8'); - assert.match(httpSrc, /PeopleLib\/2\.1\.2 \(\+https:\/\/github\.com\/lofyer\/peoplelib\)/); + assert.match(httpSrc, /PeopleLib\/2\.1\.3 \(\+https:\/\/github\.com\/lofyer\/peoplelib\)/); }); test('移除书籍默认保留阅读资料,仅显式勾选时清理', () => { diff --git a/src/_test/sources.test.js b/src/_test/sources.test.js index 25c98a5..08733a8 100644 --- a/src/_test/sources.test.js +++ b/src/_test/sources.test.js @@ -455,7 +455,7 @@ test('wikisource: 搜索使用整数偏移并携带可识别 User-Agent', async assert.strictEqual(result.items[0].subtitle, '中文'); assert.strictEqual(result.maxPage, 500, 'MediaWiki 搜索最多允许偏移到 10000 条'); assert.ok(request.url.includes('sroffset=20'), request.url); - assert.match(request.options.headers['User-Agent'], /PeopleLib\/2\.1\.2/); + assert.match(request.options.headers['User-Agent'], /PeopleLib\/2\.1\.3/); }); test('wikisource: 浏览按 continuation 令牌翻页', async () => { diff --git a/src/_test/ui.test.js b/src/_test/ui.test.js index 9c16b2e..752f039 100644 --- a/src/_test/ui.test.js +++ b/src/_test/ui.test.js @@ -165,19 +165,20 @@ test('书库页提供可管理标签目录和整理多选下拉', () => { assert.doesNotMatch(library, /id="libraryBookTags" type="text"/); }); -test('全部书籍、未分类和每个书架都显示实时书籍数量', () => { +test('全部书籍、未分类和每个书架都以内联括号显示实时数量', () => { const html = fs.readFileSync(path.join(__dirname, '..', 'ui', 'index.html'), 'utf8'); const library = fs.readFileSync(libFile, 'utf8'); assert.match( html, - /data-shelf="">[\s\S]*全部书籍[\s\S]*class="library-filter-count">0<\/span>/ + /data-shelf="">[\s\S]*全部书籍[\s\S]*class="library-filter-count">(0)<\/span>/ ); assert.match( html, - /data-shelf="__uncategorized__">[\s\S]*未分类[\s\S]*class="library-filter-count">0<\/span>/ + /data-shelf="__uncategorized__">[\s\S]*未分类[\s\S]*class="library-filter-count">(0)<\/span>/ ); assert.match(library, /items\.filter\(\(item\) => !item\.shelfId\)\.length/); - assert.match(library, /count\.textContent = String\(shelf\.count \|\| 0\)/); + assert.match(library, /count\.textContent = `(\$\{value\})`/); + assert.match(library, /count\.textContent = `(\$\{shelf\.count \|\| 0\})`/); assert.match(library, /filter\.append\(name, count\)/); }); @@ -618,6 +619,15 @@ test('侧栏行内操作不占据布局,选中高亮与静态筛选项等宽', assert.match(actionsRule[1], /position:\s*absolute/); assert.doesNotMatch(css, /\.library-shelf-actions\s*\{\s*display:\s*flex;\s*flex:\s*none/); assert.match(css, /\.library-shelf-row \.library-filter,\s*\n\.library-tag-row \.library-filter\s*\{[^}]*padding-right:\s*48px/); + // 分类数量紧跟名称显示为「分类(数量)」,不再像标签数量那样浮到右侧 + assert.match(css, /#libraryTab \.library-filter\[data-shelf\] \.library-filter-count,\s*\n\.library-shelf-row \.library-filter-count\s*\{\s*float:\s*none/); + const actionButtons = css.match( + /\.library-shelf-actions \.notes-icon-btn,\s*\n\.library-tag-actions \.notes-icon-btn\s*\{([^}]*)\}/ + ); + assert.ok(actionButtons, '缺少侧栏操作按钮样式'); + assert.match(actionButtons[1], /background:\s*transparent/); + assert.match(actionButtons[1], /border-color:\s*transparent/); + assert.match(css, /\.library-shelf-actions \.notes-icon-btn:hover,[\s\S]*?background:\s*transparent;[\s\S]*?border-color:\s*transparent/); // 书架与标签共用同一个列表容器类,行间距不会一边有一边没有 assert.match(css, /\.library-filter-list\s*\{[^}]*gap:\s*1px/); assert.match(html, /id="libraryShelfList" class="library-filter-list"/); diff --git a/src/sources/http.js b/src/sources/http.js index d1d6331..ff7cbd6 100644 --- a/src/sources/http.js +++ b/src/sources/http.js @@ -1,4 +1,4 @@ -const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36 PeopleLib/2.1.2 (+https://github.com/lofyer/peoplelib)'; +const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36 PeopleLib/2.1.3 (+https://github.com/lofyer/peoplelib)'; const { fetch: undiciFetch, ProxyAgent } = require('undici'); // 代理配置:默认直连(空字符串)。一旦设置,所有 HTTP 请求统一走该代理。 diff --git a/src/ui/index.html b/src/ui/index.html index 48665a2..8373d81 100644 --- a/src/ui/index.html +++ b/src/ui/index.html @@ -70,10 +70,10 @@
diff --git a/src/ui/style.css b/src/ui/style.css index d2326a1..0cac1ca 100644 --- a/src/ui/style.css +++ b/src/ui/style.css @@ -727,8 +727,21 @@ body { .library-tag-row:hover .library-tag-actions, .library-tag-row:focus-within .library-tag-actions { opacity: 1; pointer-events: auto; } .library-shelf-actions .notes-icon-btn, -.library-tag-actions .notes-icon-btn { width: 22px; height: 22px; font-size: 12px; } +.library-tag-actions .notes-icon-btn { + width: 22px; + height: 22px; + background: transparent; + border-color: transparent; + font-size: 12px; +} +.library-shelf-actions .notes-icon-btn:hover, +.library-tag-actions .notes-icon-btn:hover { + background: transparent; + border-color: transparent; +} .library-filter-count { float: right; color: var(--text-dim); } +#libraryTab .library-filter[data-shelf] .library-filter-count, +.library-shelf-row .library-filter-count { float: none; } .library-content { min-width: 0; } .library-search { display: flex; diff --git a/src/ui/views/library.js b/src/ui/views/library.js index 8967616..86d2b33 100644 --- a/src/ui/views/library.js +++ b/src/ui/views/library.js @@ -433,9 +433,10 @@ const Library = (() => { ); const count = button.querySelector('.library-filter-count'); if (count) { - count.textContent = String(shelfId === '__uncategorized__' + const value = shelfId === '__uncategorized__' ? items.filter((item) => !item.shelfId).length - : items.length); + : items.length; + count.textContent = `(${value})`; } }); const shelfList = $('libraryShelfList'); @@ -452,7 +453,7 @@ const Library = (() => { name.textContent = shelf.name; const count = document.createElement('span'); count.className = 'library-filter-count'; - count.textContent = String(shelf.count || 0); + count.textContent = `(${shelf.count || 0})`; filter.append(name, count); filter.onclick = () => selectShelf(shelf.id);