Skip to content

Commit fde4c1d

Browse files
Apply PR #26246: use keymap state for layer visibility
2 parents cdc5e92 + 29b7871 commit fde4c1d

11 files changed

Lines changed: 258 additions & 226 deletions

File tree

packages/opencode/src/cli/cmd/tui/app.tsx

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,16 @@ import { createTuiApi } from "@/cli/cmd/tui/plugin/api"
6666
import type { RouteMap } from "@/cli/cmd/tui/plugin/api"
6767
import { createTuiAttention } from "@/cli/cmd/tui/attention"
6868
import { FormatError, FormatUnknownError } from "@/cli/error"
69-
import { CommandPaletteProvider, useCommandPalette } from "./context/command-palette"
70-
import { OpencodeKeymapProvider, registerOpencodeKeymap, useBindings, useOpencodeKeymap } from "./keymap"
69+
import { CommandPaletteDialog } from "./component/command-palette"
70+
import {
71+
COMMAND_PALETTE_COMMAND,
72+
OPENCODE_BASE_MODE,
73+
OpencodeKeymapProvider,
74+
createOpencodeModeStack,
75+
registerOpencodeKeymap,
76+
useBindings,
77+
useOpencodeKeymap,
78+
} from "./keymap"
7179

7280
import type { EventSource } from "./context/sdk"
7381
import { DialogVariant } from "./component/dialog-variant"
@@ -178,6 +186,7 @@ export function tui(input: {
178186
}
179187
const onBeforeExit = async () => {
180188
offKeymap()
189+
modeStack.dispose()
181190
await TuiPluginRuntime.dispose()
182191
TuiAudio.dispose()
183192
}
@@ -188,6 +197,7 @@ export function tui(input: {
188197
const mode = (await renderer.waitForThemeMode(1000)) ?? "dark"
189198

190199
const keymap = createDefaultOpenTuiKeymap(renderer)
200+
const modeStack = createOpencodeModeStack(keymap)
191201
const offKeymap = registerOpencodeKeymap(keymap, renderer, input.config)
192202

193203
await render(() => {
@@ -227,17 +237,15 @@ export function tui(input: {
227237
<LocalProvider>
228238
<PromptStashProvider>
229239
<DialogProvider>
230-
<CommandPaletteProvider>
231-
<FrecencyProvider>
232-
<PromptHistoryProvider>
233-
<PromptRefProvider>
234-
<EditorContextProvider>
235-
<App onSnapshot={input.onSnapshot} />
236-
</EditorContextProvider>
237-
</PromptRefProvider>
238-
</PromptHistoryProvider>
239-
</FrecencyProvider>
240-
</CommandPaletteProvider>
240+
<FrecencyProvider>
241+
<PromptHistoryProvider>
242+
<PromptRefProvider>
243+
<EditorContextProvider>
244+
<App onSnapshot={input.onSnapshot} />
245+
</EditorContextProvider>
246+
</PromptRefProvider>
247+
</PromptHistoryProvider>
248+
</FrecencyProvider>
241249
</DialogProvider>
242250
</PromptStashProvider>
243251
</LocalProvider>
@@ -267,7 +275,6 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
267275
const dialog = useDialog()
268276
const local = useLocal()
269277
const kv = useKV()
270-
const command = useCommandPalette()
271278
const keymap = useOpencodeKeymap()
272279
const event = useEvent()
273280
const sdk = useSDK()
@@ -446,12 +453,12 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
446453
const appCommands = createMemo(() =>
447454
[
448455
{
449-
name: "command.palette.show",
456+
name: COMMAND_PALETTE_COMMAND,
450457
title: "Show command palette",
451458
category: "System",
452459
hidden: true,
453460
run: () => {
454-
command.show()
461+
dialog.replace(() => <CommandPaletteDialog />)
455462
},
456463
},
457464
{
@@ -812,14 +819,13 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
812819
}))
813820

814821
useBindings(() => ({
815-
enabled: command.matcher,
822+
opencodeMode: OPENCODE_BASE_MODE,
816823
bindings: tuiConfig.keybinds.gather("app", appBindingCommands),
817824
}))
818825

819826
useBindings(() => ({
827+
opencodeMode: OPENCODE_BASE_MODE,
820828
enabled: () => {
821-
const ok = command.matcher.get()
822-
if (!ok) return false
823829
const current = promptRef.current
824830
if (!current?.focused) return true
825831
return current.current.input === ""
@@ -828,7 +834,7 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
828834
}))
829835

830836
event.on(TuiEvent.CommandExecute.type, (evt) => {
831-
command.run(evt.properties.command)
837+
keymap.dispatchCommand(evt.properties.command)
832838
})
833839

834840
event.on(TuiEvent.ToastShow.type, (evt) => {
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { createMemo } from "solid-js"
2+
import { DialogSelect, type DialogSelectRef } from "@tui/ui/dialog-select"
3+
import { type DialogContext } from "@tui/ui/dialog"
4+
import {
5+
COMMAND_PALETTE_COMMAND,
6+
formatKeyBindings,
7+
type OpenTuiKeymap,
8+
useKeymapSelector,
9+
useOpencodeKeymap,
10+
} from "../keymap"
11+
import { useTuiConfig } from "../context/tui-config"
12+
13+
type PaletteCommandEntry = ReturnType<OpenTuiKeymap["getCommandEntries"]>[number]
14+
15+
function isVisiblePaletteCommand(entry: PaletteCommandEntry) {
16+
return entry.command.hidden !== true && entry.command.name !== COMMAND_PALETTE_COMMAND
17+
}
18+
19+
function isSuggestedPaletteCommand(entry: PaletteCommandEntry) {
20+
const suggested = entry.command.suggested
21+
if (typeof suggested === "boolean") return suggested
22+
if (typeof suggested === "function") return suggested() === true
23+
return false
24+
}
25+
26+
export function CommandPaletteDialog() {
27+
const config = useTuiConfig()
28+
const keymap = useOpencodeKeymap()
29+
const entries = useKeymapSelector((keymap: OpenTuiKeymap) => {
30+
const query = {
31+
namespace: "palette",
32+
}
33+
const reachable = keymap
34+
.getCommandEntries({
35+
...query,
36+
visibility: "reachable",
37+
})
38+
.filter(isVisiblePaletteCommand)
39+
const registeredBindings = keymap.getCommandBindings({
40+
visibility: "registered",
41+
commands: reachable.map((entry) => entry.command.name),
42+
})
43+
44+
return reachable.map((entry) => ({
45+
...entry,
46+
bindings: registeredBindings.get(entry.command.name) ?? entry.bindings,
47+
}))
48+
})
49+
const options = createMemo(() =>
50+
entries().map((entry) => ({
51+
title: typeof entry.command.title === "string" ? entry.command.title : entry.command.name,
52+
description: typeof entry.command.desc === "string" ? entry.command.desc : undefined,
53+
category: typeof entry.command.category === "string" ? entry.command.category : undefined,
54+
footer: formatKeyBindings(entry.bindings, config),
55+
value: entry.command.name,
56+
suggested: isSuggestedPaletteCommand(entry),
57+
onSelect: (dialog: DialogContext) => {
58+
dialog.clear()
59+
keymap.dispatchCommand(entry.command.name)
60+
},
61+
})),
62+
)
63+
64+
let ref: DialogSelectRef<string>
65+
const list = () => {
66+
if (ref?.filter) return options()
67+
return [
68+
...options()
69+
.filter((option) => option.suggested)
70+
.map((option) => ({
71+
...option,
72+
value: `suggested:${option.value}`,
73+
category: "Suggested",
74+
})),
75+
...options(),
76+
]
77+
}
78+
79+
return <DialogSelect ref={(value) => (ref = value)} title="Commands" options={list()} />
80+
}

packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,11 @@ import { getScrollAcceleration } from "../../util/scroll"
1313
import { useTuiConfig } from "../../context/tui-config"
1414
import { useTheme, selectedForeground } from "@tui/context/theme"
1515
import { SplitBorder } from "@tui/component/border"
16-
import { useCommandPalette } from "../../context/command-palette"
1716
import { useTerminalDimensions } from "@opentui/solid"
1817
import { Locale } from "@/util/locale"
1918
import type { PromptInfo } from "./history"
2019
import { useFrecency } from "./frecency"
21-
import { useBindings } from "../../keymap"
20+
import { useBindings, useCommandSlashes, useOpencodeModeStack } from "../../keymap"
2221
import { Reference } from "@/reference/reference"
2322
import { ConfigReference } from "@/config/reference"
2423
import { displayCharAt, mentionTriggerIndex } from "@/cli/cmd/prompt-display"
@@ -87,7 +86,8 @@ export function Autocomplete(props: {
8786
const sdk = useSDK()
8887
const sync = useSync()
8988
const project = useProject()
90-
const command = useCommandPalette()
89+
const slashes = useCommandSlashes()
90+
const modeStack = useOpencodeModeStack()
9191
const { theme } = useTheme()
9292
const dimensions = useTerminalDimensions()
9393
const frecency = useFrecency()
@@ -101,6 +101,12 @@ export function Autocomplete(props: {
101101

102102
const [positionTick, setPositionTick] = createSignal(0)
103103

104+
createEffect(() => {
105+
if (!store.visible) return
106+
const popMode = modeStack.push("autocomplete")
107+
onCleanup(popMode)
108+
})
109+
104110
createEffect(() => {
105111
if (store.visible) {
106112
let lastPos = { x: 0, y: 0, width: 0 }
@@ -367,7 +373,6 @@ export function Autocomplete(props: {
367373
const { filename, part } = createFilePart(item, lineRange)
368374
const index = store.visible === "@" ? store.index : props.input().cursorOffset
369375

370-
command.suspend(false)
371376
setStore("visible", false)
372377
setStore("index", index)
373378
insertPart(filename, part)
@@ -539,7 +544,7 @@ export function Autocomplete(props: {
539544
)
540545

541546
const commands = createMemo((): AutocompleteOption[] => {
542-
const results: AutocompleteOption[] = [...command.slashes()]
547+
const results: AutocompleteOption[] = [...slashes()]
543548

544549
for (const serverCommand of sync.data.command) {
545550
if (serverCommand.source === "skill") continue
@@ -730,7 +735,6 @@ export function Autocomplete(props: {
730735
}))
731736

732737
function show(mode: "@" | "/") {
733-
command.suspend(true)
734738
setStore({
735739
visible: mode,
736740
index: props.input().cursorOffset,
@@ -747,7 +751,6 @@ export function Autocomplete(props: {
747751
draft.input = props.input().plainText
748752
})
749753
}
750-
command.suspend(false)
751754
setStore("visible", false)
752755
}
753756

packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,13 @@ import { DialogWorkspaceUnavailable } from "../dialog-workspace-unavailable"
5959
import { useArgs } from "@tui/context/args"
6060
import { Flag } from "@opencode-ai/core/flag/flag"
6161
import { type WorkspaceStatus } from "../workspace-label"
62-
import { useCommandPalette } from "../../context/command-palette"
63-
import { useBindings, useCommandShortcut, useLeaderActive, useOpencodeKeymap } from "../../keymap"
62+
import {
63+
OPENCODE_BASE_MODE,
64+
useBindings,
65+
useCommandShortcut,
66+
useLeaderActive,
67+
useOpencodeKeymap,
68+
} from "../../keymap"
6469
import { useTuiConfig } from "../../context/tui-config"
6570

6671
export type PromptProps = {
@@ -152,7 +157,6 @@ export function Prompt(props: PromptProps) {
152157
const status = createMemo(() => sync.data.session_status?.[props.sessionID ?? ""] ?? { type: "idle" })
153158
const history = usePromptHistory()
154159
const stash = usePromptStash()
155-
const command = useCommandPalette()
156160
const keymap = useOpencodeKeymap()
157161
const agentShortcut = useCommandShortcut("agent.cycle")
158162
const paletteShortcut = useCommandShortcut("command.palette.show")
@@ -641,7 +645,7 @@ export function Prompt(props: PromptProps) {
641645
}))
642646

643647
useBindings(() => ({
644-
enabled: command.matcher,
648+
opencodeMode: OPENCODE_BASE_MODE,
645649
bindings: tuiConfig.keybinds.gather("prompt.palette", [
646650
"prompt.submit",
647651
"prompt.editor",

0 commit comments

Comments
 (0)