feat: 内置阅读器、批注笔记与 AI 助手,发布 1.3.0

新增 PDF/EPUB/MOBI/AZW 内置阅读器,PDF 分段读取支持超大文件,
批注、读书与画布笔记、封面生成与本地导入。AI 助手支持三种协议、
图像上下文与安全 Markdown 渲染,上下文范围改为 选中/当前页/全文,
页面与全文无需选中文本即可发送,全文会提示可能超出模型限制。

便携版输出目录固定为 PeopleLib-windows-x64,不再随版本号变化,
避免升级后 data/ 被遗留在旧目录。

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
lofyer
2026-08-03 12:13:02 +08:00
co-authored by factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
parent b8c8d24107
commit 3ccd044527
307 changed files with 98477 additions and 1148 deletions
+77
View File
@@ -0,0 +1,77 @@
const fs = require('fs');
const os = require('os');
const path = require('path');
const { app, BrowserWindow } = require('electron');
const ROOT = path.resolve(__dirname, '..', '..', '..');
const TMP = fs.mkdtempSync(path.join(os.tmpdir(), 'peoplelib-startup-ui-'));
app.setPath('appData', TMP);
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true';
const results = [];
function check(name, pass, detail) {
results.push([pass ? 'OK' : 'FAIL', name, detail || '']);
}
async function waitUntil(fn, timeout = 10000) {
const end = Date.now() + timeout;
while (Date.now() < end) {
try {
const value = await fn();
if (value) return value;
} catch (e) { /* 窗口仍在加载 */ }
await new Promise((resolve) => setTimeout(resolve, 25));
}
throw new Error(`等待条件超时(${timeout}ms`);
}
function printSummary() {
console.log('\n========== 启动响应集成验证 ==========');
for (const [status, name, detail] of results) {
console.log(`${status.padEnd(5)} ${name}${detail ? ` [${detail}]` : ''}`);
}
const failed = results.filter((item) => item[0] === 'FAIL').length;
console.log(`\n通过 ${results.length - failed}/${results.length}`);
return failed;
}
app.whenReady().then(async () => {
const library = require(path.join(ROOT, 'src', 'library', 'store'));
let scanStartedAt = 0;
library.scan = () => {
scanStartedAt = Date.now();
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 2200);
return { added: 0, missing: 0, total: 0 };
};
const startedAt = Date.now();
require(path.join(ROOT, 'main.js'));
const win = await waitUntil(() => (
BrowserWindow.getAllWindows().find((item) => item.getTitle() === 'PeopleLib')
));
win.hide();
await waitUntil(() => win.webContents.executeJavaScript(
`document.readyState === 'complete'
&& ['dark', 'light'].includes(document.documentElement.dataset.uiTheme)`
));
const configReadyMs = Date.now() - startedAt;
check('慢速书库扫描不会阻塞窗口配置加载', configReadyMs < 1200, `${configReadyMs}ms`);
await waitUntil(() => scanStartedAt > 0, 7000);
check('启动维护在首屏完成后延迟执行', scanStartedAt - startedAt >= 1400,
`${scanStartedAt - startedAt}ms`);
for (const window of BrowserWindow.getAllWindows()) {
if (!window.isDestroyed()) window.destroy();
}
const failed = printSummary();
app.exit(failed ? 1 : 0);
}).catch((error) => {
console.error('异常:', error);
check('启动验证未发生异常', false, error.message || String(error));
for (const window of BrowserWindow.getAllWindows()) {
if (!window.isDestroyed()) window.destroy();
}
printSummary();
app.exit(1);
});