feat: 笔记独立窗口改为多标签,AI 多轮会话与 TXT/MD 阅读
笔记独立窗口从「一窗一条」改为单窗口多标签,与阅读器一致: 标签集在主进程侧为权威,notes:tabsChanged 只能收窄不能新增, 否则渲染层可以谎报持有某条笔记来越权读取。存活编辑器上限 3 并 LRU 回收,回收前序列化未保存内容。笔记没有自动保存,关标签与 关窗都做二次确认,取消关闭必须回报主进程复位 closePending, 否则窗口再也关不掉而看门狗仍会销毁未保存内容。 AI 助手支持多轮会话:会话独立落盘,先取历史再写提问, 历史只发文本不重发图像,失败与取消都保留已流出的残片。 新增 TXT/MD 内置阅读(转内存 EPUB 复用 epub 渲染管线), 补上渲染层遗漏的可阅读格式白名单:主进程本就放行 txt/md, 但渲染层另有两份白名单漏了,表现为卡片上没有「阅读」按钮。 书库卡片封面改用 contain 完整显示,留白由同图模糊层垫底, 修正不同比例封面被裁切程度不一导致的观感不一致;多选复选框 去掉衬底色块,恢复原生外观。 其余:PDF 画质档位与画布尺寸钳制、原子写入、笔记资源托管、 GitHub Pages 站点。
This commit is contained in:
@@ -93,6 +93,34 @@ test('不同条目的阅读数据互相隔离', () => {
|
||||
assert.strictEqual(s.getState('b').bookmarks.length, 1, 'forget 误删了其它条目');
|
||||
});
|
||||
|
||||
test('孤立阅读资料按笔记数报告并可批量回收', () => {
|
||||
const s = freshStore();
|
||||
s.setProgress('kept', { kind: 'pdf', page: 2 }, 0.5);
|
||||
s.addNote('kept', { text: '保留的笔记' });
|
||||
s.addNote('gone_a', { text: '甲一' });
|
||||
s.addNote('gone_a', { text: '甲二' });
|
||||
s.addBookmark('gone_b', { locator: { kind: 'pdf', page: 3 } });
|
||||
s.addStandaloneNote({ text: '与书籍无关的独立笔记' });
|
||||
|
||||
const orphans = s.orphanReport(['kept']);
|
||||
assert.deepStrictEqual(orphans.map((o) => o.entryId).sort(), ['gone_a', 'gone_b']);
|
||||
assert.strictEqual(orphans.find((o) => o.entryId === 'gone_a').notes, 2);
|
||||
assert.strictEqual(orphans.find((o) => o.entryId === 'gone_b').bookmarks, 1);
|
||||
// 独立笔记本没有对应书籍,永远不算孤立
|
||||
assert.ok(!orphans.some((o) => o.entryId === s.STANDALONE_ENTRY_ID));
|
||||
|
||||
assert.strictEqual(s.forgetMany(orphans.map((o) => o.entryId)), 2);
|
||||
assert.strictEqual(s.getState('kept').notes.length, 1);
|
||||
assert.strictEqual(s.getState('kept').progress.percent, 0.5);
|
||||
assert.ok(s.listNotes({}).some((n) => n.text === '与书籍无关的独立笔记'));
|
||||
assert.deepStrictEqual(s.orphanReport(['kept']), []);
|
||||
assert.strictEqual(s.forgetMany([]), 0);
|
||||
|
||||
// 即使显式点名,也不能删掉独立笔记本:它没有对应书籍,永远不是可回收对象
|
||||
assert.strictEqual(s.forgetMany([s.STANDALONE_ENTRY_ID]), 0);
|
||||
assert.ok(s.listNotes({}).some((n) => n.text === '与书籍无关的独立笔记'));
|
||||
});
|
||||
|
||||
test('getState 返回副本,外部改动不污染存储', () => {
|
||||
const s = freshStore();
|
||||
s.addBookmark('e1', { locator: { kind: 'pdf', page: 1 } });
|
||||
|
||||
Reference in New Issue
Block a user