diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index c74edd94e6..f68531adc5 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -44,7 +44,7 @@ import { ImagePreview } from "@opencode-ai/ui/image-preview" import { ModelSelectorPopover } from "@/components/dialog-select-model" import { DialogSelectModelUnpaid } from "@/components/dialog-select-model-unpaid" import { useProviders } from "@/hooks/use-providers" -import { useCommand } from "@/context/command" +import { formatKeybind, useCommand } from "@/context/command" import { Persist, persisted } from "@/utils/persist" import { Identifier } from "@/utils/id" import { SessionContextUsage } from "@/components/session-context-usage" @@ -1400,8 +1400,8 @@ export const PromptInput: Component = (props) => { custom - - {command.keybind(cmd.id)} + + {formatKeybind(cmd.keybind!)} diff --git a/packages/app/src/components/shortcuts-panel.tsx b/packages/app/src/components/shortcuts-panel.tsx index 9823e0315e..73dea23b46 100644 --- a/packages/app/src/components/shortcuts-panel.tsx +++ b/packages/app/src/components/shortcuts-panel.tsx @@ -3,7 +3,7 @@ import { Tabs } from "@opencode-ai/ui/tabs" import { Icon } from "@opencode-ai/ui/icon" import { IconButton } from "@opencode-ai/ui/icon-button" import { Tooltip } from "@opencode-ai/ui/tooltip" -import { parseKeybind } from "@/context/command" +import { parseKeybind, formatKeybind } from "@/context/command" const IS_MAC = typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(navigator.platform) @@ -163,10 +163,14 @@ function ShortcutItem(props: { shortcut: Shortcut }) { {(char) => { const tooltip = SPECIAL_CHAR_NAMES[char] + const isSpecial = tooltip && !isLetter(char) + const isShift = char === "⇧" return ( - {char}}> + {char}}> - {char} + + {char} + ) @@ -197,7 +201,16 @@ export function ShortcutsPanel(props: { onClose: () => void }) { {(category) => {category.name}} - + + Close shortcuts {formatKeybind("ctrl+/")} + + } + > + + {(category) => ( diff --git a/packages/app/src/context/layout.tsx b/packages/app/src/context/layout.tsx index a8da156092..ecf25f4e42 100644 --- a/packages/app/src/context/layout.tsx +++ b/packages/app/src/context/layout.tsx @@ -1,5 +1,5 @@ import { createStore, produce } from "solid-js/store" -import { batch, createEffect, createMemo, onCleanup, onMount } from "solid-js" +import { batch, createEffect, createMemo, createSignal, onCleanup, onMount } from "solid-js" import { createSimpleContext } from "@opencode-ai/ui/context" import { useGlobalSync } from "./global-sync" import { useGlobalSDK } from "./global-sdk" @@ -93,6 +93,8 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( }), ) + const [shortcutsOpened, setShortcutsOpened] = createSignal(false) + const MAX_SESSION_KEYS = 50 const meta = { active: undefined as string | undefined, pruned: false } const used = new Map() @@ -353,6 +355,18 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( setStore("review", "diffStyle", diffStyle) }, }, + shortcuts: { + opened: shortcutsOpened, + open() { + setShortcutsOpened(true) + }, + close() { + setShortcutsOpened(false) + }, + toggle() { + setShortcutsOpened((x) => !x) + }, + }, session: { width: createMemo(() => store.session?.width ?? 600), resize(width: number) { diff --git a/packages/app/src/index.css b/packages/app/src/index.css index cb25d1c703..a61f42b471 100644 --- a/packages/app/src/index.css +++ b/packages/app/src/index.css @@ -97,7 +97,7 @@ .shortcuts-panel [data-slot="tabs-trigger-wrapper"] { background: transparent; - border: 0.5px solid transparent; + border: none; font-weight: var(--font-weight-regular); border-radius: 6px; color: var(--color-text-weak); @@ -108,8 +108,7 @@ } .shortcuts-panel [data-slot="tabs-trigger-wrapper"]:has([data-selected]) { - border: 1px solid var(--color-border-weak-base); - background: var(--color-surface-raised-base); + background: var(--color-surface-raised-base-active); color: var(--color-text-strong); } @@ -202,7 +201,20 @@ white-space: nowrap; } -/* Adjust main content when shortcuts panel is open */ +.shortcut-key-special { + font-size: 18px; +} + +.shortcut-key-shift { + position: relative; + top: -2px; +} + +/* Adjust main content and sidebar when shortcuts panel is open */ main.shortcuts-open { padding-bottom: 280px; } + +.sidebar-shortcuts-open { + padding-bottom: 280px; +} diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index e53530fa22..56e5090508 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -59,7 +59,7 @@ import { useDialog } from "@opencode-ai/ui/context/dialog" import { useTheme, type ColorScheme } from "@opencode-ai/ui/theme" import { DialogSelectProvider } from "@/components/dialog-select-provider" import { DialogSelectServer } from "@/components/dialog-select-server" -import { useCommand, type CommandOption } from "@/context/command" +import { formatKeybind, useCommand, type CommandOption } from "@/context/command" import { ConstrainDragXAxis } from "@/utils/solid-dnd" import { navStart } from "@/utils/perf" import { DialogSelectDirectory } from "@/components/dialog-select-directory" @@ -81,7 +81,6 @@ export default function Layout(props: ParentProps) { workspaceExpanded: {} as Record, }), ) - const [shortcutsOpen, setShortcutsOpen] = createSignal(false) const pageReady = createMemo(() => ready()) @@ -757,7 +756,7 @@ export default function Layout(props: ParentProps) { title: "Toggle shortcuts panel", category: "View", keybind: "ctrl+/", - onSelect: () => setShortcutsOpen(!shortcutsOpen()), + onSelect: () => layout.shortcuts.toggle(), }, { id: "project.open", @@ -1897,7 +1896,7 @@ export default function Layout(props: ParentProps) { const homedir = createMemo(() => sync.data.path.home) return ( -
+
- - setShortcutsOpen(false)} /> + + layout.shortcuts.close()} /> diff --git a/packages/ui/src/components/select.tsx b/packages/ui/src/components/select.tsx index e60fcbee14..5ca92773c6 100644 --- a/packages/ui/src/components/select.tsx +++ b/packages/ui/src/components/select.tsx @@ -15,6 +15,7 @@ export type SelectProps = Omit>, "value" | " class?: ComponentProps<"div">["class"] classList?: ComponentProps<"div">["classList"] children?: (item: T | undefined) => JSX.Element + placement?: "bottom-start" | "bottom-end" | "top-start" | "top-end" } export function Select(props: SelectProps & ButtonProps) { @@ -29,6 +30,7 @@ export function Select(props: SelectProps & ButtonProps) { "groupBy", "onSelect", "children", + "placement", ]) const grouped = createMemo(() => { const result = pipe( @@ -46,7 +48,7 @@ export function Select(props: SelectProps & ButtonProps) { {...others} data-component="select" - placement="bottom-start" + placement={local.placement ?? "bottom-start"} value={local.current} options={grouped()} optionValue={(x) => (local.value ? local.value(x) : (x as string))}