fix: reuse keys for tabs and sessions

This commit is contained in:
Shantur Rathore
2025-10-30 11:40:13 +00:00
parent 476834dab2
commit 4f583b12fe
2 changed files with 84 additions and 90 deletions

View File

@@ -18,15 +18,18 @@ const InstanceTabs: Component<InstanceTabsProps> = (props) => {
<div class="tab-bar tab-bar-instance"> <div class="tab-bar tab-bar-instance">
<div class="tab-container" role="tablist"> <div class="tab-container" role="tablist">
<div class="flex items-center gap-1 overflow-x-auto"> <div class="flex items-center gap-1 overflow-x-auto">
<For each={Array.from(props.instances.entries())}> <For each={Array.from(props.instances.keys())}>
{([id, instance]) => ( {(id) => {
<InstanceTab const instance = props.instances.get(id)
instance={instance} return (
active={id === props.activeInstanceId} <InstanceTab
onSelect={() => props.onSelect(id)} instance={instance!}
onClose={() => props.onClose(id)} active={id === props.activeInstanceId}
/> onSelect={() => props.onSelect(id)}
)} onClose={() => props.onClose(id)}
/>
)
}}
</For> </For>
<button <button
class="new-tab-button" class="new-tab-button"
@@ -37,7 +40,7 @@ const InstanceTabs: Component<InstanceTabsProps> = (props) => {
<Plus class="w-4 h-4" /> <Plus class="w-4 h-4" />
</button> </button>
</div> </div>
<Show when={Array.from(props.instances.entries()).length > 1}> <Show when={props.instances.size > 1}>
<div class="flex-shrink-0 ml-4"> <div class="flex-shrink-0 ml-4">
<KeyboardHint <KeyboardHint
shortcuts={[keyboardRegistry.get("instance-prev")!, keyboardRegistry.get("instance-next")!].filter( shortcuts={[keyboardRegistry.get("instance-prev")!, keyboardRegistry.get("instance-next")!].filter(

View File

@@ -152,42 +152,26 @@ const SessionList: Component<SessionListProps> = (props) => {
}) })
const sessionSections = createMemo(() => { const sessionSections = createMemo(() => {
const parentItems: SessionListItem[] = [] const parentIds: string[] = []
const childItems: SessionListItem[] = [] const childIds: string[] = []
for (const [id, session] of props.sessions.entries()) { for (const [id, session] of props.sessions.entries()) {
const item: SessionListItem = {
id,
title: session.title || "Untitled",
isActive: id === props.activeSessionId,
isParent: session.parentId === null,
onSelect: () => props.onSelect(id),
onClose: session.parentId === null ? () => props.onClose(id) : undefined,
}
if (session.parentId === null) { if (session.parentId === null) {
parentItems.push(item) parentIds.push(id)
} else { } else {
childItems.push(item) childIds.push(id)
} }
} }
childItems.sort((a, b) => { childIds.sort((a, b) => {
const sessionA = props.sessions.get(a.id) const sessionA = props.sessions.get(a)
const sessionB = props.sessions.get(b.id) const sessionB = props.sessions.get(b)
if (!sessionA || !sessionB) return 0 if (!sessionA || !sessionB) return 0
return sessionB.time.updated - sessionA.time.updated return sessionB.time.updated - sessionA.time.updated
}) })
parentItems.push({ parentIds.push("info")
id: "info", return { parentIds, childIds }
title: "Info",
isSpecial: true,
isActive: props.activeSessionId === "info",
onSelect: () => props.onSelect("info"),
})
return { parentItems, childItems }
}) })
return ( return (
@@ -221,30 +205,48 @@ const SessionList: Component<SessionListProps> = (props) => {
<div class="session-section-header px-3 py-2 text-xs font-semibold text-primary/70 uppercase tracking-wide"> <div class="session-section-header px-3 py-2 text-xs font-semibold text-primary/70 uppercase tracking-wide">
User Session & Info User Session & Info
</div> </div>
<For each={sessionSections().parentItems}> <For each={sessionSections().parentIds}>
{(item) => ( {(id) => {
<div class="session-list-item group"> if (id === "info") {
<button return (
class={`session-item-base ${ <div class="session-list-item group">
item.isActive ? "session-item-active" : "session-item-inactive" <button
} ${item.isSpecial ? "session-item-special" : ""}`} class={`session-item-base ${
onClick={item.onSelect} props.activeSessionId === "info" ? "session-item-active" : "session-item-inactive"
title={item.title} } session-item-special`}
role="button" onClick={() => props.onSelect("info")}
aria-selected={item.isActive} title="Info"
> role="button"
<Show when={item.isSpecial} fallback={<MessageSquare class="w-4 h-4 flex-shrink-0" />}> aria-selected={props.activeSessionId === "info"}
<Info class="w-4 h-4 flex-shrink-0" /> >
</Show> <Info class="w-4 h-4 flex-shrink-0" />
<span class="session-item-title truncate">Info</span>
</button>
</div>
)
}
<span class="session-item-title truncate">{item.title}</span> const session = props.sessions.get(id)
if (!session) return null
<Show when={!item.isSpecial && item.onClose}> return (
<div class="session-list-item group">
<button
class={`session-item-base ${
id === props.activeSessionId ? "session-item-active" : "session-item-inactive"
}`}
onClick={() => props.onSelect(id)}
title={session.title || "Untitled"}
role="button"
aria-selected={id === props.activeSessionId}
>
<MessageSquare class="w-4 h-4 flex-shrink-0" />
<span class="session-item-title truncate">{session.title || "Untitled"}</span>
<span <span
class="session-item-close opacity-0 group-hover:opacity-100 hover:bg-status-error hover:text-white rounded p-0.5 transition-all" class="session-item-close opacity-0 group-hover:opacity-100 hover:bg-status-error hover:text-white rounded p-0.5 transition-all"
onClick={(event) => { onClick={(event) => {
event.stopPropagation() event.stopPropagation()
item.onClose?.() props.onClose(id)
}} }}
role="button" role="button"
tabIndex={0} tabIndex={0}
@@ -252,51 +254,40 @@ const SessionList: Component<SessionListProps> = (props) => {
> >
<X class="w-3 h-3" /> <X class="w-3 h-3" />
</span> </span>
</Show> </button>
</button> </div>
</div> )
)} }}
</For> </For>
</div> </div>
<Show when={sessionSections().childItems.length > 0}> <Show when={sessionSections().childIds.length > 0}>
<div class="session-section"> <div class="session-section">
<div class="session-section-header px-3 py-2 text-xs font-semibold text-primary/70 uppercase tracking-wide"> <div class="session-section-header px-3 py-2 text-xs font-semibold text-primary/70 uppercase tracking-wide">
Agent Sessions Agent Sessions
</div> </div>
<For each={sessionSections().childItems}> <For each={sessionSections().childIds}>
{(item) => ( {(id) => {
<div class="session-list-item group"> const session = props.sessions.get(id)
<button if (!session) return null
class={`session-item-base ${
item.isActive ? "session-item-active" : "session-item-inactive"
} ${item.isSpecial ? "session-item-special" : ""}`}
onClick={item.onSelect}
title={item.title}
role="button"
aria-selected={item.isActive}
>
<MessageSquare class="w-4 h-4 flex-shrink-0" />
<span class="session-item-title truncate">{item.title}</span> return (
<div class="session-list-item group">
<Show when={!item.isSpecial && item.onClose}> <button
<span class={`session-item-base ${
class="session-item-close opacity-0 group-hover:opacity-100 hover:bg-status-error hover:text-white rounded p-0.5 transition-all" id === props.activeSessionId ? "session-item-active" : "session-item-inactive"
onClick={(event) => { }`}
event.stopPropagation() onClick={() => props.onSelect(id)}
item.onClose?.() title={session.title || "Untitled"}
}} role="button"
role="button" aria-selected={id === props.activeSessionId}
tabIndex={0} >
aria-label="Close session" <MessageSquare class="w-4 h-4 flex-shrink-0" />
> <span class="session-item-title truncate">{session.title || "Untitled"}</span>
<X class="w-3 h-3" /> </button>
</span> </div>
</Show> )
</button> }}
</div>
)}
</For> </For>
</div> </div>
</Show> </Show>