(token)
| 1099 | |
| 1100 | // Stream a token into the last chat line |
| 1101 | streamToken(token) { |
| 1102 | if (this.chatLines.length === 0 || !this._lastLineIsStreaming) { |
| 1103 | const prefix = this.theme.success + ' AI ' + this.theme.border + '│ ' + ANSI.reset; |
| 1104 | this.chatLines.push(prefix); |
| 1105 | this._lastLineIsStreaming = true; |
| 1106 | } |
| 1107 | const lastIdx = this.chatLines.length - 1; |
| 1108 | const maxWidth = this.chatWidth - 10; |
| 1109 | |
| 1110 | // Handle newlines in token |
| 1111 | const parts = token.split('\n'); |
| 1112 | this.chatLines[lastIdx] += parts[0]; |
| 1113 | |
| 1114 | // Word wrap current line if too long |
| 1115 | if (this._stripAnsi(this.chatLines[lastIdx]).length > maxWidth) { |
| 1116 | const full = this.chatLines[lastIdx]; |
| 1117 | const prefix = ' ' + this.theme.border + '│ ' + ANSI.reset; |
| 1118 | // Find where content starts (after any prefix) |
| 1119 | const stripped = this._stripAnsi(full); |
| 1120 | const wrapped = this._wordWrap(stripped, maxWidth); |
| 1121 | this.chatLines[lastIdx] = wrapped[0]; |
| 1122 | for (let w = 1; w < wrapped.length; w++) { |
| 1123 | this.chatLines.push(prefix + wrapped[w]); |
| 1124 | } |
| 1125 | } |
| 1126 | |
| 1127 | for (let i = 1; i < parts.length; i++) { |
| 1128 | this.chatLines.push(' ' + this.theme.border + '│ ' + ANSI.reset + parts[i]); |
| 1129 | } |
| 1130 | this.chatScroll = 0; |
| 1131 | this.render(); |
| 1132 | } |
| 1133 | |
| 1134 | endStream() { |
| 1135 | this._lastLineIsStreaming = false; |
no test coverage detected