feat: 工具调用折叠 + toolResult解析修复 + 样式区分

This commit is contained in:
buddybridge 2026-06-15 20:10:18 +08:00
parent 1f2344ea40
commit ac8fddda2b
5 changed files with 103 additions and 15 deletions

16
fix.mjs Normal file
View file

@ -0,0 +1,16 @@
import fs from 'fs';
var f = fs.readFileSync('H:/obsidian/obsidian/.obsidian/plugins/buddybridge-v1.0.0/main.js', 'utf8');
// Swap: create header FIRST, then list
var oldBlock = 'var tlist = toolsBlock.createDiv({ cls: "buddybridge-tools-list" });\n tlist.style.display = "none";\n var thdr = toolsBlock.createDiv({ cls: "buddybridge-tools-header" });';
var newBlock = 'var thdr = toolsBlock.createDiv({ cls: "buddybridge-tools-header" });\n thdr.style.cursor = "pointer";\n thdr.textContent = "\\u{1F527} 工具调用 \\u25B8";\n var tlist = toolsBlock.createDiv({ cls: "buddybridge-tools-list" });\n tlist.style.display = "none";';
f = f.replace(oldBlock, newBlock);
// Remove the duplicate header setup (cursor, textContent) that was after the old header creation
f = f.replace(' thdr.style.cursor = "pointer";\n thdr.textContent = "\\u{1F527} 工具调用 \\u25B8";\n thdr.addEventListener("click"', ' thdr.addEventListener("click"');
fs.writeFileSync('H:/obsidian/obsidian/.obsidian/plugins/buddybridge-v1.0.0/main.js', f);
fs.writeFileSync('H:/Dev/claude/buddybridge/main.js', f);
console.log('done');

38
main.js
View file

@ -199,7 +199,7 @@ function parseStreamLine(line) {
try {
const obj = JSON.parse(line);
const event = obj.event || obj;
if (obj.type === "assistant") {
if (obj.type === "assistant" || obj.type === "user") {
const msg = obj.message;
if (!msg || !Array.isArray(msg.content))
return null;
@ -210,7 +210,16 @@ function parseStreamLine(line) {
if (block.type === "text") {
return { type: "text", content: block.text || "" };
}
if (block.type === "tool_call") {
if (block.type === "tool_result") {
var rt = "";
if (Array.isArray(block.content)) {
for (var ci = 0; ci < block.content.length; ci++) {
if (block.content[ci].type === "text") rt += (block.content[ci].text || "");
}
}
return { type: "tool", content: "", toolName: "结果", toolDetail: rt.substring(0, 200) + (rt.length > 200 ? "..." : "") };
}
if (block.type === "tool_call") {
return {
type: "tool",
content: "",
@ -705,10 +714,27 @@ ${text}` : text;
}
this.scrollToBottom();
} else if (chunk.type === "tool") {
bubble.createDiv({
cls: "buddybridge-tool-call",
text: `\u{1F527} ${chunk.toolName || ""} ${chunk.toolDetail || ""}`
});
let toolsBlock = bubble.querySelector(".buddybridge-tools-block");
if (!toolsBlock) {
toolsBlock = bubble.createDiv({ cls: "buddybridge-tools-block" });
var thdr = toolsBlock.createDiv({ cls: "buddybridge-tools-header" });
thdr.style.cursor = "pointer";
thdr.textContent = "\u{1F527} 工具调用 \u25B8";
var tlist = toolsBlock.createDiv({ cls: "buddybridge-tools-list" });
tlist.style.display = "none";
thdr.addEventListener("click", function() {
var hidden = tlist.style.display === "none";
tlist.style.display = hidden ? "" : "none";
thdr.textContent = hidden ? "\u{1F527} 工具调用 \u25BE" : "\u{1F527} 工具调用 \u25B8";
});
}
var list = toolsBlock.querySelector(".buddybridge-tools-list");
if (list) {
list.createDiv({
cls: "buddybridge-tool-call",
text: chunk.toolName ? chunk.toolName + " " + (chunk.toolDetail || "") : (chunk.toolDetail || "")
});
}
} else if (chunk.type === "text") {
textContent += chunk.content;
this.manager.updateMessage(convId, aiMsg.id, textContent);

View file

@ -181,7 +181,7 @@ function parseStreamLine(line: string): StreamChunk | null {
const obj = JSON.parse(line);
const event = obj.event || obj;
if (obj.type === 'assistant') {
if (obj.type === 'assistant' || obj.type === 'user') {
const msg = obj.message;
if (!msg || !Array.isArray(msg.content)) return null;

View file

@ -251,11 +251,28 @@ ${text}`
body.setText(thinkingContent);
}
} else if (chunk.type === 'tool') {
// 工具调用行
bubble.createDiv({
cls: 'buddybridge-tool-call',
text: `🔧 ${chunk.toolName || ''} ${chunk.toolDetail || ''}`
});
let toolsBlock = bubble.querySelector('.buddybridge-tools-block') as HTMLElement;
if (!toolsBlock) {
toolsBlock = bubble.createDiv({ cls: 'buddybridge-tools-block' });
const hdr = toolsBlock.createDiv({ cls: 'buddybridge-tools-header', text: '🔧 工具调用 ▾' });
hdr.style.cursor = 'pointer';
hdr.addEventListener('click', () => {
const list = toolsBlock.querySelector('.buddybridge-tools-list') as HTMLElement;
if (list) {
const hidden = list.style.display === 'none';
list.style.display = hidden ? '' : 'none';
hdr.textContent = hidden ? '🔧 工具调用 ▾' : '🔧 工具调用 ▸';
}
});
toolsBlock.createDiv({ cls: 'buddybridge-tools-list' });
}
const list = toolsBlock.querySelector('.buddybridge-tools-list') as HTMLElement;
if (list) {
list.createDiv({
cls: 'buddybridge-tool-call',
text: `${chunk.toolName || ''} ${chunk.toolDetail || ''}`
});
}
} else if (chunk.type === 'text') {
textContent += chunk.content;
this.manager.updateMessage(convId, aiMsg.id, textContent, true);
@ -284,12 +301,10 @@ ${text}`
} finally {
this.isStreaming = false;
this.streamingMsgId = null;
// 不在 finally 中 renderMessages(), 避免清除思考块
// 仅在出错时重新渲染
}
}
private scrollToBottom() {
this.messageContainer.scrollTop = this.messageContainer.scrollHeight;
}
}
}

View file

@ -314,3 +314,34 @@
/* 不可见辅助类 */
.buddybridge-hidden { display: none !important; }
/* ==================== 工具调用块 ==================== */
.buddybridge-tools-block {
margin: 4px 0;
border: 1px solid var(--background-modifier-border);
border-radius: 6px;
overflow: hidden;
}
.buddybridge-tools-header {
padding: 4px 8px;
font-size: 11px;
color: var(--text-faint);
background: var(--background-primary);
border-bottom: 1px solid var(--background-modifier-border);
cursor: pointer;
user-select: none;
}
.buddybridge-tools-list {
padding: 4px 0;
}
.buddybridge-tools-list .buddybridge-tool-call {
padding: 2px 8px;
margin: 1px 0;
font-size: 11px;
color: var(--text-faint);
border-left: 2px solid var(--text-faint);
border-radius: 0 4px 4px 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}