Skip to content

Commit e092f1e

Browse files
Merge pull request #318 from steven-jianhao-li/dev
Dev
2 parents 3e60b72 + 8bd2990 commit e092f1e

9 files changed

Lines changed: 411 additions & 48 deletions

File tree

addon/content/outputWindow.css

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,172 @@
7171
overflow-y: hidden;
7272
}
7373

74+
.ai-butler-compact #ai-butler-summary-view > div:first-child {
75+
padding: 14px 16px 0 !important;
76+
}
77+
78+
.ai-butler-compact #ai-butler-summary-view > div:first-child h2 {
79+
margin-bottom: 12px !important;
80+
padding-bottom: 8px !important;
81+
font-size: 18px !important;
82+
}
83+
84+
.ai-butler-compact #ai-butler-summary-view #ai-butler-chat-container {
85+
padding: 10px 16px !important;
86+
}
87+
88+
.ai-butler-compact #ai-butler-summary-view #ai-butler-chat-toggle-button {
89+
margin-bottom: 8px !important;
90+
}
91+
92+
.ai-butler-compact #ai-butler-summary-view #ai-butler-chat-input-area {
93+
gap: 8px !important;
94+
}
95+
96+
.ai-butler-compact #ai-butler-summary-view #ai-butler-chat-input {
97+
min-height: 64px !important;
98+
padding: 8px !important;
99+
}
100+
101+
.ai-butler-compact #ai-butler-summary-view > div:last-child {
102+
padding: 10px 16px 12px !important;
103+
}
104+
105+
.ai-butler-compact #ai-butler-dashboard-view > div:first-child,
106+
.ai-butler-compact
107+
#ai-butler-task-queue-view
108+
#task-header-wrapper
109+
> div:first-child {
110+
padding: 14px 20px 0 !important;
111+
}
112+
113+
.ai-butler-compact #ai-butler-dashboard-view > div:first-child h2,
114+
.ai-butler-compact #ai-butler-task-queue-view #task-header-wrapper h2 {
115+
margin-bottom: 10px !important;
116+
padding-bottom: 7px !important;
117+
font-size: 18px !important;
118+
}
119+
120+
.ai-butler-compact #ai-butler-dashboard-view #butler-status-card {
121+
margin: 0 20px 12px !important;
122+
padding: 18px 24px !important;
123+
border-radius: 8px !important;
124+
}
125+
126+
.ai-butler-compact #ai-butler-dashboard-view #status-icon {
127+
font-size: 34px !important;
128+
margin-bottom: 8px !important;
129+
}
130+
131+
.ai-butler-compact #ai-butler-dashboard-view #status-text {
132+
font-size: 20px !important;
133+
margin-bottom: 6px !important;
134+
}
135+
136+
.ai-butler-compact #ai-butler-dashboard-view #status-detail {
137+
font-size: 13px !important;
138+
}
139+
140+
.ai-butler-compact #ai-butler-dashboard-view #stats-section,
141+
.ai-butler-compact #ai-butler-task-queue-view #stats-section {
142+
grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)) !important;
143+
gap: 8px !important;
144+
padding: 0 20px 10px !important;
145+
}
146+
147+
.ai-butler-compact #ai-butler-dashboard-view .stat-card,
148+
.ai-butler-compact #ai-butler-task-queue-view .stat-card {
149+
min-height: 0 !important;
150+
padding: 10px 12px !important;
151+
border-radius: 6px !important;
152+
}
153+
154+
.ai-butler-compact #ai-butler-dashboard-view .stat-card .ai-card__header,
155+
.ai-butler-compact #ai-butler-task-queue-view .stat-card .ai-card__header {
156+
margin-bottom: 4px !important;
157+
}
158+
159+
.ai-butler-compact #ai-butler-dashboard-view .stat-card .ai-card__title,
160+
.ai-butler-compact #ai-butler-task-queue-view .stat-card .ai-card__title {
161+
font-size: 11px !important;
162+
}
163+
164+
.ai-butler-compact #ai-butler-dashboard-view .stat-card .ai-card__value,
165+
.ai-butler-compact #ai-butler-task-queue-view .stat-card .ai-card__value {
166+
font-size: 24px !important;
167+
line-height: 1.05 !important;
168+
}
169+
170+
.ai-butler-compact #ai-butler-dashboard-view .stat-card .ai-card__icon {
171+
font-size: 18px !important;
172+
}
173+
174+
.ai-butler-compact
175+
#ai-butler-task-queue-view
176+
#task-header-wrapper
177+
> div:last-child {
178+
gap: 6px !important;
179+
padding: 0 20px 8px !important;
180+
}
181+
182+
.ai-butler-compact #ai-butler-task-queue-view #task-header-wrapper button,
183+
.ai-butler-compact #ai-butler-task-queue-view #task-header-wrapper input {
184+
min-height: 0 !important;
185+
padding: 5px 10px !important;
186+
font-size: 12px !important;
187+
line-height: 1.15 !important;
188+
}
189+
190+
.ai-butler-compact #ai-butler-task-queue-view #task-header-wrapper input {
191+
min-width: 150px !important;
192+
}
193+
194+
.ai-butler-compact #ai-butler-task-queue-view #task-header-wrapper span {
195+
margin-left: 2px !important;
196+
margin-right: 2px !important;
197+
}
198+
199+
.ai-butler-compact #ai-butler-task-queue-view #task-list-container {
200+
padding: 0 20px 12px !important;
201+
}
202+
203+
.ai-butler-compact #ai-butler-task-queue-view .task-item {
204+
padding: 12px !important;
205+
margin-bottom: 8px !important;
206+
}
207+
208+
.ai-butler-compact #ai-butler-task-queue-view .task-item .ai-card__header {
209+
margin-bottom: 4px !important;
210+
}
211+
212+
.ai-butler-compact #ai-butler-task-queue-view .task-item .ai-card__title {
213+
font-size: 13px !important;
214+
}
215+
216+
.ai-butler-compact #ai-butler-dashboard-view > div:nth-child(4),
217+
.ai-butler-compact #ai-butler-dashboard-view > div:nth-child(5) {
218+
padding: 0 20px 12px !important;
219+
}
220+
221+
.ai-butler-compact #ai-butler-dashboard-view h3 {
222+
margin-bottom: 8px !important;
223+
font-size: 15px !important;
224+
}
225+
226+
.ai-butler-compact #ai-butler-dashboard-view button {
227+
padding: 10px 12px !important;
228+
gap: 6px !important;
229+
}
230+
231+
.ai-butler-compact #ai-butler-dashboard-view button span {
232+
font-size: 16px !important;
233+
}
234+
235+
.ai-butler-compact #ai-butler-dashboard-view #activity-list {
236+
padding: 10px !important;
237+
max-height: 220px !important;
238+
}
239+
74240
#ai-butler-stop-button {
75241
font-size: 16px !important;
76242
font-weight: 700 !important;

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "zotero-ai-butler",
33
"type": "module",
4-
"version": "3.6.1-beta.2",
4+
"version": "3.6.1-beta.3",
55
"description": "Your personal AI butler, automatically and meticulously reads papers and summarizes notes.",
66
"config": {
77
"addonName": "zotero-ai-butler",

src/modules/ItemPaneSection.ts

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ import {
2222
LLMNoteMetadataService,
2323
type LLMNoteMetadata,
2424
} from "./llmNoteMetadata";
25-
import { markdownToZoteroNoteHtml } from "./noteMarkdown";
25+
import {
26+
buildFollowUpChatPairNoteHtml,
27+
normalizeFollowUpChatNoteHtml,
28+
} from "./noteMarkdown";
2629
import katex from "katex";
2730
// 注意: 不在主进程中直接 import 思维导图库(如 markmap-view、simple-mind-map)
2831
// 这些库在加载时会访问 document/window,而 Zotero Background 进程没有 DOM 环境
@@ -2725,26 +2728,25 @@ async function saveChatPairToNote(
27252728
): Promise<void> {
27262729
const note = await getOrCreateChatNote(item);
27272730
let noteHtml = (note as any).getNote?.() || "";
2731+
const normalizedNoteHtml = normalizeFollowUpChatNoteHtml(noteHtml);
27282732

27292733
// 检查是否已存在相同 pairId 的对话对,防止重复保存
2730-
if (noteHtml.includes(`AI_BUTLER_CHAT_PAIR_START id=${pairId}`)) {
2734+
if (normalizedNoteHtml.includes(`AI_BUTLER_CHAT_PAIR_START id=${pairId}`)) {
2735+
if (normalizedNoteHtml !== noteHtml) {
2736+
(note as any).setNote(normalizedNoteHtml);
2737+
await (note as any).saveTx();
2738+
}
27312739
ztoolkit.log("[AI-Butler] 该对话对已保存过,跳过重复保存");
27322740
return;
27332741
}
2742+
noteHtml = normalizedNoteHtml;
27342743

2735-
const renderedUserMessage = markdownToZoteroNoteHtml(userMessage);
2736-
const renderedAssistantMessage = markdownToZoteroNoteHtml(assistantMessage);
2737-
const jsonMarker = `<!-- AI_BUTLER_CHAT_JSON: ${JSON.stringify({ id: pairId, user: userMessage, assistant: assistantMessage })} -->`;
2738-
const blockContent = `
2739-
<!-- AI_BUTLER_CHAT_PAIR_START id=${escapeHtmlForNote(pairId)} -->
2740-
${jsonMarker}
2741-
<div id="ai-butler-pair-${escapeHtmlForNote(pairId)}" style="margin-top:14px; padding-top:8px; border-top:1px dashed #ccc;">
2742-
<div style="background-color:#e3f2fd; padding:10px; border-radius:6px; margin-bottom:8px;"><strong>👤 用户:</strong><div>${renderedUserMessage}</div></div>
2743-
<div style="background-color:#f5f5f5; padding:10px; border-radius:6px;"><strong>🤖 AI管家:</strong><div>${renderedAssistantMessage}</div></div>
2744-
<div style="font-size:11px; color:#999; margin-top:6px;">保存时间: ${new Date().toLocaleString("zh-CN")} (来自快速提问)</div>
2745-
</div>
2746-
<!-- AI_BUTLER_CHAT_PAIR_END id=${escapeHtmlForNote(pairId)} -->
2747-
`;
2744+
const blockContent = buildFollowUpChatPairNoteHtml({
2745+
pairId,
2746+
userMessage,
2747+
assistantMessage,
2748+
sourceLabel: "来自快速提问",
2749+
});
27482750
const block = metadata
27492751
? LLMNoteMetadataService.wrapHtml(blockContent, metadata)
27502752
: blockContent;

src/modules/noteMarkdown.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,23 @@ type ProtectedFormula = {
55
isBlock: boolean;
66
};
77

8+
export type FollowUpChatPairNoteHtmlOptions = {
9+
pairId: string;
10+
userMessage: string;
11+
assistantMessage: string;
12+
savedAt?: Date | string;
13+
sourceLabel?: string;
14+
};
15+
16+
const FOLLOW_UP_CHAT_PAIR_STYLE =
17+
"margin-top:14px; padding-top:8px; border-top:1px dashed #8a8a8a;";
18+
const FOLLOW_UP_CHAT_USER_STYLE =
19+
"padding:10px; border-left:3px solid #4f8fd9; border-radius:4px; margin-bottom:8px; color:inherit; background:transparent;";
20+
const FOLLOW_UP_CHAT_ASSISTANT_STYLE =
21+
"padding:10px; border-left:3px solid #59c0bc; border-radius:4px; color:inherit; background:transparent;";
22+
const FOLLOW_UP_CHAT_TIME_STYLE =
23+
"font-size:11px; color:inherit; opacity:0.65; margin-top:6px;";
24+
825
export function escapeHtml(text: string): string {
926
return text
1027
.replace(/&/g, "&amp;")
@@ -69,3 +86,56 @@ export function markdownToZoteroNoteHtml(markdown: string): string {
6986
},
7087
);
7188
}
89+
90+
export function buildFollowUpChatPairNoteHtml(
91+
options: FollowUpChatPairNoteHtmlOptions,
92+
): string {
93+
const renderedUserMessage = markdownToZoteroNoteHtml(options.userMessage);
94+
const renderedAssistantMessage = markdownToZoteroNoteHtml(
95+
options.assistantMessage,
96+
);
97+
const pairId = escapeHtml(options.pairId);
98+
const savedAt =
99+
typeof options.savedAt === "string"
100+
? options.savedAt
101+
: (options.savedAt ?? new Date()).toLocaleString("zh-CN");
102+
const sourceSuffix = options.sourceLabel
103+
? ` (${escapeHtml(options.sourceLabel)})`
104+
: "";
105+
const jsonMarker = `<!-- AI_BUTLER_CHAT_JSON: ${JSON.stringify({
106+
id: options.pairId,
107+
user: options.userMessage,
108+
assistant: options.assistantMessage,
109+
})} -->`;
110+
111+
return `
112+
<!-- AI_BUTLER_CHAT_PAIR_START id=${pairId} -->
113+
${jsonMarker}
114+
<div id="ai-butler-pair-${pairId}" style="${FOLLOW_UP_CHAT_PAIR_STYLE}">
115+
<div style="${FOLLOW_UP_CHAT_USER_STYLE}"><strong>👤 用户:</strong><div>${renderedUserMessage}</div></div>
116+
<div style="${FOLLOW_UP_CHAT_ASSISTANT_STYLE}"><strong>🤖 AI管家:</strong><div>${renderedAssistantMessage}</div></div>
117+
<div style="${FOLLOW_UP_CHAT_TIME_STYLE}">保存时间: ${escapeHtml(savedAt)}${sourceSuffix}</div>
118+
</div>
119+
<!-- AI_BUTLER_CHAT_PAIR_END id=${pairId} -->
120+
`;
121+
}
122+
123+
export function normalizeFollowUpChatNoteHtml(html: string): string {
124+
return html
125+
.replace(
126+
/style="background-color:\s*#e3f2fd;\s*padding:\s*10px;\s*border-radius:\s*6px;\s*margin-bottom:\s*8px;?"/gi,
127+
`style="${FOLLOW_UP_CHAT_USER_STYLE}"`,
128+
)
129+
.replace(
130+
/style="background-color:\s*#f5f5f5;\s*padding:\s*10px;\s*border-radius:\s*6px;?"/gi,
131+
`style="${FOLLOW_UP_CHAT_ASSISTANT_STYLE}"`,
132+
)
133+
.replace(
134+
/style="font-size:\s*11px;\s*color:\s*#999;\s*margin-top:\s*6px;?"/gi,
135+
`style="${FOLLOW_UP_CHAT_TIME_STYLE}"`,
136+
)
137+
.replace(
138+
/style="margin-top:\s*14px;\s*padding-top:\s*8px;\s*border-top:\s*1px\s+dashed\s+#ccc;?"/gi,
139+
`style="${FOLLOW_UP_CHAT_PAIR_STYLE}"`,
140+
);
141+
}

0 commit comments

Comments
 (0)