feat(desktop): working indicator on project sidebar (#26223)

This commit is contained in:
Erik Demaine
2026-05-07 23:42:39 -04:00
committed by GitHub
parent 114eeb21dc
commit 2ba9aa2196
3 changed files with 21 additions and 3 deletions

View File

@@ -26,7 +26,12 @@ export function getProjectAvatarSource(id?: string, icon?: { color?: string; url
return icon?.url return icon?.url
} }
export const ProjectIcon = (props: { project: LocalProject; class?: string; notify?: boolean }): JSX.Element => { export const ProjectIcon = (props: {
project: LocalProject
class?: string
notify?: boolean
working?: boolean
}): JSX.Element => {
const globalSync = useGlobalSync() const globalSync = useGlobalSync()
const notification = useNotification() const notification = useNotification()
const permission = usePermission() const permission = usePermission()
@@ -65,6 +70,11 @@ export const ProjectIcon = (props: { project: LocalProject; class?: string; noti
}} }}
/> />
</Show> </Show>
<Show when={props.working}>
<div class="absolute bottom-px right-px size-3 rounded-full bg-background-base z-10 flex items-center justify-center">
<Spinner class="size-[9px]" />
</div>
</Show>
</div> </div>
) )
} }

View File

@@ -56,6 +56,7 @@ const ProjectTile = (props: {
sidebarHovering: Accessor<boolean> sidebarHovering: Accessor<boolean>
selected: Accessor<boolean> selected: Accessor<boolean>
active: Accessor<boolean> active: Accessor<boolean>
isWorking: Accessor<boolean>
overlay: Accessor<boolean> overlay: Accessor<boolean>
suppressHover: Accessor<boolean> suppressHover: Accessor<boolean>
dirs: Accessor<string[]> dirs: Accessor<string[]>
@@ -143,7 +144,7 @@ const ProjectTile = (props: {
}} }}
onBlur={() => props.setOpen(false)} onBlur={() => props.setOpen(false)}
> >
<ProjectIcon project={props.project} notify /> <ProjectIcon project={props.project} notify working={props.isWorking()} />
</ContextMenu.Trigger> </ContextMenu.Trigger>
<ContextMenu.Portal> <ContextMenu.Portal>
<ContextMenu.Content> <ContextMenu.Content>
@@ -301,6 +302,12 @@ export const SortableProject = (props: {
} }
const projectStore = createMemo(() => globalSync.child(props.project.worktree, { bootstrap: false })[0]) const projectStore = createMemo(() => globalSync.child(props.project.worktree, { bootstrap: false })[0])
const isWorking = createMemo(() =>
dirs().some((directory) => {
const [store] = globalSync.child(directory, { bootstrap: false })
return Object.values(store.session_status).some((status) => status?.type === "busy" || status?.type === "retry")
}),
)
const projectSessions = createMemo(() => sortedRootSessions(projectStore(), props.sortNow())) const projectSessions = createMemo(() => sortedRootSessions(projectStore(), props.sortNow()))
const workspaceSessions = (directory: string) => { const workspaceSessions = (directory: string) => {
const [data] = globalSync.child(directory, { bootstrap: false }) const [data] = globalSync.child(directory, { bootstrap: false })
@@ -313,6 +320,7 @@ export const SortableProject = (props: {
sidebarHovering={props.ctx.sidebarHovering} sidebarHovering={props.ctx.sidebarHovering}
selected={selected} selected={selected}
active={active} active={active}
isWorking={isWorking}
overlay={overlay} overlay={overlay}
suppressHover={() => state.suppressHover} suppressHover={() => state.suppressHover}
dirs={dirs} dirs={dirs}

View File

@@ -154,7 +154,7 @@ export function DialogSessionList() {
} }
const isDeleting = toDelete() === x.id const isDeleting = toDelete() === x.id
const status = sync.data.session_status?.[x.id] const status = sync.data.session_status?.[x.id]
const isWorking = status?.type === "busy" const isWorking = status?.type === "busy" || status?.type === "retry"
return { return {
title: isDeleting ? `Press ${deleteHint()} again to confirm` : x.title, title: isDeleting ? `Press ${deleteHint()} again to confirm` : x.title,
bg: isDeleting ? theme.error : undefined, bg: isDeleting ? theme.error : undefined,