wip: polish to shortcuts
parent
0f398e612f
commit
3ff6ce5967
|
|
@ -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<PromptInputProps> = (props) => {
|
|||
custom
|
||||
</span>
|
||||
</Show>
|
||||
<Show when={command.keybind(cmd.id)}>
|
||||
<span class="text-12-regular text-text-subtle">{command.keybind(cmd.id)}</span>
|
||||
<Show when={cmd.keybind}>
|
||||
<span class="text-12-regular text-text-subtle">{formatKeybind(cmd.keybind!)}</span>
|
||||
</Show>
|
||||
</div>
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -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 }) {
|
|||
<For each={getKeyChars(props.shortcut.keybind)}>
|
||||
{(char) => {
|
||||
const tooltip = SPECIAL_CHAR_NAMES[char]
|
||||
const isSpecial = tooltip && !isLetter(char)
|
||||
const isShift = char === "⇧"
|
||||
return (
|
||||
<Show when={tooltip && !isLetter(char)} fallback={<kbd class="shortcut-key">{char}</kbd>}>
|
||||
<Show when={isSpecial} fallback={<kbd class="shortcut-key">{char}</kbd>}>
|
||||
<Tooltip value={tooltip} placement="top">
|
||||
<kbd class="shortcut-key">{char}</kbd>
|
||||
<kbd class="shortcut-key shortcut-key-special">
|
||||
<span classList={{ "shortcut-key-shift": isShift }}>{char}</span>
|
||||
</kbd>
|
||||
</Tooltip>
|
||||
</Show>
|
||||
)
|
||||
|
|
@ -197,7 +201,16 @@ export function ShortcutsPanel(props: { onClose: () => void }) {
|
|||
{(category) => <Tabs.Trigger value={category.name}>{category.name}</Tabs.Trigger>}
|
||||
</For>
|
||||
</Tabs.List>
|
||||
<IconButton icon="close" variant="ghost" onClick={props.onClose} />
|
||||
<Tooltip
|
||||
placement="top"
|
||||
value={
|
||||
<span>
|
||||
Close shortcuts <span class="text-text-weak">{formatKeybind("ctrl+/")}</span>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<IconButton icon="close" variant="ghost" onClick={props.onClose} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<For each={SHORTCUT_CATEGORIES}>
|
||||
{(category) => (
|
||||
|
|
|
|||
|
|
@ -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<string, number>()
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<string, boolean>,
|
||||
}),
|
||||
)
|
||||
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 (
|
||||
<div class="flex h-full w-full overflow-hidden">
|
||||
<div class="flex h-full w-full overflow-hidden" classList={{ "sidebar-shortcuts-open": layout.shortcuts.opened() }}>
|
||||
<div class="w-16 shrink-0 bg-background-base flex flex-col items-center overflow-hidden">
|
||||
<div class="flex-1 min-h-0 w-full">
|
||||
<DragDropProvider
|
||||
|
|
@ -1937,7 +1936,7 @@ export default function Layout(props: ParentProps) {
|
|||
<Tooltip placement={sidebarProps.mobile ? "bottom" : "right"} value="Settings" class="hidden">
|
||||
<IconButton disabled icon="settings-gear" variant="ghost" size="large" />
|
||||
</Tooltip>
|
||||
<DropdownMenu>
|
||||
<DropdownMenu placement={layout.shortcuts.opened() ? "top-start" : "bottom-start"}>
|
||||
<Tooltip placement={sidebarProps.mobile ? "bottom" : "right"} value="Help">
|
||||
<DropdownMenu.Trigger as={IconButton} icon="question-mark" variant="ghost" size="large" />
|
||||
</Tooltip>
|
||||
|
|
@ -1946,7 +1945,9 @@ export default function Layout(props: ParentProps) {
|
|||
<DropdownMenu.Item onSelect={() => platform.openLink("https://opencode.ai/desktop-feedback")}>
|
||||
Submit feedback
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item onSelect={() => setShortcutsOpen(true)}>Keyboard shortcuts</DropdownMenu.Item>
|
||||
<DropdownMenu.Item class="flex justify-between gap-6" onSelect={() => layout.shortcuts.open()}>
|
||||
Keyboard shortcuts <span class="text-text-weaker">{formatKeybind("ctrl+/")}</span>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
</DropdownMenu>
|
||||
|
|
@ -2160,14 +2161,14 @@ export default function Layout(props: ParentProps) {
|
|||
classList={{
|
||||
"size-full overflow-x-hidden flex flex-col items-start contain-strict border-t border-border-weak-base": true,
|
||||
"xl:border-l xl:rounded-tl-sm": !layout.sidebar.opened(),
|
||||
"shortcuts-open": shortcutsOpen(),
|
||||
"shortcuts-open": layout.shortcuts.opened(),
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</main>
|
||||
</div>
|
||||
<Show when={shortcutsOpen()}>
|
||||
<ShortcutsPanel onClose={() => setShortcutsOpen(false)} />
|
||||
<Show when={layout.shortcuts.opened()}>
|
||||
<ShortcutsPanel onClose={() => layout.shortcuts.close()} />
|
||||
</Show>
|
||||
<Toast.Region />
|
||||
<ReleaseNotesHandler />
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ export type SelectProps<T> = Omit<ComponentProps<typeof Kobalte<T>>, "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<T>(props: SelectProps<T> & ButtonProps) {
|
||||
|
|
@ -29,6 +30,7 @@ export function Select<T>(props: SelectProps<T> & ButtonProps) {
|
|||
"groupBy",
|
||||
"onSelect",
|
||||
"children",
|
||||
"placement",
|
||||
])
|
||||
const grouped = createMemo(() => {
|
||||
const result = pipe(
|
||||
|
|
@ -46,7 +48,7 @@ export function Select<T>(props: SelectProps<T> & ButtonProps) {
|
|||
<Kobalte<T, { category: string; options: T[] }>
|
||||
{...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))}
|
||||
|
|
|
|||
Loading…
Reference in New Issue