summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam <[email protected]>2025-12-31 11:24:45 -0600
committerAdam <[email protected]>2025-12-31 13:12:29 -0600
commit3a1cfa6c731bc4c33348034cb918b7a4dbe2af8b (patch)
tree48bb1174aaa7e7b8c0b9b7a69219e6c4a2338a3a
parenta2857bba8305976761c55fda269d7fb79c951b8c (diff)
downloadopencode-3a1cfa6c731bc4c33348034cb918b7a4dbe2af8b.tar.gz
opencode-3a1cfa6c731bc4c33348034cb918b7a4dbe2af8b.zip
chore(app): keybind tooltip component
-rw-r--r--packages/app/src/components/prompt-input.tsx14
-rw-r--r--packages/app/src/pages/layout.tsx38
-rw-r--r--packages/app/src/pages/session.tsx62
-rw-r--r--packages/ui/src/components/tooltip.css13
-rw-r--r--packages/ui/src/components/tooltip.tsx20
5 files changed, 66 insertions, 81 deletions
diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx
index 9cc1579fb..42a8e107a 100644
--- a/packages/app/src/components/prompt-input.tsx
+++ b/packages/app/src/components/prompt-input.tsx
@@ -11,7 +11,7 @@ import { useSync } from "@/context/sync"
import { FileIcon } from "@opencode-ai/ui/file-icon"
import { Button } from "@opencode-ai/ui/button"
import { Icon } from "@opencode-ai/ui/icon"
-import { Tooltip } from "@opencode-ai/ui/tooltip"
+import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { Select } from "@opencode-ai/ui/select"
import { getDirectory, getFilename } from "@opencode-ai/util/path"
@@ -1355,15 +1355,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
</div>
</Match>
<Match when={store.mode === "normal"}>
- <Tooltip
- placement="top"
- value={
- <div class="flex items-center gap-2">
- <span>Cycle agent</span>
- <span class="text-icon-base text-12-medium">{command.keybind("agent.cycle")}</span>
- </div>
- }
- >
+ <TooltipKeybind placement="top" title="Cycle agent" keybind={command.keybind("agent.cycle")}>
<Select
options={local.agent.list().map((agent) => agent.name)}
current={local.agent.current()?.name ?? ""}
@@ -1371,7 +1363,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
class="capitalize"
variant="ghost"
/>
- </Tooltip>
+ </TooltipKeybind>
<Tooltip
placement="top"
value={
diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx
index a45c4d792..ce753ecc3 100644
--- a/packages/app/src/pages/layout.tsx
+++ b/packages/app/src/pages/layout.tsx
@@ -22,7 +22,7 @@ import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
import { Button } from "@opencode-ai/ui/button"
import { Icon } from "@opencode-ai/ui/icon"
import { IconButton } from "@opencode-ai/ui/icon-button"
-import { Tooltip } from "@opencode-ai/ui/tooltip"
+import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
import { Collapsible } from "@opencode-ai/ui/collapsible"
import { DiffChanges } from "@opencode-ai/ui/diff-changes"
import { Spinner } from "@opencode-ai/ui/spinner"
@@ -709,17 +709,13 @@ export default function Layout(props: ParentProps) {
</A>
</Tooltip>
<div class="hidden group-hover/session:flex group-active/session:flex group-focus-within/session:flex text-text-base gap-1 items-center absolute top-1 right-1">
- <Tooltip
+ <TooltipKeybind
placement={props.mobile ? "bottom" : "right"}
- value={
- <div class="flex items-center gap-2">
- <span>Archive session</span>
- <span class="text-icon-base text-12-medium">{command.keybind("session.archive")}</span>
- </div>
- }
+ title="Archive session"
+ keybind={command.keybind("session.archive")}
>
<IconButton icon="archive" variant="ghost" onClick={() => archiveSession(props.session)} />
- </Tooltip>
+ </TooltipKeybind>
</div>
</div>
</>
@@ -787,17 +783,9 @@ export default function Layout(props: ParentProps) {
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu>
- <Tooltip
- placement="top"
- value={
- <div class="flex items-center gap-2">
- <span>New session</span>
- <span class="text-icon-base text-12-medium">{command.keybind("session.new")}</span>
- </div>
- }
- >
+ <TooltipKeybind placement="top" title="New session" keybind={command.keybind("session.new")}>
<IconButton as={A} href={`${slug()}/session`} icon="plus-small" variant="ghost" />
- </Tooltip>
+ </TooltipKeybind>
</div>
</Button>
<Collapsible.Content>
@@ -880,15 +868,11 @@ export default function Layout(props: ParentProps) {
</A>
</Show>
<Show when={!sidebarProps.mobile}>
- <Tooltip
+ <TooltipKeybind
class="shrink-0"
placement="right"
- value={
- <div class="flex items-center gap-2">
- <span>Toggle sidebar</span>
- <span class="text-icon-base text-12-medium">{command.keybind("sidebar.toggle")}</span>
- </div>
- }
+ title="Toggle sidebar"
+ keybind={command.keybind("sidebar.toggle")}
inactive={expanded()}
>
<Button
@@ -920,7 +904,7 @@ export default function Layout(props: ParentProps) {
</div>
</Show>
</Button>
- </Tooltip>
+ </TooltipKeybind>
</Show>
<DragDropProvider
onDragStart={handleDragStart}
diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx
index 3e5884460..125dc19d3 100644
--- a/packages/app/src/pages/session.tsx
+++ b/packages/app/src/pages/session.tsx
@@ -21,7 +21,7 @@ import { DateTime } from "luxon"
import { FileIcon } from "@opencode-ai/ui/file-icon"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { Icon } from "@opencode-ai/ui/icon"
-import { Tooltip } from "@opencode-ai/ui/tooltip"
+import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
import { DiffChanges } from "@opencode-ai/ui/diff-changes"
import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
import { Tabs } from "@opencode-ai/ui/tabs"
@@ -149,17 +149,9 @@ function Header(props: { onMobileMenuToggle?: () => void }) {
/>
</div>
<Show when={currentSession()}>
- <Tooltip
- class="hidden xl:block"
- value={
- <div class="flex items-center gap-2">
- <span>New session</span>
- <span class="text-icon-base text-12-medium">{command.keybind("session.new")}</span>
- </div>
- }
- >
+ <TooltipKeybind class="hidden xl:block" title="New session" keybind={command.keybind("session.new")}>
<IconButton as={A} href={`/${params.dir}/session`} icon="edit-small-2" variant="ghost" />
- </Tooltip>
+ </TooltipKeybind>
</Show>
</div>
<div class="flex items-center gap-3">
@@ -187,14 +179,10 @@ function Header(props: { onMobileMenuToggle?: () => void }) {
</div>
<div class="flex items-center gap-1">
<Show when={currentSession()?.summary?.files}>
- <Tooltip
+ <TooltipKeybind
class="hidden md:block shrink-0"
- value={
- <div class="flex items-center gap-2">
- <span>Toggle review</span>
- <span class="text-icon-base text-12-medium">{command.keybind("review.toggle")}</span>
- </div>
- }
+ title="Toggle review"
+ keybind={command.keybind("review.toggle")}
>
<Button variant="ghost" class="group/review-toggle size-6 p-0" onClick={layout.review.toggle}>
<div class="relative flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0">
@@ -215,16 +203,12 @@ function Header(props: { onMobileMenuToggle?: () => void }) {
/>
</div>
</Button>
- </Tooltip>
+ </TooltipKeybind>
</Show>
- <Tooltip
+ <TooltipKeybind
class="hidden md:block shrink-0"
- value={
- <div class="flex items-center gap-2">
- <span>Toggle terminal</span>
- <span class="text-icon-base text-12-medium">{command.keybind("terminal.toggle")}</span>
- </div>
- }
+ title="Toggle terminal"
+ keybind={command.keybind("terminal.toggle")}
>
<Button variant="ghost" class="group/terminal-toggle size-6 p-0" onClick={layout.terminal.toggle}>
<div class="relative flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0">
@@ -245,7 +229,7 @@ function Header(props: { onMobileMenuToggle?: () => void }) {
/>
</div>
</Button>
- </Tooltip>
+ </TooltipKeybind>
</div>
<Show when={shareEnabled() && currentSession()}>
<Popover
@@ -1056,13 +1040,9 @@ export default function Page() {
</For>
</SortableProvider>
<div class="bg-background-base h-full flex items-center justify-center border-b border-border-weak-base px-3">
- <Tooltip
- value={
- <div class="flex items-center gap-2">
- <span>Open file</span>
- <span class="text-icon-base text-12-medium">{command.keybind("file.open")}</span>
- </div>
- }
+ <TooltipKeybind
+ title="Open file"
+ keybind={command.keybind("file.open")}
class="flex items-center"
>
<IconButton
@@ -1071,7 +1051,7 @@ export default function Page() {
iconSize="large"
onClick={() => dialog.show(() => <DialogSelectFile />)}
/>
- </Tooltip>
+ </TooltipKeybind>
</div>
</Tabs.List>
</div>
@@ -1178,17 +1158,13 @@ export default function Page() {
<For each={terminal.all()}>{(pty) => <SortableTerminalTab terminal={pty} />}</For>
</SortableProvider>
<div class="h-full flex items-center justify-center">
- <Tooltip
- value={
- <div class="flex items-center gap-2">
- <span>New terminal</span>
- <span class="text-icon-base text-12-medium">{command.keybind("terminal.new")}</span>
- </div>
- }
+ <TooltipKeybind
+ title="New terminal"
+ keybind={command.keybind("terminal.new")}
class="flex items-center"
>
<IconButton icon="plus-small" variant="ghost" iconSize="large" onClick={terminal.new} />
- </Tooltip>
+ </TooltipKeybind>
</div>
</Tabs.List>
<For each={terminal.all()}>
diff --git a/packages/ui/src/components/tooltip.css b/packages/ui/src/components/tooltip.css
index f7afe16aa..134de6f51 100644
--- a/packages/ui/src/components/tooltip.css
+++ b/packages/ui/src/components/tooltip.css
@@ -2,6 +2,19 @@
display: flex;
}
+[data-slot="tooltip-keybind"] {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+[data-slot="tooltip-keybind-key"] {
+ color: var(--icon-base);
+ font-size: var(--font-size-small);
+ font-weight: var(--font-weight-medium);
+ line-height: var(--line-height-large);
+}
+
[data-component="tooltip"] {
z-index: 1000;
max-width: 320px;
diff --git a/packages/ui/src/components/tooltip.tsx b/packages/ui/src/components/tooltip.tsx
index b3a2b628f..c38ee5847 100644
--- a/packages/ui/src/components/tooltip.tsx
+++ b/packages/ui/src/components/tooltip.tsx
@@ -8,6 +8,26 @@ export interface TooltipProps extends ComponentProps<typeof KobalteTooltip> {
inactive?: boolean
}
+export interface TooltipKeybindProps extends Omit<TooltipProps, "value"> {
+ title: string
+ keybind: string
+}
+
+export function TooltipKeybind(props: TooltipKeybindProps) {
+ const [local, others] = splitProps(props, ["title", "keybind"])
+ return (
+ <Tooltip
+ {...others}
+ value={
+ <div data-slot="tooltip-keybind">
+ <span>{local.title}</span>
+ <span data-slot="tooltip-keybind-key">{local.keybind}</span>
+ </div>
+ }
+ />
+ )
+}
+
export function Tooltip(props: TooltipProps) {
const [open, setOpen] = createSignal(false)
const [local, others] = splitProps(props, ["children", "class", "inactive"])