From 2ed61573d53031c9a1e3b0ef47d0d608793a1e08 Mon Sep 17 00:00:00 2001 From: Aarav Sareen <96787824+arvsrn@users.noreply.github.com> Date: Mon, 15 Jun 2026 17:19:43 +0530 Subject: [PATCH 01/16] #131: fix focus states for tabs --- packages/app/src/components/titlebar.css | 26 +++++++++++++++++++++++ packages/app/src/components/titlebar.tsx | 12 ++++++++--- packages/ui/src/v2/components/tabs-v2.css | 4 +++- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/packages/app/src/components/titlebar.css b/packages/app/src/components/titlebar.css index edce93c3106a..348e1c443dd2 100644 --- a/packages/app/src/components/titlebar.css +++ b/packages/app/src/components/titlebar.css @@ -1,3 +1,29 @@ +[data-slot="titlebar-tab-item"] { + user-select: none; +} + +[data-slot="titlebar-tab-item"] a { + outline: none; +} + +[data-slot="titlebar-v2"] [data-component="icon-button-v2"][data-variant="ghost-muted"]:is(:focus-visible, [data-state="focus"]):not(:disabled) { + outline: none; + background-color: var(--v2-overlay-simple-overlay-hover); +} + +[data-slot="titlebar-tab-item"] [data-component="icon-button-v2"]:is(:focus-visible, [data-state="focus"]):not(:disabled) { + opacity: 1; +} + +[data-slot="titlebar-tab-item"]:has([data-component="icon-button-v2"]:focus-visible) [data-slot="titlebar-tab-close"] { + right: 0; + left: auto; +} + +[data-slot="titlebar-tab-item"]:has([data-component="icon-button-v2"]:focus-visible) [data-slot="titlebar-tab-close-fade"] { + background-image: var(--active-bg); +} + @keyframes titlebar-tab-fade-left { from { visibility: hidden; diff --git a/packages/app/src/components/titlebar.tsx b/packages/app/src/components/titlebar.tsx index e813f9f10b08..a6dbaedca4bb 100644 --- a/packages/app/src/components/titlebar.tsx +++ b/packages/app/src/components/titlebar.tsx @@ -230,6 +230,7 @@ export function Titlebar(props: { update?: TitlebarUpdate }) { return (
{ if (event.button !== 1) return @@ -848,10 +850,12 @@ function TabNavItem(props: {
{ if (event.button !== 1) return closeTab(event) @@ -952,7 +957,8 @@ function NewSessionTabItem(props: { ref?: HTMLDivElement; href: string; title: s return (
{ if (event.button !== 1) return closeTab(event) diff --git a/packages/ui/src/v2/components/tabs-v2.css b/packages/ui/src/v2/components/tabs-v2.css index f88ba67ae942..8582bbf678c0 100644 --- a/packages/ui/src/v2/components/tabs-v2.css +++ b/packages/ui/src/v2/components/tabs-v2.css @@ -45,6 +45,7 @@ align-items: center; flex-shrink: 0; white-space: nowrap; + user-select: none; } [data-component="tabs-v2"] [data-slot="tabs-v2-trigger"] { @@ -54,6 +55,7 @@ overflow: hidden; text-overflow: ellipsis; outline: none; + user-select: none; font-size: 13px; font-weight: 440; @@ -61,7 +63,7 @@ letter-spacing: -0.04px; } -[data-component="tabs-v2"] [data-slot="tabs-v2-trigger"]:focus-visible { +[data-component="tabs-v2"] [data-slot="tabs-v2-trigger"]:is(:focus, :focus-visible) { outline: none; box-shadow: none; } From 5f57f6a55aff8858314c01fb1c7ce5dcedc08859 Mon Sep 17 00:00:00 2001 From: Aarav Sareen <96787824+arvsrn@users.noreply.github.com> Date: Mon, 15 Jun 2026 17:36:38 +0530 Subject: [PATCH 02/16] #130: fix spacing --- packages/ui/src/v2/components/project-avatar-v2.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/v2/components/project-avatar-v2.css b/packages/ui/src/v2/components/project-avatar-v2.css index 1c59fc17d931..708c13932522 100644 --- a/packages/ui/src/v2/components/project-avatar-v2.css +++ b/packages/ui/src/v2/components/project-avatar-v2.css @@ -143,7 +143,7 @@ flex-shrink: 0; align-items: center; justify-content: center; - width: 22px; - height: 22px; + width: 16px; + height: 16px; overflow: visible; } From 6e69f68a5b00a7b740289499de945c95f425bd2e Mon Sep 17 00:00:00 2001 From: Aarav Sareen <96787824+arvsrn@users.noreply.github.com> Date: Mon, 15 Jun 2026 17:41:42 +0530 Subject: [PATCH 03/16] #126: tooltip for new session icon button --- packages/app/src/components/titlebar.tsx | 37 ++++++++---- packages/app/src/context/command.tsx | 75 ++++++++++++++---------- 2 files changed, 68 insertions(+), 44 deletions(-) diff --git a/packages/app/src/components/titlebar.tsx b/packages/app/src/components/titlebar.tsx index a6dbaedca4bb..2adc1f851373 100644 --- a/packages/app/src/components/titlebar.tsx +++ b/packages/app/src/components/titlebar.tsx @@ -20,10 +20,12 @@ import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip" import { useTheme } from "@opencode-ai/ui/theme/context" import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2" import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon" +import { KeybindV2 } from "@opencode-ai/ui/v2/keybind-v2" +import { TooltipV2 } from "@opencode-ai/ui/v2/tooltip-v2" import { getProjectAvatarVariant, LayoutRoute, useLayout, type LocalProject } from "@/context/layout" import { usePlatform } from "@/context/platform" -import { useCommand } from "@/context/command" +import { formatKeybindKeys, useCommand } from "@/context/command" import { useLanguage } from "@/context/language" import { useSettings } from "@/context/settings" import { WindowsAppMenu } from "./windows-app-menu" @@ -333,6 +335,7 @@ export function Titlebar(props: { update?: TitlebarUpdate }) { }) const openNewTab = () => navigate(newSessionHref()) + const newTabKeybind = "mod+t" command.register("tabs", () => { const current = currentTab() @@ -342,7 +345,7 @@ export function Titlebar(props: { update?: TitlebarUpdate }) { id: "tab.new", category: "tab", title: language.t("command.session.new"), - keybind: "mod+t", + keybind: newTabKeybind, hidden: true, onSelect: openNewTab, }, @@ -541,16 +544,26 @@ export function Titlebar(props: { update?: TitlebarUpdate }) { />
- } - as="a" - href={newSessionHref()} - aria-label={language.t("command.session.new")} - /> + + {language.t("command.session.new")} + + + } + > + } + as="a" + href={newSessionHref()} + aria-label={language.t("command.session.new")} + /> +
diff --git a/packages/app/src/context/command.tsx b/packages/app/src/context/command.tsx index e979ad6a0595..2a6e61b3a033 100644 --- a/packages/app/src/context/command.tsx +++ b/packages/app/src/context/command.tsx @@ -170,11 +170,11 @@ export function matchKeybind(keybinds: Keybind[], event: KeyboardEvent): boolean return false } -export function formatKeybind(config: string, t?: (key: KeyLabel) => string): string { - if (!config || config === "none") return "" +function formatKeybindParts(config: string, t?: (key: KeyLabel) => string): string[] { + if (!config || config === "none") return [] const keybinds = parseKeybind(config) - if (keybinds.length === 0) return "" + if (keybinds.length === 0) return [] const kb = keybinds[0] const parts: string[] = [] @@ -184,43 +184,54 @@ export function formatKeybind(config: string, t?: (key: KeyLabel) => string): st if (kb.shift) parts.push(IS_MAC ? "⇧" : keyText("common.key.shift", t)) if (kb.meta) parts.push(IS_MAC ? "⌘" : keyText("common.key.meta", t)) - if (kb.key) { - const keys: Record = { - arrowup: "↑", - arrowdown: "↓", - arrowleft: "←", - arrowright: "→", - comma: ",", - plus: "+", - } - const named: Record = { - backspace: "common.key.backspace", - delete: "common.key.delete", - end: "common.key.end", - enter: "common.key.enter", - esc: "common.key.esc", - escape: "common.key.esc", - home: "common.key.home", - insert: "common.key.insert", - pagedown: "common.key.pageDown", - pageup: "common.key.pageUp", - space: "common.key.space", - tab: "common.key.tab", - } - const key = kb.key.toLowerCase() - const displayKey = - keys[key] ?? + if (!kb.key) return parts + + const keys: Record = { + arrowup: "↑", + arrowdown: "↓", + arrowleft: "←", + arrowright: "→", + comma: ",", + plus: "+", + } + const named: Record = { + backspace: "common.key.backspace", + delete: "common.key.delete", + end: "common.key.end", + enter: "common.key.enter", + esc: "common.key.esc", + escape: "common.key.esc", + home: "common.key.home", + insert: "common.key.insert", + pagedown: "common.key.pageDown", + pageup: "common.key.pageUp", + space: "common.key.space", + tab: "common.key.tab", + } + const key = kb.key.toLowerCase() + parts.push( + keys[key] ?? (named[key] ? keyText(named[key], t) : key.length === 1 ? key.toUpperCase() - : key.charAt(0).toUpperCase() + key.slice(1)) - parts.push(displayKey) - } + : key.charAt(0).toUpperCase() + key.slice(1)), + ) + + return parts +} +export function formatKeybind(config: string, t?: (key: KeyLabel) => string): string { + const parts = formatKeybindParts(config, t) + if (parts.length === 0) return "" return IS_MAC ? parts.join("") : parts.join("+") } +// KeybindV2 takes an array instead of a string +export function formatKeybindKeys(config: string, t?: (key: KeyLabel) => string): string[] { + return formatKeybindParts(config, t) +} + function isEditableTarget(target: EventTarget | null) { if (!(target instanceof HTMLElement)) return false if (target.isContentEditable) return true From 7f5cd9b284d35741face85755fc1c6c8507afafe Mon Sep 17 00:00:00 2001 From: Aarav Sareen <96787824+arvsrn@users.noreply.github.com> Date: Mon, 15 Jun 2026 17:48:04 +0530 Subject: [PATCH 04/16] #124: hide project name when project is selected --- packages/app/src/pages/home.tsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/app/src/pages/home.tsx b/packages/app/src/pages/home.tsx index 24c7e4c3abe4..dc5f6ffd7ac8 100644 --- a/packages/app/src/pages/home.tsx +++ b/packages/app/src/pages/home.tsx @@ -374,6 +374,7 @@ function HomeDesign() { open={searchOpen()} loading={sessionLoad.isLoading} results={searchResults()} + showProjectName={!selectedProject()} server={state.selection.server} activeServer={state.selection.server === server.key} noResultsLabel={language.t("home.sessions.search.noResults", { query: search() })} @@ -414,6 +415,7 @@ function HomeDesign() { {(record) => ( ( void }) { const title = createMemo(() => sessionTitle(props.record.session.title) || props.record.session.id) + const showProjectName = () => props.showProjectName && props.record.projectName const key = () => homeSessionSearchKey(props.record) @@ -985,11 +991,11 @@ function HomeSessionSearchResultRow(props: { />
{title()} - + {props.record.projectName}
@@ -1022,11 +1028,13 @@ function HomeSessionGroupHeader(props: { title: string; onNewSession?: () => voi function HomeSessionRow(props: { record: HomeSessionRecord + showProjectName: boolean server: ServerConnection.Key activeServer: boolean openSession: (session: Session) => void }) { const title = createMemo(() => sessionTitle(props.record.session.title) || props.record.session.id) + const showProjectName = () => props.showProjectName && props.record.projectName return (
- } - aria-label={props.language.t("command.session.new")} - onClick={() => props.openNewSession(props.server, props.project.worktree)} - /> + } + aria-label={props.language.t("command.session.new")} + onClick={() => props.openNewSession(props.server, props.project.worktree)} + />
) From 2e439883d0f31604f90d9e5e3c8df80677047650 Mon Sep 17 00:00:00 2001 From: Aarav Sareen <96787824+arvsrn@users.noreply.github.com> Date: Wed, 17 Jun 2026 13:04:58 +0530 Subject: [PATCH 08/16] #116: home screen alignment --- packages/app/src/pages/home.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/app/src/pages/home.tsx b/packages/app/src/pages/home.tsx index 69ba2968e536..1548bc75e012 100644 --- a/packages/app/src/pages/home.tsx +++ b/packages/app/src/pages/home.tsx @@ -830,7 +830,7 @@ function HomeSessionSearch(props: { ) return ( -
+
void }) { const language = useLanguage() return ( -
+
{(onNewSession) => ( @@ -1040,7 +1040,7 @@ function HomeSessionRow(props: {
diff --git a/packages/ui/src/components/scroll-view.css b/packages/ui/src/components/scroll-view.css index f6a49e241c6e..7c5d8ed0f7bb 100644 --- a/packages/ui/src/components/scroll-view.css +++ b/packages/ui/src/components/scroll-view.css @@ -1,10 +1,14 @@ .scroll-view { position: relative; + display: flex; + flex-direction: column; + min-height: 0; overflow: hidden; } .scroll-view__viewport { - height: 100%; + flex: 1 1 auto; + min-height: 0; width: 100%; overflow-y: auto; scrollbar-width: none; From 43afdd9da3044532bab80d61cc25bd5c26043f9e Mon Sep 17 00:00:00 2001 From: Aarav Sareen <96787824+arvsrn@users.noreply.github.com> Date: Wed, 17 Jun 2026 13:20:59 +0530 Subject: [PATCH 11/16] update session tab indicator --- packages/app/src/pages/home.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/src/pages/home.tsx b/packages/app/src/pages/home.tsx index 8a60836868c5..67c2d5590bf9 100644 --- a/packages/app/src/pages/home.tsx +++ b/packages/app/src/pages/home.tsx @@ -738,7 +738,7 @@ function HomeSessionLeading(props: {