@@ -20,7 +20,6 @@ import { useTheme } from "@tui/context/theme"
2020import {
2121 BoxRenderable ,
2222 ScrollBoxRenderable ,
23- TextAttributes ,
2423 addDefaultParsers ,
2524 MacOSScrollAccel ,
2625 type ScrollAcceleration ,
@@ -65,7 +64,6 @@ import { Editor } from "../../util/editor"
6564import { Global } from "@/global"
6665import fs from "fs/promises"
6766import stripAnsi from "strip-ansi"
68- import { LSP } from "@/lsp/index.ts"
6967
7068addDefaultParsers ( parsers . parsers )
7169
@@ -101,7 +99,12 @@ export function Session() {
10199 const permissions = createMemo ( ( ) => sync . data . permission [ route . sessionID ] ?? [ ] )
102100
103101 const pending = createMemo ( ( ) => {
104- return messages ( ) . findLast ( ( x ) => x . role === "assistant" && ! x . time ?. completed ) ?. id
102+ return messages ( ) . findLast ( ( x ) => x . role === "assistant" && ! x . time . completed ) ?. id
103+ } )
104+
105+ const lastUserMessage = createMemo ( ( ) => {
106+ const p = pending ( )
107+ return messages ( ) . findLast ( ( x ) => x . role === "user" && ( ! p || x . id < p ) ) as UserMessage
105108 } )
106109
107110 const dimensions = useTerminalDimensions ( )
@@ -801,7 +804,7 @@ export function Session() {
801804 </ Match >
802805 < Match when = { message . role === "assistant" } >
803806 < AssistantMessage
804- last = { index ( ) === messages ( ) . length - 1 }
807+ last = { pending ( ) === message . id }
805808 message = { message as AssistantMessage }
806809 parts = { sync . data . part [ message . id ] ?? [ ] }
807810 />
@@ -856,64 +859,84 @@ function UserMessage(props: {
856859 const queued = createMemo ( ( ) => props . pending && props . message . id > props . pending )
857860 const color = createMemo ( ( ) => ( queued ( ) ? theme . accent : theme . secondary ) )
858861
862+ const compaction = createMemo ( ( ) => props . parts . find ( ( x ) => x . type === "compaction" ) )
863+
859864 return (
860- < Show when = { text ( ) } >
861- < box
862- id = { props . message . id }
863- onMouseOver = { ( ) => {
864- setHover ( true )
865- } }
866- onMouseOut = { ( ) => {
867- setHover ( false )
868- } }
869- onMouseUp = { props . onMouseUp }
870- border = { [ "left" ] }
871- paddingTop = { 1 }
872- paddingBottom = { 1 }
873- paddingLeft = { 2 }
874- marginTop = { props . index === 0 ? 0 : 1 }
875- backgroundColor = { hover ( ) ? theme . backgroundElement : theme . backgroundPanel }
876- customBorderChars = { SplitBorder . customBorderChars }
877- borderColor = { color ( ) }
878- flexShrink = { 0 }
879- >
880- < text fg = { theme . text } > { text ( ) ?. text } </ text >
881- < Show when = { files ( ) . length } >
882- < box flexDirection = "row" paddingBottom = { 1 } paddingTop = { 1 } gap = { 1 } flexWrap = "wrap" >
883- < For each = { files ( ) } >
884- { ( file ) => {
885- const bg = createMemo ( ( ) => {
886- if ( file . mime . startsWith ( "image/" ) ) return theme . accent
887- if ( file . mime === "application/pdf" ) return theme . primary
888- return theme . secondary
889- } )
890- return (
891- < text fg = { theme . text } >
892- < span style = { { bg : bg ( ) , fg : theme . background } } > { MIME_BADGE [ file . mime ] ?? file . mime } </ span >
893- < span style = { { bg : theme . backgroundElement , fg : theme . textMuted } } > { file . filename } </ span >
894- </ text >
895- )
896- } }
897- </ For >
898- </ box >
899- </ Show >
900- < text fg = { theme . text } >
901- { sync . data . config . username ?? "You" } { " " }
902- < Show
903- when = { queued ( ) }
904- fallback = { < span style = { { fg : theme . textMuted } } > ({ Locale . time ( props . message . time . created ) } )</ span > }
905- >
906- < span style = { { bg : theme . accent , fg : theme . backgroundPanel , bold : true } } > QUEUED </ span >
865+ < >
866+ < Show when = { text ( ) } >
867+ < box
868+ id = { props . message . id }
869+ onMouseOver = { ( ) => {
870+ setHover ( true )
871+ } }
872+ onMouseOut = { ( ) => {
873+ setHover ( false )
874+ } }
875+ onMouseUp = { props . onMouseUp }
876+ border = { [ "left" ] }
877+ paddingTop = { 1 }
878+ paddingBottom = { 1 }
879+ paddingLeft = { 2 }
880+ marginTop = { props . index === 0 ? 0 : 1 }
881+ backgroundColor = { hover ( ) ? theme . backgroundElement : theme . backgroundPanel }
882+ customBorderChars = { SplitBorder . customBorderChars }
883+ borderColor = { color ( ) }
884+ flexShrink = { 0 }
885+ >
886+ < text fg = { theme . text } > { text ( ) ?. text } </ text >
887+ < Show when = { files ( ) . length } >
888+ < box flexDirection = "row" paddingBottom = { 1 } paddingTop = { 1 } gap = { 1 } flexWrap = "wrap" >
889+ < For each = { files ( ) } >
890+ { ( file ) => {
891+ const bg = createMemo ( ( ) => {
892+ if ( file . mime . startsWith ( "image/" ) ) return theme . accent
893+ if ( file . mime === "application/pdf" ) return theme . primary
894+ return theme . secondary
895+ } )
896+ return (
897+ < text fg = { theme . text } >
898+ < span style = { { bg : bg ( ) , fg : theme . background } } > { MIME_BADGE [ file . mime ] ?? file . mime } </ span >
899+ < span style = { { bg : theme . backgroundElement , fg : theme . textMuted } } > { file . filename } </ span >
900+ </ text >
901+ )
902+ } }
903+ </ For >
904+ </ box >
907905 </ Show >
908- </ text >
909- </ box >
910- </ Show >
906+ < text fg = { theme . text } >
907+ { sync . data . config . username ?? "You" } { " " }
908+ < Show
909+ when = { queued ( ) }
910+ fallback = { < span style = { { fg : theme . textMuted } } > ({ Locale . time ( props . message . time . created ) } )</ span > }
911+ >
912+ < span style = { { bg : theme . accent , fg : theme . backgroundPanel , bold : true } } > QUEUED </ span >
913+ </ Show >
914+ </ text >
915+ </ box >
916+ </ Show >
917+ < Show when = { compaction ( ) } >
918+ < box
919+ marginTop = { 1 }
920+ border = { [ "top" ] }
921+ title = " Compaction "
922+ titleAlignment = "center"
923+ borderColor = { theme . borderActive }
924+ />
925+ </ Show >
926+ </ >
911927 )
912928}
913929
914930function AssistantMessage ( props : { message : AssistantMessage ; parts : Part [ ] ; last : boolean } ) {
915931 const local = useLocal ( )
916932 const { theme } = useTheme ( )
933+ const sync = useSync ( )
934+ const status = createMemo (
935+ ( ) =>
936+ sync . data . session_status [ props . message . sessionID ] ?? {
937+ type : "idle" ,
938+ } ,
939+ )
917940 return (
918941 < >
919942 < For each = { props . parts } >
@@ -945,23 +968,15 @@ function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; las
945968 < text fg = { theme . textMuted } > { props . message . error ?. data . message } </ text >
946969 </ box >
947970 </ Show >
948- < Show
949- when = {
950- ! props . message . time . completed ||
951- ( props . last && props . parts . some ( ( item ) => item . type === "step-finish" && item . reason === "tool-calls" ) )
952- }
953- >
954- < box
955- paddingLeft = { 2 }
956- marginTop = { 1 }
957- flexDirection = "row"
958- gap = { 1 }
959- border = { [ "left" ] }
960- customBorderChars = { SplitBorder . customBorderChars }
961- borderColor = { theme . backgroundElement }
962- >
971+ < Show when = { props . last && status ( ) . type !== "idle" } >
972+ < box paddingLeft = { 3 } flexDirection = "row" gap = { 1 } marginTop = { 1 } >
963973 < text fg = { local . agent . color ( props . message . mode ) } > { Locale . titlecase ( props . message . mode ) } </ text >
964- < Shimmer text = { `${ props . message . modelID } ` } color = { theme . text } />
974+ < Shimmer text = { props . message . modelID } color = { theme . text } />
975+ < Show when = { status ( ) . type === "retry" } >
976+ < text fg = { theme . error } >
977+ { ( status ( ) as any ) . message } [attempt #{ ( status ( ) as any ) . attempt } ]
978+ </ text >
979+ </ Show >
965980 </ box >
966981 </ Show >
967982 < Show
0 commit comments