Skip to content

Commit 6e69f68

Browse files
committed
anomalyco#126: tooltip for new session icon button
1 parent 5f57f6a commit 6e69f68

2 files changed

Lines changed: 68 additions & 44 deletions

File tree

‎packages/app/src/components/titlebar.tsx‎

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
2020
import { useTheme } from "@opencode-ai/ui/theme/context"
2121
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
2222
import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
23+
import { KeybindV2 } from "@opencode-ai/ui/v2/keybind-v2"
24+
import { TooltipV2 } from "@opencode-ai/ui/v2/tooltip-v2"
2325

2426
import { getProjectAvatarVariant, LayoutRoute, useLayout, type LocalProject } from "@/context/layout"
2527
import { usePlatform } from "@/context/platform"
26-
import { useCommand } from "@/context/command"
28+
import { formatKeybindKeys, useCommand } from "@/context/command"
2729
import { useLanguage } from "@/context/language"
2830
import { useSettings } from "@/context/settings"
2931
import { WindowsAppMenu } from "./windows-app-menu"
@@ -333,6 +335,7 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
333335
})
334336

335337
const openNewTab = () => navigate(newSessionHref())
338+
const newTabKeybind = "mod+t"
336339

337340
command.register("tabs", () => {
338341
const current = currentTab()
@@ -342,7 +345,7 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
342345
id: "tab.new",
343346
category: "tab",
344347
title: language.t("command.session.new"),
345-
keybind: "mod+t",
348+
keybind: newTabKeybind,
346349
hidden: true,
347350
onSelect: openNewTab,
348351
},
@@ -541,16 +544,26 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
541544
/>
542545
</div>
543546
<Show when={!(creating() && params.dir)}>
544-
<IconButtonV2
545-
type="button"
546-
variant="ghost-muted"
547-
size="large"
548-
class="shrink-0"
549-
icon={<IconV2 name="plus" />}
550-
as="a"
551-
href={newSessionHref()}
552-
aria-label={language.t("command.session.new")}
553-
/>
547+
<TooltipV2
548+
placement="bottom"
549+
value={
550+
<>
551+
{language.t("command.session.new")}
552+
<KeybindV2 keys={formatKeybindKeys(newTabKeybind, language.t)} variant="neutral" />
553+
</>
554+
}
555+
>
556+
<IconButtonV2
557+
type="button"
558+
variant="ghost-muted"
559+
size="large"
560+
class="shrink-0"
561+
icon={<IconV2 name="plus" />}
562+
as="a"
563+
href={newSessionHref()}
564+
aria-label={language.t("command.session.new")}
565+
/>
566+
</TooltipV2>
554567
</Show>
555568
<div class="flex-1" />
556569
<TitlebarV2Right state={v2RightState()} />

‎packages/app/src/context/command.tsx‎

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,11 @@ export function matchKeybind(keybinds: Keybind[], event: KeyboardEvent): boolean
170170
return false
171171
}
172172

173-
export function formatKeybind(config: string, t?: (key: KeyLabel) => string): string {
174-
if (!config || config === "none") return ""
173+
function formatKeybindParts(config: string, t?: (key: KeyLabel) => string): string[] {
174+
if (!config || config === "none") return []
175175

176176
const keybinds = parseKeybind(config)
177-
if (keybinds.length === 0) return ""
177+
if (keybinds.length === 0) return []
178178

179179
const kb = keybinds[0]
180180
const parts: string[] = []
@@ -184,43 +184,54 @@ export function formatKeybind(config: string, t?: (key: KeyLabel) => string): st
184184
if (kb.shift) parts.push(IS_MAC ? "⇧" : keyText("common.key.shift", t))
185185
if (kb.meta) parts.push(IS_MAC ? "⌘" : keyText("common.key.meta", t))
186186

187-
if (kb.key) {
188-
const keys: Record<string, string> = {
189-
arrowup: "↑",
190-
arrowdown: "↓",
191-
arrowleft: "←",
192-
arrowright: "→",
193-
comma: ",",
194-
plus: "+",
195-
}
196-
const named: Record<string, KeyLabel> = {
197-
backspace: "common.key.backspace",
198-
delete: "common.key.delete",
199-
end: "common.key.end",
200-
enter: "common.key.enter",
201-
esc: "common.key.esc",
202-
escape: "common.key.esc",
203-
home: "common.key.home",
204-
insert: "common.key.insert",
205-
pagedown: "common.key.pageDown",
206-
pageup: "common.key.pageUp",
207-
space: "common.key.space",
208-
tab: "common.key.tab",
209-
}
210-
const key = kb.key.toLowerCase()
211-
const displayKey =
212-
keys[key] ??
187+
if (!kb.key) return parts
188+
189+
const keys: Record<string, string> = {
190+
arrowup: "↑",
191+
arrowdown: "↓",
192+
arrowleft: "←",
193+
arrowright: "→",
194+
comma: ",",
195+
plus: "+",
196+
}
197+
const named: Record<string, KeyLabel> = {
198+
backspace: "common.key.backspace",
199+
delete: "common.key.delete",
200+
end: "common.key.end",
201+
enter: "common.key.enter",
202+
esc: "common.key.esc",
203+
escape: "common.key.esc",
204+
home: "common.key.home",
205+
insert: "common.key.insert",
206+
pagedown: "common.key.pageDown",
207+
pageup: "common.key.pageUp",
208+
space: "common.key.space",
209+
tab: "common.key.tab",
210+
}
211+
const key = kb.key.toLowerCase()
212+
parts.push(
213+
keys[key] ??
213214
(named[key]
214215
? keyText(named[key], t)
215216
: key.length === 1
216217
? key.toUpperCase()
217-
: key.charAt(0).toUpperCase() + key.slice(1))
218-
parts.push(displayKey)
219-
}
218+
: key.charAt(0).toUpperCase() + key.slice(1)),
219+
)
220+
221+
return parts
222+
}
220223

224+
export function formatKeybind(config: string, t?: (key: KeyLabel) => string): string {
225+
const parts = formatKeybindParts(config, t)
226+
if (parts.length === 0) return ""
221227
return IS_MAC ? parts.join("") : parts.join("+")
222228
}
223229

230+
// KeybindV2 takes an array instead of a string
231+
export function formatKeybindKeys(config: string, t?: (key: KeyLabel) => string): string[] {
232+
return formatKeybindParts(config, t)
233+
}
234+
224235
function isEditableTarget(target: EventTarget | null) {
225236
if (!(target instanceof HTMLElement)) return false
226237
if (target.isContentEditable) return true

0 commit comments

Comments
 (0)