feat: 分类计数改为括号样式并发布 v2.1.3
构建与发布 / 单测与集成测试 (push) Waiting to run
构建与发布 / 打包 ${{ matrix.platform }} ${{ matrix.arch }} (arm64, linux, ubuntu-24.04-arm) (push) Blocked by required conditions
构建与发布 / 打包 ${{ matrix.platform }} ${{ matrix.arch }} (arm64, macos, macos-15) (push) Blocked by required conditions
构建与发布 / 打包 ${{ matrix.platform }} ${{ matrix.arch }} (x64, linux, ubuntu-24.04) (push) Blocked by required conditions
构建与发布 / 打包 ${{ matrix.platform }} ${{ matrix.arch }} (x64, windows, windows-2025) (push) Blocked by required conditions
构建与发布 / 发布 GitHub Release (push) Blocked by required conditions

This commit is contained in:
lofyer
2026-08-06 18:37:50 +08:00
parent d6078098d6
commit c294b68e96
11 changed files with 53 additions and 21 deletions
+1 -1
View File
@@ -74,7 +74,7 @@ DRM 保护的 MOBI/AZW/AZW3、KFX、Topaz 以及损坏或不兼容的文件不
2. 双击目录中的 `PeopleLib.exe` 2. 双击目录中的 `PeopleLib.exe`
3. 保留整个程序目录,不要只移动 exe。用户数据默认保存在程序同级的 `data/` 3. 保留整个程序目录,不要只移动 exe。用户数据默认保存在程序同级的 `data/`
当前版本为 **2.1.2**。可在「设置」中手动检查更新,也可启用启动时自动检查。检测到新版本后,应用会打开对应的 GitHub Release 下载页,更新前请退出旧版本并覆盖程序文件,`data/` 目录无需替换。 当前版本为 **2.1.3**。可在「设置」中手动检查更新,也可启用启动时自动检查。检测到新版本后,应用会打开对应的 GitHub Release 下载页,更新前请退出旧版本并覆盖程序文件,`data/` 目录无需替换。
## 开发 ## 开发
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "peoplelib", "name": "peoplelib",
"version": "2.1.2", "version": "2.1.3",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "peoplelib", "name": "peoplelib",
"version": "2.1.2", "version": "2.1.3",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"foliate-js": "1.0.1", "foliate-js": "1.0.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "peoplelib", "name": "peoplelib",
"version": "2.1.2", "version": "2.1.3",
"description": "多源开放文献、电子书与本地书库客户端", "description": "多源开放文献、电子书与本地书库客户端",
"main": "main.js", "main": "main.js",
"author": "peoplelib", "author": "peoplelib",
@@ -579,10 +579,18 @@ app.whenReady().then(async () => {
const shelves = Array.from(document.querySelectorAll('#libraryShelfList .library-filter')); const shelves = Array.from(document.querySelectorAll('#libraryShelfList .library-filter'));
const research = shelves.find((button) => button.textContent.includes('研究书架')); const research = shelves.find((button) => button.textContent.includes('研究书架'));
const review = shelves.find((button) => button.textContent.includes('待复核书架')); const review = shelves.find((button) => button.textContent.includes('待复核书架'));
return all.querySelector('.library-filter-count').textContent === '3' const actionButtons = Array.from(
&& uncategorized.querySelector('.library-filter-count').textContent === '1' document.querySelectorAll('.library-shelf-actions .notes-icon-btn')
&& research.querySelector('.library-filter-count').textContent === '1' );
&& review.querySelector('.library-filter-count').textContent === '1'; 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( check(
+1 -1
View File
@@ -73,7 +73,7 @@ test('下载校验协议,拒绝 file:// 等非 http(s)', () => {
test('下载请求使用可识别且带项目地址的 User-Agent', () => { test('下载请求使用可识别且带项目地址的 User-Agent', () => {
assert.match(mainSrc, /UA:\s*DL_UA[\s\S]+require\('\.\/src\/sources\/http'\)/); assert.match(mainSrc, /UA:\s*DL_UA[\s\S]+require\('\.\/src\/sources\/http'\)/);
const httpSrc = fs.readFileSync(path.join(__dirname, '..', 'sources', 'http.js'), 'utf8'); 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('移除书籍默认保留阅读资料,仅显式勾选时清理', () => { test('移除书籍默认保留阅读资料,仅显式勾选时清理', () => {
+1 -1
View File
@@ -455,7 +455,7 @@ test('wikisource: 搜索使用整数偏移并携带可识别 User-Agent', async
assert.strictEqual(result.items[0].subtitle, '中文'); assert.strictEqual(result.items[0].subtitle, '中文');
assert.strictEqual(result.maxPage, 500, 'MediaWiki 搜索最多允许偏移到 10000 条'); assert.strictEqual(result.maxPage, 500, 'MediaWiki 搜索最多允许偏移到 10000 条');
assert.ok(request.url.includes('sroffset=20'), request.url); 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 () => { test('wikisource: 浏览按 continuation 令牌翻页', async () => {
+14 -4
View File
@@ -165,19 +165,20 @@ test('书库页提供可管理标签目录和整理多选下拉', () => {
assert.doesNotMatch(library, /id="libraryBookTags" type="text"/); assert.doesNotMatch(library, /id="libraryBookTags" type="text"/);
}); });
test('全部书籍、未分类和每个书架都显示实时书籍数量', () => { test('全部书籍、未分类和每个书架都以内联括号显示实时数量', () => {
const html = fs.readFileSync(path.join(__dirname, '..', 'ui', 'index.html'), 'utf8'); const html = fs.readFileSync(path.join(__dirname, '..', 'ui', 'index.html'), 'utf8');
const library = fs.readFileSync(libFile, 'utf8'); const library = fs.readFileSync(libFile, 'utf8');
assert.match( assert.match(
html, 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( assert.match(
html, 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, /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\)/); assert.match(library, /filter\.append\(name, count\)/);
}); });
@@ -618,6 +619,15 @@ test('侧栏行内操作不占据布局,选中高亮与静态筛选项等宽',
assert.match(actionsRule[1], /position:\s*absolute/); assert.match(actionsRule[1], /position:\s*absolute/);
assert.doesNotMatch(css, /\.library-shelf-actions\s*\{\s*display:\s*flex;\s*flex:\s*none/); 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, /\.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(css, /\.library-filter-list\s*\{[^}]*gap:\s*1px/);
assert.match(html, /id="libraryShelfList" class="library-filter-list"/); assert.match(html, /id="libraryShelfList" class="library-filter-list"/);
+1 -1
View File
@@ -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'); const { fetch: undiciFetch, ProxyAgent } = require('undici');
// 代理配置:默认直连(空字符串)。一旦设置,所有 HTTP 请求统一走该代理。 // 代理配置:默认直连(空字符串)。一旦设置,所有 HTTP 请求统一走该代理。
+2 -2
View File
@@ -70,10 +70,10 @@
</div> </div>
<div class="library-filter-list"> <div class="library-filter-list">
<button class="library-filter active" data-shelf=""> <button class="library-filter active" data-shelf="">
<span>全部书籍</span><span class="library-filter-count">0</span> <span>全部书籍</span><span class="library-filter-count">0</span>
</button> </button>
<button class="library-filter" data-shelf="__uncategorized__"> <button class="library-filter" data-shelf="__uncategorized__">
<span>未分类</span><span class="library-filter-count">0</span> <span>未分类</span><span class="library-filter-count">0</span>
</button> </button>
<div id="libraryShelfList" class="library-filter-list"></div> <div id="libraryShelfList" class="library-filter-list"></div>
</div> </div>
+14 -1
View File
@@ -727,8 +727,21 @@ body {
.library-tag-row:hover .library-tag-actions, .library-tag-row:hover .library-tag-actions,
.library-tag-row:focus-within .library-tag-actions { opacity: 1; pointer-events: auto; } .library-tag-row:focus-within .library-tag-actions { opacity: 1; pointer-events: auto; }
.library-shelf-actions .notes-icon-btn, .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); } .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-content { min-width: 0; }
.library-search { .library-search {
display: flex; display: flex;
+4 -3
View File
@@ -433,9 +433,10 @@ const Library = (() => {
); );
const count = button.querySelector('.library-filter-count'); const count = button.querySelector('.library-filter-count');
if (count) { if (count) {
count.textContent = String(shelfId === '__uncategorized__' const value = shelfId === '__uncategorized__'
? items.filter((item) => !item.shelfId).length ? items.filter((item) => !item.shelfId).length
: items.length); : items.length;
count.textContent = `${value}`;
} }
}); });
const shelfList = $('libraryShelfList'); const shelfList = $('libraryShelfList');
@@ -452,7 +453,7 @@ const Library = (() => {
name.textContent = shelf.name; name.textContent = shelf.name;
const count = document.createElement('span'); const count = document.createElement('span');
count.className = 'library-filter-count'; count.className = 'library-filter-count';
count.textContent = String(shelf.count || 0); count.textContent = `${shelf.count || 0}`;
filter.append(name, count); filter.append(name, count);
filter.onclick = () => selectShelf(shelf.id); filter.onclick = () => selectShelf(shelf.id);