{announcement()}
diff --git a/packages/kilo-vscode/webview-ui/src/components/chat/SessionTabSwitcher.tsx b/packages/kilo-vscode/webview-ui/src/components/chat/SessionTabSwitcher.tsx
new file mode 100644
index 00000000000..3ba5618a02b
--- /dev/null
+++ b/packages/kilo-vscode/webview-ui/src/components/chat/SessionTabSwitcher.tsx
@@ -0,0 +1,177 @@
+import { Icon } from "@kilocode/kilo-ui/icon"
+import { IconButton } from "@kilocode/kilo-ui/icon-button"
+import { List } from "@kilocode/kilo-ui/list"
+import type { ListRef } from "@kilocode/kilo-ui/list"
+import { Popover } from "@kilocode/kilo-ui/popover"
+import { Spinner } from "@kilocode/kilo-ui/spinner"
+import { Tooltip } from "@kilocode/kilo-ui/tooltip"
+import { Show, createEffect, createMemo, createSignal, type Component, type JSX } from "solid-js"
+
+interface SessionTabSwitcherItem {
+ id: string
+ title: string
+ active: boolean
+ busy: boolean
+ pending: boolean
+}
+
+interface SessionTabSwitcherProps {
+ items: () => SessionTabSwitcherItem[]
+ labels: {
+ open: string
+ search: string
+ close: string
+ current: string
+ pending: string
+ busy: string
+ }
+ onSelect: (id: string) => void
+ onRestore: () => void
+ onClose: (id: string) => void
+ defaultOpen?: boolean
+ portal?: boolean
+}
+
+export const SessionTabSwitcher: Component
= (props) => {
+ const [open, setOpen] = createSignal(props.defaultOpen ?? false)
+ const [notice, setNotice] = createSignal("")
+ let list: ListRef | undefined
+ let root: HTMLDivElement | undefined
+
+ const current = createMemo(() => props.items().find((item) => item.active))
+
+ const focus = (reset = false) =>
+ queueMicrotask(() => {
+ if (reset) list?.setFilter("")
+ root?.querySelector("input")?.focus({ preventScroll: true })
+ })
+
+ createEffect(() => {
+ if (open()) focus(true)
+ })
+
+ const select = (item: SessionTabSwitcherItem) => {
+ setOpen(false)
+ props.onSelect(item.id)
+ // Restore the prompt after the closing popover finishes its current event.
+ queueMicrotask(props.onRestore)
+ }
+
+ const remove = (item: SessionTabSwitcherItem) => {
+ const last = props.items().length === 2
+ props.onClose(item.id)
+ setNotice(`${props.labels.close}: ${item.title}`)
+ if (last) {
+ queueMicrotask(props.onRestore)
+ return
+ }
+ focus()
+ }
+
+ const key = (event: KeyboardEvent, item: SessionTabSwitcherItem | undefined) => {
+ const target = event.target
+ if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) return
+ const node = event.currentTarget
+ const id = node instanceof HTMLElement ? node.dataset.key : undefined
+ // With no initial cursor, a row reached by Tab may not be the List's active item.
+ const value = props.items().find((row) => row.id === id) ?? item
+ if (!value) return
+ if (event.key === "Enter") {
+ event.preventDefault()
+ select(value)
+ return
+ }
+ if (event.key !== "Delete" && event.key !== "Backspace") return
+ event.preventDefault()
+ remove(value)
+ }
+
+ const wrap = (item: SessionTabSwitcherItem, node: JSX.Element) => (
+
+ {node}
+ {
+ event.preventDefault()
+ event.stopPropagation()
+ remove(item)
+ }}
+ />
+
+ )
+
+ return (
+
+
+
+ )
+}
diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts
index 5a04fd4eb7c..bef28b4e812 100644
--- a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts
+++ b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts
@@ -1102,6 +1102,11 @@ export const dict = {
"session.showHistory": "عرض السجل",
"session.search.placeholder": "البحث في الجلسات...",
"session.empty": "لا توجد جلسات بعد. انقر + لبدء محادثة جديدة.",
+ "session.tabs.switcher.open": "إظهار التبويبات المفتوحة",
+ "session.tabs.switcher.search": "البحث في التبويبات المفتوحة...",
+ "session.tabs.switcher.current": "الحالي",
+ "session.tabs.switcher.pending": "جديد",
+ "session.tabs.switcher.busy": "جارٍ العمل",
"session.tab.local": "محلي",
"session.tab.cloud": "السحابة",
"session.cloud.repoOnly": "هذا المستودع فقط",
diff --git a/packages/kilo-vscode/webview-ui/src/i18n/br.ts b/packages/kilo-vscode/webview-ui/src/i18n/br.ts
index f153fec3c12..e76f7353d76 100644
--- a/packages/kilo-vscode/webview-ui/src/i18n/br.ts
+++ b/packages/kilo-vscode/webview-ui/src/i18n/br.ts
@@ -1123,6 +1123,11 @@ export const dict = {
"session.showHistory": "Mostrar Histórico",
"session.search.placeholder": "Buscar sessões...",
"session.empty": "Nenhuma sessão ainda. Clique + para iniciar uma nova conversa.",
+ "session.tabs.switcher.open": "Mostrar abas abertas",
+ "session.tabs.switcher.search": "Buscar abas abertas...",
+ "session.tabs.switcher.current": "Atual",
+ "session.tabs.switcher.pending": "Nova",
+ "session.tabs.switcher.busy": "Trabalhando",
"session.tab.local": "Local",
"session.tab.cloud": "Nuvem",
"session.cloud.repoOnly": "Apenas este repositório",
diff --git a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts
index 8a83fd8be41..91a11fc570a 100644
--- a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts
+++ b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts
@@ -1170,6 +1170,11 @@ export const dict = {
"session.showHistory": "Prikaži historiju",
"session.search.placeholder": "Pretraži sesije...",
"session.empty": "Još nema sesija. Kliknite + za početak novog razgovora.",
+ "session.tabs.switcher.open": "Prikaži otvorene kartice",
+ "session.tabs.switcher.search": "Pretraži otvorene kartice...",
+ "session.tabs.switcher.current": "Trenutno",
+ "session.tabs.switcher.pending": "Novo",
+ "session.tabs.switcher.busy": "Radi",
"session.tab.local": "Lokalno",
"session.tab.cloud": "Oblak",
"session.cloud.repoOnly": "Samo ovaj repozitorij",
diff --git a/packages/kilo-vscode/webview-ui/src/i18n/da.ts b/packages/kilo-vscode/webview-ui/src/i18n/da.ts
index d4c0d06ce86..16f9fb7c51e 100644
--- a/packages/kilo-vscode/webview-ui/src/i18n/da.ts
+++ b/packages/kilo-vscode/webview-ui/src/i18n/da.ts
@@ -1162,6 +1162,11 @@ export const dict = {
"session.showHistory": "Vis historik",
"session.search.placeholder": "Søg sessioner...",
"session.empty": "Ingen sessioner endnu. Klik + for at starte en ny samtale.",
+ "session.tabs.switcher.open": "Vis åbne faner",
+ "session.tabs.switcher.search": "Søg i åbne faner...",
+ "session.tabs.switcher.current": "Aktuel",
+ "session.tabs.switcher.pending": "Ny",
+ "session.tabs.switcher.busy": "Arbejder",
"session.tab.local": "Lokal",
"session.tab.cloud": "Sky",
"session.cloud.repoOnly": "Kun dette repository",
diff --git a/packages/kilo-vscode/webview-ui/src/i18n/de.ts b/packages/kilo-vscode/webview-ui/src/i18n/de.ts
index bf036e817cf..de3a32ae916 100644
--- a/packages/kilo-vscode/webview-ui/src/i18n/de.ts
+++ b/packages/kilo-vscode/webview-ui/src/i18n/de.ts
@@ -1182,6 +1182,11 @@ export const dict = {
"session.showHistory": "Verlauf anzeigen",
"session.search.placeholder": "Sitzungen suchen...",
"session.empty": "Noch keine Sitzungen. Klicke + um eine neue Unterhaltung zu starten.",
+ "session.tabs.switcher.open": "Offene Tabs anzeigen",
+ "session.tabs.switcher.search": "Offene Tabs suchen...",
+ "session.tabs.switcher.current": "Aktuell",
+ "session.tabs.switcher.pending": "Neu",
+ "session.tabs.switcher.busy": "In Arbeit",
"session.tab.local": "Lokal",
"session.tab.cloud": "Cloud",
"session.cloud.repoOnly": "Nur dieses Repository",
diff --git a/packages/kilo-vscode/webview-ui/src/i18n/en.ts b/packages/kilo-vscode/webview-ui/src/i18n/en.ts
index bd742755d4f..a9c1169571a 100644
--- a/packages/kilo-vscode/webview-ui/src/i18n/en.ts
+++ b/packages/kilo-vscode/webview-ui/src/i18n/en.ts
@@ -1077,6 +1077,11 @@ export const dict = {
"session.history.sources": "History source",
"session.search.placeholder": "Search sessions...",
"session.empty": "No sessions yet. Click + to start a new conversation.",
+ "session.tabs.switcher.open": "Show open tabs",
+ "session.tabs.switcher.search": "Search open tabs...",
+ "session.tabs.switcher.current": "Current",
+ "session.tabs.switcher.pending": "New",
+ "session.tabs.switcher.busy": "Working",
"session.tab.local": "Local",
"session.tab.cloud": "Cloud",
"session.cloud.repoOnly": "Only this repository",
diff --git a/packages/kilo-vscode/webview-ui/src/i18n/es.ts b/packages/kilo-vscode/webview-ui/src/i18n/es.ts
index 415c87f2acb..487fefbe2cf 100644
--- a/packages/kilo-vscode/webview-ui/src/i18n/es.ts
+++ b/packages/kilo-vscode/webview-ui/src/i18n/es.ts
@@ -1174,6 +1174,11 @@ export const dict = {
"session.showHistory": "Mostrar historial",
"session.search.placeholder": "Buscar sesiones...",
"session.empty": "Aún no hay sesiones. Haz clic en + para iniciar una nueva conversación.",
+ "session.tabs.switcher.open": "Mostrar pestañas abiertas",
+ "session.tabs.switcher.search": "Buscar pestañas abiertas...",
+ "session.tabs.switcher.current": "Actual",
+ "session.tabs.switcher.pending": "Nueva",
+ "session.tabs.switcher.busy": "Trabajando",
"session.tab.local": "Local",
"session.tab.cloud": "Nube",
"session.cloud.repoOnly": "Solo este repositorio",
diff --git a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts
index 05fac462ea6..ac24daab2a6 100644
--- a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts
+++ b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts
@@ -1181,6 +1181,11 @@ export const dict = {
"session.showHistory": "Afficher l'historique",
"session.search.placeholder": "Rechercher des sessions...",
"session.empty": "Aucune session pour l'instant. Cliquez + pour démarrer une nouvelle conversation.",
+ "session.tabs.switcher.open": "Afficher les onglets ouverts",
+ "session.tabs.switcher.search": "Rechercher dans les onglets ouverts...",
+ "session.tabs.switcher.current": "Actuel",
+ "session.tabs.switcher.pending": "Nouveau",
+ "session.tabs.switcher.busy": "En cours",
"session.tab.local": "Local",
"session.tab.cloud": "Cloud",
"session.cloud.repoOnly": "Uniquement ce dépôt",
diff --git a/packages/kilo-vscode/webview-ui/src/i18n/it.ts b/packages/kilo-vscode/webview-ui/src/i18n/it.ts
index 43f32b66be8..2eaba4f7de9 100644
--- a/packages/kilo-vscode/webview-ui/src/i18n/it.ts
+++ b/packages/kilo-vscode/webview-ui/src/i18n/it.ts
@@ -928,6 +928,11 @@ export const dict = {
"session.history.sources": "Origine cronologia",
"session.search.placeholder": "Cerca sessioni...",
"session.empty": "Ancora nessuna sessione. Fai clic su + per avviare una nuova conversazione.",
+ "session.tabs.switcher.open": "Mostra schede aperte",
+ "session.tabs.switcher.search": "Cerca schede aperte...",
+ "session.tabs.switcher.current": "Corrente",
+ "session.tabs.switcher.pending": "Nuova",
+ "session.tabs.switcher.busy": "In corso",
"session.tab.local": "Locale",
"session.tab.cloud": "Cloud",
"session.cloud.repoOnly": "Solo questa repository",
diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts
index 8b63bbee619..e19b1ded6ee 100644
--- a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts
+++ b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts
@@ -1157,6 +1157,11 @@ export const dict = {
"session.showHistory": "履歴を表示",
"session.search.placeholder": "セッションを検索...",
"session.empty": "セッションがありません。+ をクリックして新しい会話を始めましょう。",
+ "session.tabs.switcher.open": "開いているタブを表示",
+ "session.tabs.switcher.search": "開いているタブを検索...",
+ "session.tabs.switcher.current": "現在",
+ "session.tabs.switcher.pending": "新規",
+ "session.tabs.switcher.busy": "作業中",
"session.tab.local": "ローカル",
"session.tab.cloud": "クラウド",
"session.cloud.repoOnly": "このリポジトリのみ",
diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts
index edcfa35ae65..d52d7e27b37 100644
--- a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts
+++ b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts
@@ -1111,6 +1111,11 @@ export const dict = {
"session.showHistory": "기록 보기",
"session.search.placeholder": "세션 검색...",
"session.empty": "아직 세션이 없습니다. +를 클릭하여 새 대화를 시작하세요.",
+ "session.tabs.switcher.open": "열린 탭 표시",
+ "session.tabs.switcher.search": "열린 탭 검색...",
+ "session.tabs.switcher.current": "현재",
+ "session.tabs.switcher.pending": "새 항목",
+ "session.tabs.switcher.busy": "작업 중",
"session.tab.local": "로컬",
"session.tab.cloud": "클라우드",
"session.cloud.repoOnly": "이 저장소만",
diff --git a/packages/kilo-vscode/webview-ui/src/i18n/nl.ts b/packages/kilo-vscode/webview-ui/src/i18n/nl.ts
index 3508d0bd94a..1d0c12e55bb 100644
--- a/packages/kilo-vscode/webview-ui/src/i18n/nl.ts
+++ b/packages/kilo-vscode/webview-ui/src/i18n/nl.ts
@@ -1117,6 +1117,11 @@ export const dict = {
"session.showHistory": "Geschiedenis weergeven",
"session.search.placeholder": "Zoek sessies...",
"session.empty": "Nog geen sessies. Klik op + om een nieuw gesprek te starten.",
+ "session.tabs.switcher.open": "Open tabbladen tonen",
+ "session.tabs.switcher.search": "Open tabbladen zoeken...",
+ "session.tabs.switcher.current": "Huidig",
+ "session.tabs.switcher.pending": "Nieuw",
+ "session.tabs.switcher.busy": "Bezig",
"session.tab.local": "Lokaal",
"session.tab.cloud": "Cloud",
"session.cloud.repoOnly": "Alleen deze repository",
diff --git a/packages/kilo-vscode/webview-ui/src/i18n/no.ts b/packages/kilo-vscode/webview-ui/src/i18n/no.ts
index 82375c64f6b..6a7cdb80906 100644
--- a/packages/kilo-vscode/webview-ui/src/i18n/no.ts
+++ b/packages/kilo-vscode/webview-ui/src/i18n/no.ts
@@ -1123,6 +1123,11 @@ export const dict = {
"session.showHistory": "Vis historikk",
"session.search.placeholder": "Søk i sesjoner...",
"session.empty": "Ingen sesjoner ennå. Klikk + for å starte en ny samtale.",
+ "session.tabs.switcher.open": "Vis åpne faner",
+ "session.tabs.switcher.search": "Søk i åpne faner...",
+ "session.tabs.switcher.current": "Gjeldende",
+ "session.tabs.switcher.pending": "Ny",
+ "session.tabs.switcher.busy": "Jobber",
"session.tab.local": "Lokal",
"session.tab.cloud": "Sky",
"session.cloud.repoOnly": "Kun dette repositoriet",
diff --git a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts
index 762e3499eca..9bc5cce9c79 100644
--- a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts
+++ b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts
@@ -1121,6 +1121,11 @@ export const dict = {
"session.showHistory": "Pokaż historię",
"session.search.placeholder": "Szukaj sesji...",
"session.empty": "Brak sesji. Kliknij + aby rozpocząć nową rozmowę.",
+ "session.tabs.switcher.open": "Pokaż otwarte karty",
+ "session.tabs.switcher.search": "Szukaj otwartych kart...",
+ "session.tabs.switcher.current": "Bieżąca",
+ "session.tabs.switcher.pending": "Nowa",
+ "session.tabs.switcher.busy": "Pracuje",
"session.tab.local": "Lokalny",
"session.tab.cloud": "Chmura",
"session.cloud.repoOnly": "Tylko to repozytorium",
diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts
index e8f25868876..c39ad113591 100644
--- a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts
+++ b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts
@@ -1167,6 +1167,11 @@ export const dict = {
"session.showHistory": "Показать историю",
"session.search.placeholder": "Поиск сессий...",
"session.empty": "Сессий пока нет. Нажмите + чтобы начать новый разговор.",
+ "session.tabs.switcher.open": "Показать открытые вкладки",
+ "session.tabs.switcher.search": "Поиск открытых вкладок...",
+ "session.tabs.switcher.current": "Текущая",
+ "session.tabs.switcher.pending": "Новая",
+ "session.tabs.switcher.busy": "В работе",
"session.tab.local": "Локальный",
"session.tab.cloud": "Облако",
"session.cloud.repoOnly": "Только этот репозиторий",
diff --git a/packages/kilo-vscode/webview-ui/src/i18n/th.ts b/packages/kilo-vscode/webview-ui/src/i18n/th.ts
index 9b4db932452..660f6d6cff5 100644
--- a/packages/kilo-vscode/webview-ui/src/i18n/th.ts
+++ b/packages/kilo-vscode/webview-ui/src/i18n/th.ts
@@ -1151,6 +1151,11 @@ export const dict = {
"session.showHistory": "แสดงประวัติ",
"session.search.placeholder": "ค้นหาเซสชัน...",
"session.empty": "ยังไม่มีเซสชัน คลิก + เพื่อเริ่มการสนทนาใหม่",
+ "session.tabs.switcher.open": "แสดงแท็บที่เปิดอยู่",
+ "session.tabs.switcher.search": "ค้นหาแท็บที่เปิดอยู่...",
+ "session.tabs.switcher.current": "ปัจจุบัน",
+ "session.tabs.switcher.pending": "ใหม่",
+ "session.tabs.switcher.busy": "กำลังทำงาน",
"session.tab.local": "ในเครื่อง",
"session.tab.cloud": "คลาวด์",
"session.cloud.repoOnly": "เฉพาะรีโพซิทอรีนี้",
diff --git a/packages/kilo-vscode/webview-ui/src/i18n/tr.ts b/packages/kilo-vscode/webview-ui/src/i18n/tr.ts
index 997798a9f05..87e6850d0a8 100644
--- a/packages/kilo-vscode/webview-ui/src/i18n/tr.ts
+++ b/packages/kilo-vscode/webview-ui/src/i18n/tr.ts
@@ -1113,6 +1113,11 @@ export const dict = {
"session.showHistory": "Geçmişi Göster",
"session.search.placeholder": "Oturum ara...",
"session.empty": "Henüz oturum yok. Yeni bir sohbet başlatmak için + tıklayın.",
+ "session.tabs.switcher.open": "Açık sekmeleri göster",
+ "session.tabs.switcher.search": "Açık sekmelerde ara...",
+ "session.tabs.switcher.current": "Geçerli",
+ "session.tabs.switcher.pending": "Yeni",
+ "session.tabs.switcher.busy": "Çalışıyor",
"session.tab.local": "Local",
"session.tab.cloud": "Cloud",
"session.cloud.repoOnly": "Yalnızca bu depo",
diff --git a/packages/kilo-vscode/webview-ui/src/i18n/uk.ts b/packages/kilo-vscode/webview-ui/src/i18n/uk.ts
index c7edb20c28b..9fe8f6c066e 100644
--- a/packages/kilo-vscode/webview-ui/src/i18n/uk.ts
+++ b/packages/kilo-vscode/webview-ui/src/i18n/uk.ts
@@ -1114,6 +1114,11 @@ export const dict = {
"session.showHistory": "Показати історію",
"session.search.placeholder": "Пошук сесій...",
"session.empty": "Сесій поки немає. Натисніть +, щоб почати новий чат.",
+ "session.tabs.switcher.open": "Показати відкриті вкладки",
+ "session.tabs.switcher.search": "Пошук відкритих вкладок...",
+ "session.tabs.switcher.current": "Поточна",
+ "session.tabs.switcher.pending": "Нова",
+ "session.tabs.switcher.busy": "Працює",
"session.tab.local": "Локальний",
"session.tab.cloud": "Хмарний",
"session.cloud.repoOnly": "Лише цей репозиторій",
diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts
index 3444a21d8d9..aef89a31fd4 100644
--- a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts
+++ b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts
@@ -1127,6 +1127,11 @@ export const dict = {
"session.showHistory": "显示历史",
"session.search.placeholder": "搜索会话...",
"session.empty": "暂无会话。点击 + 开始新的对话。",
+ "session.tabs.switcher.open": "显示打开的标签页",
+ "session.tabs.switcher.search": "搜索打开的标签页...",
+ "session.tabs.switcher.current": "当前",
+ "session.tabs.switcher.pending": "新建",
+ "session.tabs.switcher.busy": "工作中",
"session.tab.local": "本地",
"session.tab.cloud": "云端",
"session.cloud.repoOnly": "仅此仓库",
diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts
index dfa2dd85f31..5e5599ce9fa 100644
--- a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts
+++ b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts
@@ -1088,6 +1088,11 @@ export const dict = {
"session.showHistory": "顯示歷史",
"session.search.placeholder": "搜尋工作階段...",
"session.empty": "尚無工作階段。點選 + 以開始新的對話。",
+ "session.tabs.switcher.open": "顯示開啟的分頁",
+ "session.tabs.switcher.search": "搜尋開啟的分頁...",
+ "session.tabs.switcher.current": "目前",
+ "session.tabs.switcher.pending": "新增",
+ "session.tabs.switcher.busy": "工作中",
"session.tab.local": "本機",
"session.tab.cloud": "雲端",
"session.cloud.repoOnly": "僅此儲存庫",
diff --git a/packages/kilo-vscode/webview-ui/src/stories/session-tabs.stories.tsx b/packages/kilo-vscode/webview-ui/src/stories/session-tabs.stories.tsx
new file mode 100644
index 00000000000..26337440548
--- /dev/null
+++ b/packages/kilo-vscode/webview-ui/src/stories/session-tabs.stories.tsx
@@ -0,0 +1,60 @@
+/** @jsxImportSource solid-js */
+
+import type { Meta, StoryObj } from "storybook-solidjs-vite"
+import { SessionTabSwitcher } from "../components/chat/SessionTabSwitcher"
+import { StoryProviders } from "./StoryProviders"
+
+const rows = [
+ { id: "refactor", title: "Refactor shared search menu styles", active: false, busy: false, pending: false },
+ { id: "current", title: "Run the extension test suite", active: true, busy: true, pending: false },
+ { id: "pending", title: "Untitled session", active: false, busy: false, pending: true },
+ { id: "idle", title: "Review keyboard navigation behavior", active: false, busy: false, pending: false },
+]
+
+const noop = () => {}
+const focus = () => document.querySelector('[data-slot="session-prompt-focus-target"]')?.focus()
+const meta: Meta = {
+ title: "Session Tabs",
+ parameters: { layout: "fullscreen" },
+}
+
+export default meta
+type Story = StoryObj
+
+export const SwitcherOpen: Story = {
+ name: "Session tab switcher — open",
+ render: () => (
+
+
+
+
+ rows}
+ labels={{
+ open: "Show open tabs",
+ search: "Search open tabs",
+ close: "Close tab",
+ current: "Current",
+ pending: "New",
+ busy: "Working",
+ }}
+ onSelect={noop}
+ onRestore={focus}
+ onClose={noop}
+ defaultOpen
+ portal={false}
+ />
+
+
+
+ ),
+}
diff --git a/packages/kilo-vscode/webview-ui/src/styles/chat.css b/packages/kilo-vscode/webview-ui/src/styles/chat.css
index e7fc13c8c6a..9bd9a1b1667 100644
--- a/packages/kilo-vscode/webview-ui/src/styles/chat.css
+++ b/packages/kilo-vscode/webview-ui/src/styles/chat.css
@@ -7,6 +7,7 @@
@import "./session-title-editor.css";
@import "./task-header.css";
+@import "./search-menu.css";
@import "./session-tabs.css";
@import "./chat-layout.css";
@import "./banners.css";
diff --git a/packages/kilo-vscode/webview-ui/src/styles/search-menu.css b/packages/kilo-vscode/webview-ui/src/styles/search-menu.css
new file mode 100644
index 00000000000..48671ea42bc
--- /dev/null
+++ b/packages/kilo-vscode/webview-ui/src/styles/search-menu.css
@@ -0,0 +1,92 @@
+/* Shared searchable popovers */
+
+.search-menu-trigger[data-component="icon-button"][data-expanded] {
+ background: var(--surface-base-hover);
+}
+
+.search-menu-popover[data-component="popover-content"] {
+ width: min(320px, calc(100vw - 24px));
+}
+
+.search-menu [data-component="list"] {
+ gap: 0;
+ padding: 8px 0 0;
+}
+
+.search-menu [data-slot="list-search-wrapper"] {
+ margin: 0 0 4px;
+ padding: 0 8px;
+}
+
+.search-menu [data-slot="list-search"] {
+ background: var(--surface-inset-base);
+}
+
+.search-menu [data-slot="list-scroll"] {
+ gap: 2px;
+}
+
+.search-menu [data-slot="list-item"][data-active="true"],
+.search-menu [data-slot="list-item"][data-selected="true"] {
+ background: color-mix(in srgb, var(--text-base) 10%, transparent);
+}
+
+.search-menu-row {
+ display: flex;
+ width: 100%;
+ min-width: 0;
+ align-items: center;
+ gap: 8px;
+}
+
+.search-menu-icon {
+ display: inline-flex;
+ width: 18px;
+ height: 18px;
+ flex-shrink: 0;
+ align-items: center;
+ justify-content: center;
+ color: var(--text-weak);
+}
+
+.search-menu-spinner {
+ width: 14px;
+ height: 14px;
+}
+
+.search-menu-copy {
+ display: flex;
+ min-width: 0;
+ flex: 1;
+ flex-direction: column;
+ gap: 1px;
+ text-align: start;
+}
+
+.search-menu-title,
+.search-menu-meta {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.search-menu-title {
+ color: var(--text-base);
+ font-size: var(--kilo-font-size-12);
+ font-weight: 500;
+ line-height: 1.35;
+}
+
+.search-menu-meta {
+ font-size: var(--kilo-font-size-10);
+ font-weight: 400;
+ line-height: 1.35;
+}
+
+.search-menu-status {
+ flex-shrink: 0;
+ margin-inline-start: auto;
+ color: var(--text-base);
+ font-size: var(--kilo-font-size-10);
+ font-weight: 400;
+}
diff --git a/packages/kilo-vscode/webview-ui/src/styles/session-tabs.css b/packages/kilo-vscode/webview-ui/src/styles/session-tabs.css
index 6524259ad87..b99cbfb38ec 100644
--- a/packages/kilo-vscode/webview-ui/src/styles/session-tabs.css
+++ b/packages/kilo-vscode/webview-ui/src/styles/session-tabs.css
@@ -228,6 +228,75 @@
color: var(--text-base);
}
+.session-tab-switcher-wrap {
+ display: flex;
+ align-items: center;
+ flex-shrink: 0;
+ margin-inline-start: 4px;
+ padding-inline: 6px 4px;
+ border-inline-start: 1px solid var(--border-weak-base);
+}
+
+.session-tab-switcher-popover[data-component="popover-content"] {
+ max-height: min(420px, calc(100vh - 64px));
+}
+
+.session-tab-switcher [data-component="list"] {
+ max-height: min(390px, calc(100vh - 88px));
+}
+
+.session-tab-switcher [data-slot="list-scroll"] {
+ max-height: 320px;
+ padding: 0 4px 4px;
+}
+
+.session-tab-switcher [data-slot="list-item"] {
+ min-height: 42px;
+ padding-block: 6px;
+ padding-inline: 8px 36px;
+}
+
+.session-tab-switcher [data-slot="list-item-selected-icon"] {
+ display: none;
+}
+
+.session-tab-switcher-item {
+ position: relative;
+ width: 100%;
+ min-width: 0;
+}
+
+.session-tab-switcher-meta {
+ color: var(--text-weak);
+}
+
+.session-tab-switcher-status {
+ color: var(--text-weak);
+ font-weight: 500;
+ line-height: 1;
+ text-transform: uppercase;
+}
+
+.session-tab-switcher-close[data-component="icon-button"] {
+ position: absolute;
+ inset-block-start: 50%;
+ inset-inline-end: 4px;
+ opacity: 0;
+ pointer-events: none;
+ transform: translateY(-50%);
+ transition: opacity 100ms ease;
+}
+
+.session-tab-switcher-item:hover .session-tab-switcher-close[data-component="icon-button"],
+.session-tab-switcher-item:focus-within .session-tab-switcher-close[data-component="icon-button"] {
+ opacity: 1;
+ pointer-events: auto;
+}
+
+.session-tab-switcher-close[data-component="icon-button"] [data-slot="icon-svg"] {
+ color: var(--text-base);
+}
+
/* Match tab context menus to dropdown-menu visuals on every tab surface. */
.session-tab-menu {
min-width: 210px;
diff --git a/packages/ui/src/components/list.tsx b/packages/ui/src/components/list.tsx
index 4dda0161d0b..f451ceb5b7f 100644
--- a/packages/ui/src/components/list.tsx
+++ b/packages/ui/src/components/list.tsx
@@ -175,14 +175,16 @@ export function List(props: ListProps & { ref?: (ref: ListRef) => void })
const all = flat()
const selected = all.find((x) => props.key(x) === active())
- const index = selected ? all.indexOf(selected) : -1
props.onKeyEvent?.(e, selected)
if (e.defaultPrevented) return
if (e.key === "Enter" && !e.isComposing) {
e.preventDefault()
- if (selected) handleSelect(selected, index)
+ // kilocode_change start - fall back to first result when no item is active (noInitialSelection)
+ const target = selected ?? all[0]
+ if (target) handleSelect(target, all.indexOf(target))
+ // kilocode_change end
} else if (props.search) {
if (e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey && (e.key === "n" || e.key === "p")) {
onKeyDown(e)
diff --git a/packages/ui/src/components/popover.tsx b/packages/ui/src/components/popover.tsx
index 576547b349f..4c692f3fc4a 100644
--- a/packages/ui/src/components/popover.tsx
+++ b/packages/ui/src/components/popover.tsx
@@ -20,6 +20,7 @@ export interface PopoverProps
trigger?: JSXElement
triggerAs?: T
triggerProps?: ComponentProps
+ contentLabel?: string // kilocode_change
title?: JSXElement
description?: JSXElement
class?: ComponentProps<"div">["class"]
@@ -34,6 +35,7 @@ export function Popover(props: PopoverProps
"trigger",
"triggerAs",
"triggerProps",
+ "contentLabel", // kilocode_change
"title",
"description",
"class",
@@ -136,6 +138,7 @@ export function Popover(props: PopoverProps
setState("contentRef", el)}
data-component="popover-content"
+ aria-label={local.contentLabel /* kilocode_change */}
classList={{
...local.classList,
[local.class ?? ""]: !!local.class,