|
120 | 120 |
|
121 | 121 | function renderMessage(m) { |
122 | 122 | var div = document.createElement('div'); |
123 | | - div.className = 'msg ' + (m.role || ''); |
| 123 | + var classes = ['msg', m.role || '']; |
| 124 | + if (isToolOnlyMessage(m)) classes.push('tool-only'); |
| 125 | + div.className = classes.join(' ').trim(); |
124 | 126 | var role = document.createElement('div'); |
125 | 127 | role.className = 'role ' + (m.role || ''); |
126 | 128 | role.textContent = m.role || 'message'; |
|
132 | 134 | return div; |
133 | 135 | } |
134 | 136 |
|
| 137 | + // A message is "tool-only" when every block in its content is a tool call, |
| 138 | + // tool result, or thinking block — i.e. there is no prose, image, or other |
| 139 | + // user-visible content. When the toggle hides those blocks we also hide the |
| 140 | + // whole row so we don't leave behind a blank user/assistant stub. |
| 141 | + function isToolOnlyMessage(m) { |
| 142 | + var content = m && m.content; |
| 143 | + if (typeof content === 'string') return false; |
| 144 | + if (!Array.isArray(content) || content.length === 0) return false; |
| 145 | + return content.every(isHiddenByToolToggle); |
| 146 | + } |
| 147 | + |
| 148 | + function isHiddenByToolToggle(block) { |
| 149 | + if (!block || typeof block !== 'object') return false; |
| 150 | + switch (block.type) { |
| 151 | + case 'tool_use': |
| 152 | + case 'tool_result': |
| 153 | + case 'thinking': |
| 154 | + case 'redacted_thinking': |
| 155 | + return true; |
| 156 | + case 'text': |
| 157 | + // Empty/whitespace-only text blocks contribute nothing visible, so |
| 158 | + // treat them as "no content" too — otherwise a turn that's just a |
| 159 | + // tool call + an empty trailing text frame would still render blank. |
| 160 | + return !(block.text && block.text.trim().length > 0); |
| 161 | + default: |
| 162 | + return false; |
| 163 | + } |
| 164 | + } |
| 165 | + |
135 | 166 | function renderContent(parent, content) { |
136 | 167 | if (typeof content === 'string') { |
137 | 168 | parent.insertAdjacentHTML('beforeend', renderMarkdown(content)); |
|
0 commit comments