(role, content)
| 967 | // ─── Public API ────────────────────────────────────────────────────── |
| 968 | |
| 969 | addChat(role, content) { |
| 970 | this.showWelcome = false; // Dismiss welcome screen on first message |
| 971 | |
| 972 | let prefix = ''; |
| 973 | if (role === 'user') { |
| 974 | prefix = this.theme.accent + ' USER ' + this.theme.border + '│ ' + ANSI.reset; |
| 975 | } else if (role === 'assistant') { |
| 976 | prefix = this.theme.success + ' AI ' + this.theme.border + '│ ' + ANSI.reset; |
| 977 | } else { |
| 978 | prefix = this.theme.muted + ' SYS ' + this.theme.border + '│ ' + ANSI.reset; |
| 979 | } |
| 980 | const contPrefix = ' ' + this.theme.border + '│ ' + ANSI.reset; |
| 981 | const t = this.theme; |
| 982 | |
| 983 | const rawLines = content.split('\n'); |
| 984 | let inCodeBlock = false; |
| 985 | |
| 986 | for (let i = 0; i < rawLines.length; i++) { |
| 987 | const line = rawLines[i]; |
| 988 | |
| 989 | // Code block toggle |
| 990 | if (line.trim().startsWith('```')) { |
| 991 | inCodeBlock = !inCodeBlock; |
| 992 | const p = i === 0 ? prefix : contPrefix; |
| 993 | if (inCodeBlock) { |
| 994 | this.chatLines.push(p + t.border + '┌─ ' + t.muted + line.trim().slice(3) + ANSI.reset); |
| 995 | } else { |
| 996 | this.chatLines.push(contPrefix + t.border + '└─' + ANSI.reset); |
| 997 | } |
| 998 | continue; |
| 999 | } |
| 1000 | |
| 1001 | const p = i === 0 ? prefix : contPrefix; |
| 1002 | const maxWidth = this.chatWidth - 10; |
| 1003 | |
| 1004 | if (inCodeBlock) { |
| 1005 | // Syntax highlight code lines |
| 1006 | const highlighted = this._highlightCode(line); |
| 1007 | this.chatLines.push(contPrefix + t.border + '│ ' + ANSI.reset + highlighted); |
| 1008 | } else { |
| 1009 | const wrapped = this._wordWrap(line, maxWidth); |
| 1010 | for (let j = 0; j < wrapped.length; j++) { |
| 1011 | this.chatLines.push((j === 0 ? p : contPrefix) + wrapped[j]); |
| 1012 | } |
| 1013 | } |
| 1014 | } |
| 1015 | this.chatLines.push(''); // spacer |
| 1016 | this.chatScroll = 0; // snap to bottom |
| 1017 | this.msgCount++; |
| 1018 | |
| 1019 | // Cap chatLines to prevent unbounded growth (keep last 5000 lines). |
| 1020 | // A very long session with verbose tool output can accumulate tens of |
| 1021 | // thousands of lines; rendering stays fast by only keeping recent history. |
| 1022 | const MAX_CHAT_LINES = 5000; |
| 1023 | if (this.chatLines.length > MAX_CHAT_LINES) { |
| 1024 | this.chatLines.splice(0, this.chatLines.length - MAX_CHAT_LINES); |
| 1025 | } |
| 1026 |
no test coverage detected