()
| 286 | // ─── Rendering ───────────────────────────────────────────────────── |
| 287 | |
| 288 | render() { |
| 289 | if (!this.active) return; |
| 290 | this._computeLayout(); // Recalculate in case input grew/shrunk |
| 291 | |
| 292 | let buf = ''; |
| 293 | |
| 294 | // Clear |
| 295 | buf += ANSI.clearScreen + ANSI.moveTo(1, 1); |
| 296 | |
| 297 | // Chat panel |
| 298 | buf += this._renderChatPanel(); |
| 299 | |
| 300 | // Tool panel (if split) |
| 301 | if (this.toolWidth > 0) { |
| 302 | buf += this._renderToolPanel(); |
| 303 | } |
| 304 | |
| 305 | // Input area |
| 306 | buf += this._renderInput(); |
| 307 | |
| 308 | // Status bar |
| 309 | buf += this._renderStatus(); |
| 310 | |
| 311 | // Position cursor in wrapped input (visual-width-aware) |
| 312 | const inputAvail = this.width - 5; |
| 313 | const pos = visualCursorPosition(this.inputBuffer, this.inputCursor, inputAvail); |
| 314 | const inputRow = this.chatHeight + 2 + pos.line; // +1 border, +1 for 1-index |
| 315 | const inputCol = 5 + pos.col; // "│ > " prefix |
| 316 | buf += ANSI.moveTo(inputRow, inputCol) + ANSI.showCursor; |
| 317 | |
| 318 | this._rawWrite(buf); |
| 319 | } |
| 320 | |
| 321 | _renderChatPanel() { |
| 322 | let buf = ''; |
no test coverage detected