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

This commit is contained in:
Rhys Sullivan 2025-12-11 18:57:03 -08:00 committed by GitHub
parent a5cb4e41f5
commit 11efda3f5c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 15 additions and 1 deletions

View file

@ -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)

View file

@ -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}