(ev, uiState)
| 133 | } |
| 134 | |
| 135 | function renderUIEvent(ev, uiState) { |
| 136 | if (!ev || !ev.channel || !ev.type) return; |
| 137 | |
| 138 | if (ev.channel === "progress") { |
| 139 | switch (ev.type) { |
| 140 | case "text_chunk_start": |
| 141 | uiState.currentTextLine = appendPhaseLine("", "tool"); |
| 142 | uiState.textStarted = true; |
| 143 | break; |
| 144 | case "text_chunk": |
| 145 | if (ev.payload && ev.payload.delta) { |
| 146 | // 如果还没有文本行,创建一个(兼容没有 text_chunk_start 的情况) |
| 147 | if (!uiState.textStarted) { |
| 148 | uiState.currentTextLine = appendPhaseLine("", "tool"); |
| 149 | uiState.textStarted = true; |
| 150 | } |
| 151 | appendToLastLine(ev.payload.delta); |
| 152 | } |
| 153 | break; |
| 154 | case "tool:start": |
| 155 | if (ev.payload && ev.payload.call) { |
| 156 | uiState.textStarted = false; // 重置文本状态 |
| 157 | renderToolStartWeb(uiState, ev.payload.call); |
| 158 | } |
| 159 | break; |
| 160 | case "tool:error": |
| 161 | if (ev.payload && ev.payload.call) { |
| 162 | uiState.textStarted = false; // 重置文本状态 |
| 163 | appendPhaseLine(`[Tool Error] ${ev.payload.call.name}: ${ev.payload.error || ""}`, "error"); |
| 164 | } |
| 165 | break; |
| 166 | case "done": |
| 167 | uiState.textStarted = false; // 重置文本状态 |
| 168 | appendPhaseLine(`[Done] ${ev.payload && ev.payload.reason ? ev.payload.reason : ""}`, "tool"); |
| 169 | break; |
| 170 | } |
| 171 | } else if (ev.channel === "monitor") { |
| 172 | if (ev.type === "state_changed" && ev.payload) { |
| 173 | appendPhaseLine(`[State] ${ev.payload.state}`, "tool"); |
| 174 | } |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | function renderToolStartWeb(uiState, call) { |
| 179 | if (!call || !call.name) return; |
no test coverage detected