11import { createResource , Show , createMemo , createSignal , onMount , type Accessor , type JSX } from "solid-js"
2- import { TextAttributes , type RGBA } from "@opentui/core"
2+ import { TextAttributes } from "@opentui/core"
33import { useTerminalDimensions } from "@opentui/solid"
44import { debounce , leadingAndTrailing } from "@solid-primitives/scheduled"
5- import type { Message , Part , Session as SdkSession , SnapshotFileDiff } from "@opencode-ai/sdk/v2"
5+ import type { Message , Part , Session as SdkSession } from "@opencode-ai/sdk/v2"
66import { useTheme } from "@tui/context/theme"
77import { useSDK } from "@tui/context/sdk"
88import { useSync } from "@tui/context/sync"
99import { Locale } from "@/util/locale"
1010import { Spinner } from "@tui/component/spinner"
11- import { extractMessageMarkdown , extractMessageText , formatDiffSummary , relativeTime , shortModelLabel } from "./util"
11+ import { extractMessageMarkdown , extractMessageText , relativeTime } from "./util"
1212
1313type WithParts = { info : Message ; parts : Part [ ] }
1414
1515type Sdk = ReturnType < typeof useSDK >
1616type Sync = ReturnType < typeof useSync >
1717
1818const messageCache = new Map < string , Promise < WithParts [ ] > > ( )
19- const diffCache = new Map < string , Promise < SnapshotFileDiff [ ] > > ( )
2019
2120function cacheKey ( sessionID : string , version : number ) {
2221 return `${ sessionID } :${ version } `
@@ -36,41 +35,21 @@ function loadMessages(sdk: Sdk, sessionID: string, version: number): Promise<Wit
3635 const promise = sdk . client . session
3736 . messages ( { sessionID, limit : 50 } )
3837 . then ( ( res ) => {
39- if ( res . error ) messageCache . delete ( key )
38+ if ( res . error ) throw res . error
4039 return ( res . data as WithParts [ ] | undefined ) ?? [ ]
4140 } )
42- . catch ( ( ) => {
41+ . catch ( ( error ) => {
4342 messageCache . delete ( key )
44- return [ ] as WithParts [ ]
43+ throw error
4544 } )
4645 messageCache . set ( key , promise )
4746 return promise
4847}
4948
50- function loadDiff ( sdk : Sdk , sessionID : string , version : number ) : Promise < SnapshotFileDiff [ ] > {
51- const key = cacheKey ( sessionID , version )
52- const cached = diffCache . get ( key )
53- if ( cached ) return cached
54-
55- const promise = sdk . client . session
56- . diff ( { sessionID } )
57- . then ( ( res ) => {
58- if ( res . error ) diffCache . delete ( key )
59- return ( res . data as SnapshotFileDiff [ ] | undefined ) ?? [ ]
60- } )
61- . catch ( ( ) => {
62- diffCache . delete ( key )
63- return [ ] as SnapshotFileDiff [ ]
64- } )
65- diffCache . set ( key , promise )
66- return promise
67- }
68-
6949export function prefetchPreviews ( sdk : Sdk , sync : Sync , sessionIDs : readonly string [ ] ) {
7050 for ( const id of sessionIDs ) {
7151 const version = sync . data . session . find ( ( session ) => session . id === id ) ?. time . updated ?? 0
7252 if ( ! hydrateFromSync ( sync , id ) ) loadMessages ( sdk , id , version ) . catch ( ( ) => { } )
73- if ( ! sync . data . session_diff [ id ] ?. length ) loadDiff ( sdk , id , version ) . catch ( ( ) => { } )
7453 }
7554}
7655
@@ -121,13 +100,6 @@ export function SessionPreviewPane(props: {
121100 return hydrateFromSync ( sync , id )
122101 } )
123102
124- const syncedDiff = createMemo ( ( ) => {
125- const id = props . sessionID ( )
126- if ( ! id ) return undefined
127- const diff = sync . data . session_diff [ id ]
128- return diff && diff . length > 0 ? ( diff as SnapshotFileDiff [ ] ) : undefined
129- } )
130-
131103 const [ fetchedMessages ] = createResource (
132104 ( ) => {
133105 const id = props . sessionID ( )
@@ -137,31 +109,7 @@ export function SessionPreviewPane(props: {
137109 async ( input ) => loadMessages ( sdk , input . sessionID , input . version ) ,
138110 )
139111
140- const [ fetchedDiff ] = createResource (
141- ( ) => {
142- const id = props . sessionID ( )
143- if ( ! id || syncedDiff ( ) ) return undefined
144- return { sessionID : id , version : session ( ) ?. time . updated ?? 0 }
145- } ,
146- async ( input ) => loadDiff ( sdk , input . sessionID , input . version ) ,
147- )
148-
149112 const messages = createMemo ( ( ) => syncedMessages ( ) ?? fetchedMessages ( ) ?? [ ] )
150- const diff = createMemo ( ( ) => syncedDiff ( ) ?? fetchedDiff ( ) ?? [ ] )
151-
152- const diffSummary = createMemo ( ( ) => {
153- const live = diff ( )
154- if ( live && live . length > 0 ) {
155- let additions = 0
156- let deletions = 0
157- for ( const file of live ) {
158- additions += file . additions ?? 0
159- deletions += file . deletions ?? 0
160- }
161- return formatDiffSummary ( { additions, deletions, files : live . length } )
162- }
163- return formatDiffSummary ( session ( ) ?. summary )
164- } )
165113
166114 const exchange = createMemo ( ( ) => {
167115 const items = messages ( )
@@ -174,13 +122,13 @@ export function SessionPreviewPane(props: {
174122 return { user, assistant }
175123 } )
176124
177- const loading = createMemo ( ( ) => ( fetchedMessages . loading || fetchedDiff . loading ) && ! exchange ( ) )
125+ const loading = createMemo ( ( ) => fetchedMessages . loading && ! exchange ( ) )
178126
179127 const statusLabel = createMemo ( ( ) => {
180128 const s = status ( )
181- if ( s === "busy" ) return { text : "working" , color : theme . warning }
182- if ( s === "retry" ) return { text : "retrying" , color : theme . warning }
183- return { text : "idle" , color : theme . textMuted }
129+ if ( s === "busy" ) return "working"
130+ if ( s === "retry" ) return "retrying"
131+ return "idle"
184132 } )
185133
186134 return (
@@ -191,7 +139,7 @@ export function SessionPreviewPane(props: {
191139 paddingTop = { 1 }
192140 paddingBottom = { 1 }
193141 gap = { 1 }
194- maxHeight = { maxHeight ( ) }
142+ height = { maxHeight ( ) }
195143 overflow = "hidden"
196144 >
197145 < Show
@@ -204,7 +152,7 @@ export function SessionPreviewPane(props: {
204152 >
205153 { ( s ) => (
206154 < >
207- < Header session = { s ( ) } statusLabel = { statusLabel ( ) } diff = { diffSummary ( ) } />
155+ < Header session = { s ( ) } statusLabel = { statusLabel ( ) } />
208156 < Show when = { loading ( ) } >
209157 < Spinner > loading preview...</ Spinner >
210158 </ Show >
@@ -213,7 +161,7 @@ export function SessionPreviewPane(props: {
213161 fallback = {
214162 < Show when = { ! loading ( ) } >
215163 < text fg = { theme . textMuted } wrapMode = "word" >
216- No messages yet
164+ { fetchedMessages . error ? "Preview unavailable" : " No messages yet" }
217165 </ text >
218166 </ Show >
219167 }
@@ -241,24 +189,12 @@ function messageParentID(item: WithParts) {
241189
242190const ROW_WIDTH = 40
243191
244- function Header ( props : {
245- session : SdkSession
246- statusLabel : { text : string ; color : RGBA }
247- diff : { additions : number ; deletions : number ; files : number } | undefined
248- } ) {
192+ function Header ( props : { session : SdkSession ; statusLabel : string } ) {
249193 const { theme } = useTheme ( )
250194 const title = createMemo ( ( ) => Locale . truncate ( props . session . title , ROW_WIDTH ) )
251- const modelAgent = createMemo ( ( ) => {
252- const m = shortModelLabel ( props . session . model )
253- const a = props . session . agent ?? ""
254- if ( m && a ) return Locale . truncate ( `${ m } · ${ a } ` , ROW_WIDTH )
255- if ( m ) return Locale . truncate ( m , ROW_WIDTH )
256- if ( a ) return Locale . truncate ( a , ROW_WIDTH )
257- return ""
258- } )
259195 const statusRest = createMemo ( ( ) => {
260196 const joined = ` · ${ relativeTime ( props . session . time . updated ) } `
261- return Locale . truncate ( joined , Math . max ( 0 , ROW_WIDTH - props . statusLabel . text . length ) )
197+ return Locale . truncate ( joined , Math . max ( 0 , ROW_WIDTH - props . statusLabel . length ) )
262198 } )
263199
264200 return (
@@ -268,20 +204,12 @@ function Header(props: {
268204 { title ( ) }
269205 </ text >
270206 </ Row >
271- < Show when = { modelAgent ( ) } >
272- < Row height = { 1 } >
273- < text fg = { theme . text } wrapMode = "none" overflow = "hidden" >
274- { modelAgent ( ) }
275- </ text >
276- </ Row >
277- </ Show >
278207 < Row height = { 1 } >
279208 < text fg = { theme . textMuted } wrapMode = "none" overflow = "hidden" >
280- < span style = { { fg : props . statusLabel . color } } > { props . statusLabel . text } </ span >
209+ < span > { props . statusLabel } </ span >
281210 < span > { statusRest ( ) } </ span >
282211 </ text >
283212 </ Row >
284- < Show when = { props . diff } > { ( d ) => < DiffRow diff = { d ( ) } /> } </ Show >
285213 </ box >
286214 )
287215}
@@ -294,28 +222,6 @@ function Row(props: { height: number; children: JSX.Element }) {
294222 )
295223}
296224
297- function DiffRow ( props : { diff : { additions : number ; deletions : number ; files : number } } ) {
298- const { theme } = useTheme ( )
299- const showAdds = ( ) => props . diff . additions > 0
300- const showDels = ( ) => props . diff . deletions > 0
301- if ( ! showAdds ( ) && ! showDels ( ) ) return null
302- return (
303- < Row height = { 1 } >
304- < text wrapMode = "none" overflow = "hidden" >
305- < Show when = { showAdds ( ) } >
306- < span style = { { fg : theme . diffAdded } } > +{ props . diff . additions } </ span >
307- </ Show >
308- < Show when = { showAdds ( ) && showDels ( ) } >
309- < span > </ span >
310- </ Show >
311- < Show when = { showDels ( ) } >
312- < span style = { { fg : theme . diffRemoved } } > −{ props . diff . deletions } </ span >
313- </ Show >
314- </ text >
315- </ Row >
316- )
317- }
318-
319225const PROMPT_MAX_CHARS = 240
320226const REPLY_MAX_LINES = 12
321227const REPLY_MAX_CHARS = 800
0 commit comments