diff --git a/fix.mjs b/fix.mjs new file mode 100644 index 0000000..332174c --- /dev/null +++ b/fix.mjs @@ -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'); \ No newline at end of file diff --git a/main.js b/main.js index 5c68b8f..c9cab03 100644 --- a/main.js +++ b/main.js @@ -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); diff --git a/src/api.ts b/src/api.ts index 9d2f5c4..a4cc9bf 100644 --- a/src/api.ts +++ b/src/api.ts @@ -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; diff --git a/src/views/chat.ts b/src/views/chat.ts index a94865f..0b2c435 100644 --- a/src/views/chat.ts +++ b/src/views/chat.ts @@ -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; } -} \ No newline at end of file +} diff --git a/styles.css b/styles.css index f8fd2bc..e8a2325 100644 --- a/styles.css +++ b/styles.css @@ -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; +}