@@ -57,6 +57,8 @@ export interface BuildChatLinesInput {
5757
5858const INDENT = ' ' ;
5959const INDENT_WIDTH = 2 ;
60+ const USER_MESSAGE_BORDER = '┃ ' ;
61+ const USER_MESSAGE_BORDER_WIDTH = textWidth ( USER_MESSAGE_BORDER ) ;
6062const MAX_ELEMENT_LINES = 1000 ;
6163const MAX_TABLE_ROWS = 12 ;
6264const MIN_TABLE_CELL_WIDTH = 3 ;
@@ -108,6 +110,13 @@ export function buildChatLines(input: BuildChatLinesInput): VisualLine[] {
108110 return lines ;
109111 }
110112
113+ // Keep the first message clear of the viewport edge when it replaces the
114+ // startup banner.
115+ if ( hasMessages ) {
116+ push ( 'spacer:top:0' , blankNode ( 'spacer:top:0' ) ) ;
117+ push ( 'spacer:top:1' , blankNode ( 'spacer:top:1' ) ) ;
118+ }
119+
111120 for ( const message of input . messages ) {
112121 pushMessageLines ( message , toolCalls , bodyWidth , push ) ;
113122 push ( `m:${ message . id } :after` , blankNode ( `m:${ message . id } :after` ) ) ;
@@ -171,12 +180,31 @@ function pushMessageLines(
171180 bodyWidth : number ,
172181 push : ( key : string , node : React . ReactNode ) => void ,
173182) : void {
183+ const isUser = message . role === 'user' ;
174184 const headerKey = `m:${ message . id } :h` ;
175- push ( headerKey , < MessageHeader key = { headerKey } message = { message } /> ) ;
185+ const messageBodyWidth = isUser
186+ ? Math . max ( 1 , bodyWidth - USER_MESSAGE_BORDER_WIDTH )
187+ : bodyWidth ;
188+
189+ const pushLine = ( key : string , node : React . ReactNode ) => {
190+ if ( isUser ) {
191+ push (
192+ key ,
193+ < Box key = { `box-${ key } ` } >
194+ < Text color = "blue" > { USER_MESSAGE_BORDER } </ Text >
195+ { node }
196+ </ Box > ,
197+ ) ;
198+ } else {
199+ push ( key , node ) ;
200+ }
201+ } ;
202+
203+ pushLine ( headerKey , < MessageHeader key = { headerKey } message = { message } /> ) ;
176204
177205 if ( message . elements . length === 0 && message . isStreaming ) {
178206 const key = `m:${ message . id } :thinking` ;
179- push ( key , < ThinkingLine key = { key } /> ) ;
207+ pushLine ( key , < ThinkingLine key = { key } /> ) ;
180208 return ;
181209 }
182210
@@ -188,7 +216,7 @@ function pushMessageLines(
188216 // uniform marginTop={1} spacing within the flat single-row line model.
189217 const separate = ( key : string ) : void => {
190218 if ( blocksEmitted > 0 ) {
191- push ( key , blankNode ( key ) ) ;
219+ pushLine ( key , blankNode ( key ) ) ;
192220 }
193221 } ;
194222
@@ -202,12 +230,12 @@ function pushMessageLines(
202230 }
203231 separate ( `${ keyBase } :gap` ) ;
204232 blocksEmitted += 1 ;
205- pushMarkdownLines ( normalized , bodyWidth , keyBase , ( key , node ) => {
233+ pushMarkdownLines ( normalized , messageBodyWidth , keyBase , ( key , node ) => {
206234 if ( emittedLines >= MAX_ELEMENT_LINES ) {
207235 return ;
208236 }
209237 emittedLines += 1 ;
210- push ( key , node ) ;
238+ pushLine ( key , node ) ;
211239 } ) ;
212240 return ;
213241 }
@@ -220,7 +248,7 @@ function pushMessageLines(
220248 const key = `${ keyBase } :tool` ;
221249 separate ( `${ key } :gap` ) ;
222250 blocksEmitted += 1 ;
223- push (
251+ pushLine (
224252 key ,
225253 < Box key = { key } paddingLeft = { INDENT_WIDTH } >
226254 < InlineToolCall toolCall = { toolCall } showName />
@@ -230,7 +258,7 @@ function pushMessageLines(
230258
231259 if ( message . isStreaming && message . elements . length > 0 ) {
232260 const key = `m:${ message . id } :cursor` ;
233- push ( key , < Text key = { key } dimColor > { `${ INDENT } ▊` } </ Text > ) ;
261+ pushLine ( key , < Text key = { key } dimColor > { `${ INDENT } ▊` } </ Text > ) ;
234262 }
235263}
236264
0 commit comments