fix(app): reveal tab avatar on hover (#33824)

Co-authored-by: Test <test@opencode.test>
This commit is contained in:
opencode-agent[bot]
2026-06-25 15:54:44 +08:00
committed by GitHub
co-authored by Test
parent b44af11e03
commit 5471d2a769
2 changed files with 11 additions and 39 deletions
@@ -27,7 +27,17 @@ export function SessionTabAvatar(props: {
/>
}
>
<SessionProgressIndicatorV2 class="shrink-0" />
<span class="relative block size-4 shrink-0">
<SessionProgressIndicatorV2 class="absolute inset-0 group-hover:invisible" />
<span class="invisible absolute inset-0 group-hover:visible">
<ProjectAvatar
fallback={displayName(props.project ?? { worktree: props.directory })}
src={getProjectAvatarSource(props.project?.id, props.project?.icon)}
variant={getProjectAvatarVariant(props.project?.icon?.color)}
unread={state.unread()}
/>
</span>
</span>
</Show>
)
}
@@ -34,7 +34,6 @@ import { IconButton } from "@opencode-ai/ui/icon-button"
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
import { Dialog } from "@opencode-ai/ui/dialog"
import { InlineInput } from "@opencode-ai/ui/inline-input"
import { Spinner } from "@opencode-ai/ui/spinner"
import { SessionRetry } from "@opencode-ai/session-ui/session-retry"
import { ScrollView } from "@opencode-ai/ui/scroll-view"
import { StickyAccordionHeader } from "@opencode-ai/ui/sticky-accordion-header"
@@ -66,9 +65,7 @@ import { legacySessionHref, requireServerKey, sessionHref } from "@/utils/sessio
import { useSDK } from "@/context/sdk"
import { useSync } from "@/context/sync"
import { notifySessionTabsRemoved } from "@/components/titlebar-session-events"
import { messageAgentColor } from "@/utils/agent"
import { sessionTitle } from "@/utils/session-title"
import { makeTimer } from "@solid-primitives/timer"
import { scheduleConnectedMeasure } from "./measure"
import { createTimelineProjection } from "./projection"
import { MessageComment, SummaryDiff, TimelineRow, TimelineRowMap } from "./rows"
@@ -277,25 +274,7 @@ export function MessageTimeline(props: {
if (!id) return idle
return sync().data.session_status[id] ?? idle
})
const working = createMemo(() => sessionStatus().type !== "idle")
const sessionMessages = createMemo(() => (sessionID() ? (sync().data.message[sessionID()!] ?? []) : []))
const tint = createMemo(() => messageAgentColor(sessionMessages(), sync().data.agent))
const [timeoutDone, setTimeoutDone] = createSignal(true)
const workingStatus = createMemo<"hidden" | "showing" | "hiding">((prev) => {
if (working()) return "showing"
if (prev === "showing" || !timeoutDone()) return "hiding"
return "hidden"
})
createEffect(() => {
if (workingStatus() !== "hiding") return
setTimeoutDone(false)
makeTimer(() => setTimeoutDone(true), 260, setTimeout)
})
const info = createMemo(() => {
const id = sessionID()
if (!id) return
@@ -1338,23 +1317,6 @@ export function MessageTimeline(props: {
/
</span>
</Show>
<div
class="shrink-0 flex items-center justify-center overflow-hidden transition-[width,margin] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)]"
style={{
width: working() ? "16px" : "0px",
"margin-right": working() ? "8px" : "0px",
}}
aria-hidden="true"
>
<Show when={workingStatus() !== "hidden"}>
<div
class="transition-opacity duration-200 ease-out"
classList={{ "opacity-0": workingStatus() === "hiding" }}
>
<Spinner class="size-4" style={{ color: tint() ?? "var(--icon-interactive-base)" }} />
</div>
</Show>
</div>
<Show when={childTitle() || title.editing}>
<Show
when={title.editing}