@@ -13,8 +13,10 @@ import {
1313 formatSelectedSkillsStatus ,
1414 getPromptCursorPlacement ,
1515 getPromptReturnKeyAction ,
16+ isPromptCursorAtWrapBoundary ,
1617 isClearImageAttachmentsShortcut ,
1718 removeCurrentSlashToken ,
19+ resolvePromptTerminalCursorPosition ,
1820 toggleSkillSelection ,
1921 renderBufferWithCursor ,
2022 buildInitPromptSubmission ,
@@ -294,24 +296,83 @@ test("renderBufferWithCursor styles exactly one simulated cursor", () => {
294296 assert . equal ( ( renderBufferWithCursor ( { text : "hello\nworld" , cursor : 6 } , true ) . match ( ANSI_RE ) ?? [ ] ) . length , 2 ) ;
295297} ) ;
296298
297- test ( "getPromptCursorPlacement targets the prompt row above divider and footer" , ( ) => {
298- const placement = getPromptCursorPlacement ( { text : "hello" , cursor : 5 } , 80 , 2 , "Enter send" ) ;
299- assert . deepEqual ( placement , { rowsUp : 3 , column : 7 } ) ;
299+ test ( "renderBufferWithCursor can suppress the simulated cursor for real terminal cursor mode" , ( ) => {
300+ assert . equal (
301+ ( renderBufferWithCursor ( { text : "" , cursor : 0 } , true , undefined , undefined , false ) . match ( ANSI_RE ) ?? [ ] ) . length ,
302+ 0
303+ ) ;
304+ assert . equal (
305+ stripAnsi ( renderBufferWithCursor ( { text : "" , cursor : 0 } , true , "Ask anything" , undefined , false ) ) ,
306+ " Ask anything"
307+ ) ;
308+ assert . equal (
309+ ( renderBufferWithCursor ( { text : "hello" , cursor : 1 } , true , undefined , undefined , false ) . match ( ANSI_RE ) ?? [ ] )
310+ . length ,
311+ 0
312+ ) ;
313+ assert . equal (
314+ stripAnsi ( renderBufferWithCursor ( { text : "hello\n" , cursor : 6 } , true , undefined , undefined , false ) ) ,
315+ "hello\n "
316+ ) ;
317+ } ) ;
318+
319+ test ( "getPromptCursorPlacement targets an Ink-relative prompt cell" , ( ) => {
320+ const placement = getPromptCursorPlacement ( { text : "hello" , cursor : 5 } , 80 ) ;
321+ assert . deepEqual ( placement , { row : 0 , column : 5 } ) ;
300322} ) ;
301323
302324test ( "getPromptCursorPlacement targets the reserved row after a trailing newline" , ( ) => {
303- const placement = getPromptCursorPlacement ( { text : "hello\n" , cursor : 6 } , 80 , 2 , "Enter send" ) ;
304- assert . deepEqual ( placement , { rowsUp : 3 , column : 2 } ) ;
325+ const placement = getPromptCursorPlacement ( { text : "hello\n" , cursor : 6 } , 80 ) ;
326+ assert . deepEqual ( placement , { row : 1 , column : 0 } ) ;
305327} ) ;
306328
307329test ( "getPromptCursorPlacement accounts for CJK character width" , ( ) => {
308- const placement = getPromptCursorPlacement ( { text : "你好" , cursor : 2 } , 80 , 2 , "Enter send" ) ;
309- assert . equal ( placement . column , 6 ) ;
330+ const placement = getPromptCursorPlacement ( { text : "你好" , cursor : 2 } , 80 ) ;
331+ assert . equal ( placement . column , 4 ) ;
310332} ) ;
311333
312334test ( "getPromptCursorPlacement accounts for multiline buffer rows" , ( ) => {
313- const placement = getPromptCursorPlacement ( { text : "hello\nworld" , cursor : 11 } , 80 , 2 , "Enter send" ) ;
314- assert . deepEqual ( placement , { rowsUp : 3 , column : 7 } ) ;
315- const middle = getPromptCursorPlacement ( { text : "hello\nworld" , cursor : 2 } , 80 , 2 , "Enter send" ) ;
316- assert . deepEqual ( middle , { rowsUp : 4 , column : 4 } ) ;
335+ const placement = getPromptCursorPlacement ( { text : "hello\nworld" , cursor : 11 } , 80 ) ;
336+ assert . deepEqual ( placement , { row : 1 , column : 5 } ) ;
337+ const middle = getPromptCursorPlacement ( { text : "hello\nworld" , cursor : 2 } , 80 ) ;
338+ assert . deepEqual ( middle , { row : 0 , column : 2 } ) ;
339+ } ) ;
340+
341+ test ( "getPromptCursorPlacement accounts for wrapped input rows" , ( ) => {
342+ const placement = getPromptCursorPlacement ( { text : "hello" , cursor : 5 } , 5 ) ;
343+ assert . deepEqual ( placement , { row : 1 , column : 0 } ) ;
344+ const cursorBeforeWrappedChar = getPromptCursorPlacement ( { text : "hello!" , cursor : 5 } , 5 ) ;
345+ assert . deepEqual ( cursorBeforeWrappedChar , { row : 1 , column : 0 } ) ;
346+ const secondLine = getPromptCursorPlacement ( { text : "hello!" , cursor : 6 } , 5 ) ;
347+ assert . deepEqual ( secondLine , { row : 1 , column : 1 } ) ;
348+ } ) ;
349+
350+ test ( "isPromptCursorAtWrapBoundary detects hard-wrapped cursor positions" , ( ) => {
351+ assert . equal ( isPromptCursorAtWrapBoundary ( { text : "hell" , cursor : 4 } , 5 ) , false ) ;
352+ assert . equal ( isPromptCursorAtWrapBoundary ( { text : "hello" , cursor : 5 } , 5 ) , true ) ;
353+ assert . equal ( isPromptCursorAtWrapBoundary ( { text : "hello!" , cursor : 6 } , 5 ) , true ) ;
354+ assert . equal ( isPromptCursorAtWrapBoundary ( { text : "hello world" , cursor : 6 } , 5 ) , true ) ;
355+ assert . equal ( isPromptCursorAtWrapBoundary ( { text : "hello\n" , cursor : 6 } , 5 ) , false ) ;
356+ assert . equal ( isPromptCursorAtWrapBoundary ( { text : "hello\nworld" , cursor : 11 } , 5 ) , true ) ;
357+ } ) ;
358+
359+ test ( "resolvePromptTerminalCursorPosition requires matching measured layout" , ( ) => {
360+ const placement = { row : 1 , column : 4 } ;
361+ const origin = { layoutKey : "skills:1" , left : 2 , top : 3 } ;
362+
363+ assert . deepEqual ( resolvePromptTerminalCursorPosition ( placement , true , "skills:1" , origin ) , { x : 6 , y : 4 } ) ;
364+ assert . equal ( resolvePromptTerminalCursorPosition ( placement , true , "skills:0" , origin ) , undefined ) ;
365+ assert . equal ( resolvePromptTerminalCursorPosition ( placement , false , "skills:1" , origin ) , undefined ) ;
366+ assert . equal ( resolvePromptTerminalCursorPosition ( placement , true , "skills:1" , null ) , undefined ) ;
367+ } ) ;
368+
369+ test ( "resolvePromptTerminalCursorPosition clamps negative terminal cells" , ( ) => {
370+ assert . deepEqual (
371+ resolvePromptTerminalCursorPosition ( { row : 0 , column : 1 } , true , "current" , {
372+ layoutKey : "current" ,
373+ left : - 5 ,
374+ top : - 1 ,
375+ } ) ,
376+ { x : 0 , y : 0 }
377+ ) ;
317378} ) ;
0 commit comments