Просмотр исходного кода

[feat]: show indicator for in progress chats in the sessions list (#5417)

Rhys Sullivan 2 месяцев назад
Родитель
Сommit
11efda3f5c

+ 6 - 0
packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx

@@ -8,6 +8,7 @@ import { Keybind } from "@/util/keybind"
 import { useTheme } from "../context/theme"
 import { useTheme } from "../context/theme"
 import { useSDK } from "../context/sdk"
 import { useSDK } from "../context/sdk"
 import { DialogSessionRename } from "./dialog-session-rename"
 import { DialogSessionRename } from "./dialog-session-rename"
+import "opentui-spinner/solid"
 
 
 export function DialogSessionList() {
 export function DialogSessionList() {
   const dialog = useDialog()
   const dialog = useDialog()
@@ -22,6 +23,8 @@ export function DialogSessionList() {
 
 
   const currentSessionID = createMemo(() => (route.data.type === "session" ? route.data.sessionID : undefined))
   const currentSessionID = createMemo(() => (route.data.type === "session" ? route.data.sessionID : undefined))
 
 
+  const spinnerFrames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
+
   const options = createMemo(() => {
   const options = createMemo(() => {
     const today = new Date().toDateString()
     const today = new Date().toDateString()
     return sync.data.session
     return sync.data.session
@@ -34,12 +37,15 @@ export function DialogSessionList() {
           category = "Today"
           category = "Today"
         }
         }
         const isDeleting = toDelete() === x.id
         const isDeleting = toDelete() === x.id
+        const status = sync.data.session_status[x.id]
+        const isWorking = status?.type === "busy"
         return {
         return {
           title: isDeleting ? `Press ${deleteKeybind} again to confirm` : x.title,
           title: isDeleting ? `Press ${deleteKeybind} again to confirm` : x.title,
           bg: isDeleting ? theme.error : undefined,
           bg: isDeleting ? theme.error : undefined,
           value: x.id,
           value: x.id,
           category,
           category,
           footer: Locale.time(x.time.updated),
           footer: Locale.time(x.time.updated),
+          gutter: isWorking ? <spinner frames={spinnerFrames} interval={80} color={theme.primary} /> : undefined,
         }
         }
       })
       })
       .slice(0, 150)
       .slice(0, 150)

+ 9 - 1
packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx

@@ -36,6 +36,7 @@ export interface DialogSelectOption<T = any> {
   category?: string
   category?: string
   disabled?: boolean
   disabled?: boolean
   bg?: RGBA
   bg?: RGBA
+  gutter?: JSX.Element
   onSelect?: (ctx: DialogContext, trigger?: "prompt") => void
   onSelect?: (ctx: DialogContext, trigger?: "prompt") => void
 }
 }
 
 
@@ -239,7 +240,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
                         moveTo(index)
                         moveTo(index)
                       }}
                       }}
                       backgroundColor={active() ? (option.bg ?? theme.primary) : RGBA.fromInts(0, 0, 0, 0)}
                       backgroundColor={active() ? (option.bg ?? theme.primary) : RGBA.fromInts(0, 0, 0, 0)}
-                      paddingLeft={current() ? 1 : 3}
+                      paddingLeft={current() || option.gutter ? 1 : 3}
                       paddingRight={3}
                       paddingRight={3}
                       gap={1}
                       gap={1}
                     >
                     >
@@ -249,6 +250,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
                         description={option.description !== category ? option.description : undefined}
                         description={option.description !== category ? option.description : undefined}
                         active={active()}
                         active={active()}
                         current={current()}
                         current={current()}
+                        gutter={option.gutter}
                       />
                       />
                     </box>
                     </box>
                   )
                   )
@@ -282,6 +284,7 @@ function Option(props: {
   active?: boolean
   active?: boolean
   current?: boolean
   current?: boolean
   footer?: JSX.Element | string
   footer?: JSX.Element | string
+  gutter?: JSX.Element
   onMouseOver?: () => void
   onMouseOver?: () => void
 }) {
 }) {
   const { theme } = useTheme()
   const { theme } = useTheme()
@@ -294,6 +297,11 @@ function Option(props: {
         </text>
         </text>
       </Show>
       </Show>
+      <Show when={!props.current && props.gutter}>
+        <box flexShrink={0} marginRight={0.5}>
+          {props.gutter}
+        </box>
+      </Show>
       <text
       <text
         flexGrow={1}
         flexGrow={1}
         fg={props.active ? fg : props.current ? theme.primary : theme.text}
         fg={props.active ? fg : props.current ? theme.primary : theme.text}