Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions packages/opencode/src/cli/cmd/tui/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -443,21 +443,19 @@ function App() {
},
},
{
title: "Favorite cycle",
title: "Favorite model cycle",
value: "model.cycle_favorite",
keybind: "model_cycle_favorite",
category: "Agent",
hidden: true,
onSelect: () => {
local.model.cycleFavorite(1)
},
},
{
title: "Favorite cycle reverse",
title: "Favorite model cycle reverse",
value: "model.cycle_favorite_reverse",
keybind: "model_cycle_favorite_reverse",
category: "Agent",
hidden: true,
onSelect: () => {
local.model.cycleFavorite(-1)
},
Expand Down
3 changes: 2 additions & 1 deletion packages/opencode/src/cli/cmd/tui/component/tips.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { DEFAULT_THEMES, useTheme } from "@tui/context/theme"

const themeCount = Object.keys(DEFAULT_THEMES).length
const themeTip = `Use {highlight}/themes{/highlight} or {highlight}Ctrl+X T{/highlight} to switch between ${themeCount} built-in themes`
const meta = process.platform === "darwin" ? "Option" : "Alt"

type TipPart = { text: string; highlight: boolean }

Expand Down Expand Up @@ -69,7 +70,7 @@ const TIPS = [
"Press {highlight}Ctrl+P{/highlight} to see all available actions and commands",
"Run {highlight}/connect{/highlight} to add API keys for 75+ supported LLM providers",
"The leader key is {highlight}Ctrl+X{/highlight}; combine with other keys for quick actions",
"Press {highlight}F2{/highlight} to quickly switch between recently used models",
`Press {highlight}F2{/highlight} to quickly switch between recently used models, or {highlight}${meta}+C{/highlight}/{highlight}${meta}+X{/highlight} for favorite models`,
"Press {highlight}Ctrl+X B{/highlight} to show/hide the sidebar panel",
"Use {highlight}PageUp{/highlight}/{highlight}PageDown{/highlight} to navigate through conversation history",
"Press {highlight}Ctrl+G{/highlight} or {highlight}Home{/highlight} to jump to the beginning of the conversation",
Expand Down
4 changes: 2 additions & 2 deletions packages/opencode/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -807,8 +807,8 @@ export namespace Config {
model_list: z.string().optional().default("<leader>m").describe("List available models"),
model_cycle_recent: z.string().optional().default("f2").describe("Next recently used model"),
model_cycle_recent_reverse: z.string().optional().default("shift+f2").describe("Previous recently used model"),
model_cycle_favorite: z.string().optional().default("none").describe("Next favorite model"),
model_cycle_favorite_reverse: z.string().optional().default("none").describe("Previous favorite model"),
model_cycle_favorite: z.string().optional().default("alt+c").describe("Next favorite model"),
model_cycle_favorite_reverse: z.string().optional().default("alt+x").describe("Previous favorite model"),
command_list: z.string().optional().default("ctrl+p").describe("List available commands"),
agent_list: z.string().optional().default("<leader>a").describe("List agents"),
agent_cycle: z.string().optional().default("tab").describe("Next agent"),
Expand Down
5 changes: 3 additions & 2 deletions packages/opencode/src/util/keybind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ export namespace Keybind {
}
}

export function toString(info: Info | undefined): string {
export function toString(info: Info | undefined, opts?: { platform?: NodeJS.Platform }): string {
if (!info) return ""
const parts: string[] = []
const meta = (opts?.platform ?? process.platform) === "darwin" ? "option" : "alt"

if (info.ctrl) parts.push("ctrl")
if (info.meta) parts.push("alt")
if (info.meta) parts.push(meta)
if (info.super) parts.push("super")
if (info.shift) parts.push("shift")
if (info.name) {
Expand Down
13 changes: 13 additions & 0 deletions packages/opencode/test/config/tui.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ test("loads tui config with the same precedence order as server config paths", a
})
})

test("loads default favorite model cycle keybinds", async () => {
await using tmp = await tmpdir()

await Instance.provide({
directory: tmp.path,
fn: async () => {
const config = await TuiConfig.get()
expect(config.keybinds?.model_cycle_favorite).toBe("alt+c")
expect(config.keybinds?.model_cycle_favorite_reverse).toBe("alt+x")
},
})
})

test("migrates tui-specific keys from opencode.json when tui.json does not exist", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
Expand Down
5 changes: 5 additions & 0 deletions packages/opencode/test/keybind.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ describe("Keybind.toString", () => {
expect(Keybind.toString(info)).toBe("ctrl+alt+g")
})

test("should convert meta to option on macOS", () => {
const info: Keybind.Info = { ctrl: false, meta: true, shift: false, leader: false, name: "c" }
expect(Keybind.toString(info, { platform: "darwin" })).toBe("option+c")
})

test("should convert all modifiers to string", () => {
const info: Keybind.Info = { ctrl: true, meta: true, shift: true, leader: true, name: "h" }
expect(Keybind.toString(info)).toBe("<leader> ctrl+alt+shift+h")
Expand Down
Loading