@@ -17,6 +17,7 @@ import {
1717} from "@opencode-ai/ui"
1818import { FileIcon } from "@/ui"
1919import FileTree from "@/components/file-tree"
20+ import { MessageProgress } from "@/components/message-progress"
2021import { For , onCleanup , onMount , Show , Match , Switch , createSignal , createEffect , createMemo } from "solid-js"
2122import { useLocal , type LocalFile } from "@/context/local"
2223import { createStore } from "solid-js/store"
@@ -39,6 +40,7 @@ import { useSync } from "@/context/sync"
3940import { useSDK } from "@/context/sdk"
4041import { type AssistantMessage as AssistantMessageType } from "@opencode-ai/sdk"
4142import { Markdown } from "@opencode-ai/ui"
43+ import { Spinner } from "@/components/spinner"
4244
4345export default function Page ( ) {
4446 const local = useLocal ( )
@@ -546,21 +548,31 @@ export default function Page() {
546548 < For each = { local . session . userMessages ( ) } >
547549 { ( message ) => {
548550 const diffs = createMemo ( ( ) => message . summary ?. diffs ?? [ ] )
551+ const working = createMemo ( ( ) => ! message . summary ?. title )
549552 return (
550- < li
551- class = "group/li flex items-center gap-x-2 py-1 self-stretch cursor-default"
552- onClick = { ( ) => local . session . setActiveMessage ( message . id ) }
553- >
554- < DiffChanges diff = { diffs ( ) } variant = "bars" />
555- < div
556- data-active = { local . session . activeMessage ( ) ?. id === message . id }
557- classList = { {
558- "text-14-regular text-text-weak whitespace-nowrap truncate min-w-0" : true ,
559- "text-text-weak data-[active=true]:text-text-strong group-hover/li:text-text-base" : true ,
560- } }
553+ < li class = "group/li flex items-center self-stretch" >
554+ < button
555+ class = "flex items-center self-stretch w-full gap-x-2 py-1 cursor-default"
556+ onClick = { ( ) => local . session . setActiveMessage ( message . id ) }
561557 >
562- { message . summary ?. title ?? local . session . getMessageText ( message ) }
563- </ div >
558+ < Switch >
559+ < Match when = { working ( ) } >
560+ < Spinner class = "text-text-base shrink-0 w-[18px] aspect-square" />
561+ </ Match >
562+ < Match when = { true } >
563+ < DiffChanges diff = { diffs ( ) } variant = "bars" />
564+ </ Match >
565+ </ Switch >
566+ < div
567+ data-active = { local . session . activeMessage ( ) ?. id === message . id }
568+ classList = { {
569+ "text-14-regular text-text-weak whitespace-nowrap truncate min-w-0" : true ,
570+ "text-text-weak data-[active=true]:text-text-strong group-hover/li:text-text-base" : true ,
571+ } }
572+ >
573+ { message . summary ?. title ?? local . session . getMessageText ( message ) }
574+ </ div >
575+ </ button >
564576 </ li >
565577 )
566578 } }
@@ -600,19 +612,15 @@ export default function Page() {
600612 </ Show >
601613 </ div >
602614 </ div >
603- < Show when = { title } >
604- < div class = "-mt-8" >
605- < Message message = { message } parts = { parts ( ) } />
606- </ div >
607- </ Show >
615+ < div class = "-mt-8" >
616+ < Message message = { message } parts = { parts ( ) } />
617+ </ div >
608618 { /* Summary */ }
609619 < Show when = { ! working ( ) } >
610620 < div class = "w-full flex flex-col gap-6 items-start self-stretch" >
611621 < div class = "flex flex-col items-start gap-1 self-stretch" >
612622 < h2 class = "text-12-medium text-text-weak" > Summary</ h2 >
613- < Show when = { summary ( ) } >
614- < Markdown text = { summary ( ) ! } />
615- </ Show >
623+ < Show when = { summary ( ) } > { ( summary ) => < Markdown text = { summary ( ) } /> } </ Show >
616624 </ div >
617625 < Accordion class = "w-full" multiple >
618626 < For each = { message . summary ?. diffs || [ ] } >
@@ -666,85 +674,7 @@ export default function Page() {
666674 < div class = "w-full" >
667675 < Switch >
668676 < Match when = { working ( ) } >
669- { ( _ ) => {
670- const items = createMemo ( ( ) =>
671- assistantMessages ( ) . flatMap ( ( m ) => sync . data . part [ m . id ] ) ,
672- )
673- const finishedItems = createMemo ( ( ) =>
674- items ( ) . filter (
675- ( p ) =>
676- ( p ?. type === "text" && p . time ?. end ) ||
677- ( p ?. type === "reasoning" && p . time ?. end ) ||
678- ( p ?. type === "tool" && p . state . status === "completed" ) ,
679- ) ,
680- )
681-
682- const MINIMUM_DELAY = 800
683- const [ visibleCount , setVisibleCount ] = createSignal ( 1 )
684-
685- createEffect ( ( ) => {
686- const total = finishedItems ( ) . length
687- if ( total > visibleCount ( ) ) {
688- const timer = setTimeout ( ( ) => {
689- setVisibleCount ( ( prev ) => prev + 1 )
690- } , MINIMUM_DELAY )
691- onCleanup ( ( ) => clearTimeout ( timer ) )
692- } else if ( total < visibleCount ( ) ) {
693- setVisibleCount ( total )
694- }
695- } )
696-
697- const translateY = createMemo ( ( ) => {
698- const total = visibleCount ( )
699- if ( total < 2 ) return "0px"
700- return `-${ ( total - 2 ) * 48 - 8 } px`
701- } )
702-
703- return (
704- < div class = "flex flex-col gap-3" >
705- < div
706- class = "h-36 overflow-hidden pointer-events-none
707- mask-alpha mask-y-from-66% mask-y-from-background-base mask-y-to-transparent"
708- >
709- < div
710- class = "w-full flex flex-col items-start self-stretch gap-2 py-10
711- transform transition-transform duration-500 ease-[cubic-bezier(0.22,1,0.36,1)]"
712- style = { { transform : `translateY(${ translateY ( ) } )` } }
713- >
714- < For each = { finishedItems ( ) } >
715- { ( part ) => {
716- const message = createMemo ( ( ) =>
717- sync . data . message [ part . sessionID ] . find (
718- ( m ) => m . id === part . messageID ,
719- ) ,
720- )
721- return (
722- < div class = "h-10 flex items-center w-full" >
723- < Switch >
724- < Match when = { part . type === "text" && part } >
725- { ( p ) => (
726- < div
727- textContent = { p ( ) . text }
728- class = "text-12-regular text-text-base whitespace-nowrap truncate w-full"
729- />
730- ) }
731- </ Match >
732- < Match when = { part . type === "reasoning" && part } >
733- { ( p ) => < Part message = { message ( ) ! } part = { p ( ) } /> }
734- </ Match >
735- < Match when = { part . type === "tool" && part } >
736- { ( p ) => < Part message = { message ( ) ! } part = { p ( ) } /> }
737- </ Match >
738- </ Switch >
739- </ div >
740- )
741- } }
742- </ For >
743- </ div >
744- </ div >
745- </ div >
746- )
747- } }
677+ < MessageProgress assistantMessages = { assistantMessages } />
748678 </ Match >
749679 < Match when = { ! working ( ) } >
750680 < Collapsible variant = "ghost" open = { expanded ( ) } onOpenChange = { setExpanded } >
0 commit comments