feat: AI 会话支持按轮保存完整笔记

This commit is contained in:
lofyer
2026-08-04 20:50:31 +08:00
parent 0fd7c59e08
commit f72c26642f
9 changed files with 471 additions and 33 deletions
@@ -377,6 +377,86 @@ app.whenReady().then(async () => {
)),
JSON.stringify(storedMessages.map((m) => m.images.map((i) => i.imageId.slice(0, 12)))));
async function waitForNoteCount(count) {
const deadline = Date.now() + 5000;
let notes = [];
while (Date.now() < deadline) {
notes = readerStore.listNotes({ entryId: e.id });
if (notes.length >= count) return notes;
await new Promise((r) => setTimeout(r, 100));
}
return notes;
}
const waitForSaveModal = () => js(`new Promise((resolve) => {
const deadline = Date.now() + 3000;
const check = () => {
if (!document.getElementById('aiSessionSaveModal').classList.contains('hidden')) {
resolve(true);
} else if (Date.now() >= deadline) {
resolve(false);
} else {
setTimeout(check, 50);
}
};
check();
})`);
const notesBeforeSave = readerStore.listNotes({ entryId: e.id });
const noteIdsBeforeSave = new Set(notesBeforeSave.map((note) => note.id));
await js("document.getElementById('aiSaveBtn').click()");
await waitForSaveModal();
await js(`(() => {
const recent = document.getElementById('aiSessionSaveRecent');
const rounds = document.getElementById('aiSessionSaveRounds');
recent.click();
rounds.value = '2';
rounds.dispatchEvent(new Event('input', { bubbles: true }));
document.getElementById('aiSessionSaveConfirmBtn').click();
})()`);
const notesAfterRecent = await waitForNoteCount(notesBeforeSave.length + 1);
const recentNote = notesAfterRecent.find((note) => !noteIdsBeforeSave.has(note.id));
const recentText = String(recentNote?.text || '');
const recentSecondQuestion = recentText.indexOf('这张页面图像讲了什么');
const recentSecondAnswer = recentText.indexOf(AI_MARKDOWN, recentSecondQuestion);
const recentThirdQuestion = recentText.indexOf('这个框选区域是什么', recentSecondAnswer);
const recentThirdAnswer = recentText.indexOf(AI_MARKDOWN, recentThirdQuestion);
chk('保存最近两轮只新增一条 AI 会话笔记',
notesAfterRecent.length === notesBeforeSave.length + 1
&& recentNote?.source === 'ai'
&& recentNote?.title === storedList[0].title,
`新增=${notesAfterRecent.length - notesBeforeSave.length} 来源=${recentNote?.source} 标题=${recentNote?.title}`);
chk('最近两轮笔记正文排除第一轮并保持问答顺序',
!recentText.includes('这章讲了什么')
&& recentSecondQuestion >= 0
&& recentSecondAnswer > recentSecondQuestion
&& recentThirdQuestion > recentSecondAnswer
&& recentThirdAnswer > recentThirdQuestion,
recentText.slice(0, 160));
const recentPreference = settings.get('reader.aiSessionSave', null);
chk('保存最近两轮偏好已持久化',
recentPreference?.mode === 'recent' && recentPreference?.rounds === 2,
JSON.stringify(recentPreference));
const recentNoteIds = new Set(notesAfterRecent.map((note) => note.id));
await js("document.getElementById('aiSaveBtn').click()");
await waitForSaveModal();
await js(`(() => {
document.getElementById('aiSessionSaveAll').click();
document.getElementById('aiSessionSaveConfirmBtn').click();
})()`);
const notesAfterAll = await waitForNoteCount(notesAfterRecent.length + 1);
const allNote = notesAfterAll.find((note) => !recentNoteIds.has(note.id));
const allText = String(allNote?.text || '');
chk('保存全部三轮再次只新增一条会话笔记',
notesAfterAll.length === notesAfterRecent.length + 1
&& allNote?.source === 'ai'
&& allNote?.title === storedList[0].title
&& allText.includes('这章讲了什么')
&& allText.includes('这张页面图像讲了什么')
&& allText.includes('这个框选区域是什么'),
`新增=${notesAfterAll.length - notesAfterRecent.length} 标题=${allNote?.title}`);
const reopened = new BrowserWindow({
show: false, width: 1200, height: 860,
webPreferences: { preload: path.join(ROOT, 'preload.js'), contextIsolation: true, nodeIntegration: false }