Skip to content

Commit 3402855

Browse files
committed
chore: cleanup
1 parent dd5b5f5 commit 3402855

4 files changed

Lines changed: 142 additions & 18 deletions

File tree

packages/app/src/components/settings-general.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,15 @@ export const SettingsGeneral: Component = () => {
7777
current={themeOptions().find((o) => o.id === theme.themeId())}
7878
value={(o) => o.id}
7979
label={(o) => o.name}
80-
onSelect={(option) => option && theme.setTheme(option.id)}
80+
onSelect={(option) => {
81+
if (!option) return
82+
theme.setTheme(option.id)
83+
}}
84+
onHighlight={(option) => {
85+
if (!option) return
86+
theme.previewTheme(option.id)
87+
return () => theme.cancelPreview()
88+
}}
8189
variant="secondary"
8290
size="small"
8391
/>
@@ -135,6 +143,10 @@ export const SettingsGeneral: Component = () => {
135143
current={soundOptions.find((o) => o.id === settings.sounds.agent())}
136144
value={(o) => o.id}
137145
label={(o) => o.label}
146+
onHighlight={(option) => {
147+
if (!option) return
148+
playSound(option.src)
149+
}}
138150
onSelect={(option) => {
139151
if (!option) return
140152
settings.sounds.setAgent(option.id)
@@ -151,6 +163,10 @@ export const SettingsGeneral: Component = () => {
151163
current={soundOptions.find((o) => o.id === settings.sounds.permissions())}
152164
value={(o) => o.id}
153165
label={(o) => o.label}
166+
onHighlight={(option) => {
167+
if (!option) return
168+
playSound(option.src)
169+
}}
154170
onSelect={(option) => {
155171
if (!option) return
156172
settings.sounds.setPermissions(option.id)
@@ -167,6 +183,10 @@ export const SettingsGeneral: Component = () => {
167183
current={soundOptions.find((o) => o.id === settings.sounds.errors())}
168184
value={(o) => o.id}
169185
label={(o) => o.label}
186+
onHighlight={(option) => {
187+
if (!option) return
188+
playSound(option.src)
189+
}}
170190
onSelect={(option) => {
171191
if (!option) return
172192
settings.sounds.setErrors(option.id)

packages/app/src/components/settings-keybinds.tsx

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,23 @@ export const SettingsKeybinds: Component = () => {
124124
const out = new Map<string, KeybindMeta>()
125125
out.set(PALETTE_ID, { title: "Command palette", group: "General" })
126126

127+
for (const opt of command.catalog) {
128+
if (opt.id.startsWith("suggested.")) continue
129+
out.set(opt.id, { title: opt.title, group: groupFor(opt.id) })
130+
}
131+
127132
for (const opt of command.options) {
128133
if (opt.id.startsWith("suggested.")) continue
134+
out.set(opt.id, { title: opt.title, group: groupFor(opt.id) })
135+
}
129136

130-
out.set(opt.id, {
131-
title: opt.title,
132-
group: groupFor(opt.id),
133-
})
137+
const keybinds = settings.current.keybinds as Record<string, string | undefined> | undefined
138+
if (keybinds) {
139+
for (const [id, value] of Object.entries(keybinds)) {
140+
if (typeof value !== "string") continue
141+
if (out.has(id)) continue
142+
out.set(id, { title: id, group: groupFor(id) })
143+
}
134144
}
135145

136146
return out
@@ -181,11 +191,21 @@ export const SettingsKeybinds: Component = () => {
181191
add(sig, { id: PALETTE_ID, title: "Command palette" })
182192
}
183193

184-
for (const opt of command.options) {
185-
if (opt.id.startsWith("suggested.")) continue
186-
if (!opt.keybind) continue
187-
for (const sig of signatures(opt.keybind)) {
188-
add(sig, { id: opt.id, title: opt.title })
194+
const valueFor = (id: string) => {
195+
const custom = settings.keybinds.get(id)
196+
if (typeof custom === "string") return custom
197+
198+
const live = command.options.find((x) => x.id === id)
199+
if (live?.keybind) return live.keybind
200+
201+
const meta = command.catalog.find((x) => x.id === id)
202+
return meta?.keybind
203+
}
204+
205+
for (const id of list().keys()) {
206+
if (id === PALETTE_ID) continue
207+
for (const sig of signatures(valueFor(id))) {
208+
add(sig, { id, title: title(id) })
189209
}
190210
}
191211

packages/app/src/context/command.tsx

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import { createMemo, createSignal, onCleanup, onMount, type Accessor } from "solid-js"
1+
import { createEffect, createMemo, createSignal, onCleanup, onMount, type Accessor } from "solid-js"
2+
import { createStore } from "solid-js/store"
23
import { createSimpleContext } from "@opencode-ai/ui/context"
34
import { useDialog } from "@opencode-ai/ui/context/dialog"
45
import { useSettings } from "@/context/settings"
6+
import { Persist, persisted } from "@/utils/persist"
57

68
const IS_MAC = typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(navigator.platform)
79

@@ -44,6 +46,14 @@ export interface CommandOption {
4446
onHighlight?: () => (() => void) | void
4547
}
4648

49+
export type CommandCatalogItem = {
50+
title: string
51+
description?: string
52+
category?: string
53+
keybind?: KeybindConfig
54+
slash?: string
55+
}
56+
4757
export function parseKeybind(config: string): Keybind[] {
4858
if (!config || config === "none") return []
4959

@@ -148,14 +158,19 @@ export const { use: useCommand, provider: CommandProvider } = createSimpleContex
148158
const [registrations, setRegistrations] = createSignal<Accessor<CommandOption[]>[]>([])
149159
const [suspendCount, setSuspendCount] = createSignal(0)
150160

161+
const [catalog, setCatalog, _, catalogReady] = persisted(
162+
Persist.global("command.catalog.v1"),
163+
createStore<Record<string, CommandCatalogItem>>({}),
164+
)
165+
151166
const bind = (id: string, def: KeybindConfig | undefined) => {
152167
const custom = settings.keybinds.get(actionId(id))
153168
const config = custom ?? def
154169
if (!config || config === "none") return
155170
return config
156171
}
157172

158-
const options = createMemo(() => {
173+
const registered = createMemo(() => {
159174
const seen = new Set<string>()
160175
const all: CommandOption[] = []
161176

@@ -167,7 +182,28 @@ export const { use: useCommand, provider: CommandProvider } = createSimpleContex
167182
}
168183
}
169184

170-
const resolved = all.map((opt) => ({
185+
return all
186+
})
187+
188+
createEffect(() => {
189+
if (!catalogReady()) return
190+
191+
for (const opt of registered()) {
192+
const id = actionId(opt.id)
193+
setCatalog(id, {
194+
title: opt.title,
195+
description: opt.description,
196+
category: opt.category,
197+
keybind: opt.keybind,
198+
slash: opt.slash,
199+
})
200+
}
201+
})
202+
203+
const catalogOptions = createMemo(() => Object.entries(catalog).map(([id, meta]) => ({ id, ...meta })))
204+
205+
const options = createMemo(() => {
206+
const resolved = registered().map((opt) => ({
171207
...opt,
172208
keybind: bind(opt.id, opt.keybind),
173209
}))
@@ -246,15 +282,23 @@ export const { use: useCommand, provider: CommandProvider } = createSimpleContex
246282
return formatKeybind(settings.keybinds.get(PALETTE_ID) ?? DEFAULT_PALETTE_KEYBIND)
247283
}
248284

249-
const option = options().find((x) => x.id === id || x.id === SUGGESTED_PREFIX + id)
250-
if (!option?.keybind) return ""
251-
return formatKeybind(option.keybind)
285+
const base = actionId(id)
286+
const option = options().find((x) => actionId(x.id) === base)
287+
if (option?.keybind) return formatKeybind(option.keybind)
288+
289+
const meta = catalog[base]
290+
const config = bind(base, meta?.keybind)
291+
if (!config) return ""
292+
return formatKeybind(config)
252293
},
253294
show: showPalette,
254295
keybinds(enabled: boolean) {
255296
setSuspendCount((count) => count + (enabled ? -1 : 1))
256297
},
257298
suspended,
299+
get catalog() {
300+
return catalogOptions()
301+
},
258302
get options() {
259303
return options()
260304
},

packages/ui/src/components/select.tsx

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Select as Kobalte } from "@kobalte/core/select"
2-
import { createMemo, splitProps, type ComponentProps, type JSX } from "solid-js"
2+
import { createMemo, onCleanup, splitProps, type ComponentProps, type JSX } from "solid-js"
33
import { pipe, groupBy, entries, map } from "remeda"
44
import { Button, ButtonProps } from "./button"
55
import { Icon } from "./icon"
@@ -12,6 +12,7 @@ export type SelectProps<T> = Omit<ComponentProps<typeof Kobalte<T>>, "value" | "
1212
label?: (x: T) => string
1313
groupBy?: (x: T) => string
1414
onSelect?: (value: T | undefined) => void
15+
onHighlight?: (value: T | undefined) => (() => void) | void
1516
class?: ComponentProps<"div">["class"]
1617
classList?: ComponentProps<"div">["classList"]
1718
children?: (item: T | undefined) => JSX.Element
@@ -28,8 +29,40 @@ export function Select<T>(props: SelectProps<T> & ButtonProps) {
2829
"label",
2930
"groupBy",
3031
"onSelect",
32+
"onHighlight",
33+
"onOpenChange",
3134
"children",
3235
])
36+
37+
const state = {
38+
key: undefined as string | undefined,
39+
cleanup: undefined as (() => void) | void,
40+
}
41+
42+
const stop = () => {
43+
state.cleanup?.()
44+
state.cleanup = undefined
45+
state.key = undefined
46+
}
47+
48+
const keyFor = (item: T) => (local.value ? local.value(item) : (item as string))
49+
50+
const move = (item: T | undefined) => {
51+
if (!local.onHighlight) return
52+
if (!item) {
53+
stop()
54+
return
55+
}
56+
57+
const key = keyFor(item)
58+
if (state.key === key) return
59+
state.cleanup?.()
60+
state.cleanup = local.onHighlight(item)
61+
state.key = key
62+
}
63+
64+
onCleanup(stop)
65+
3366
const grouped = createMemo(() => {
3467
const result = pipe(
3568
local.options,
@@ -58,12 +91,14 @@ export function Select<T>(props: SelectProps<T> & ButtonProps) {
5891
)}
5992
itemComponent={(itemProps) => (
6093
<Kobalte.Item
94+
{...itemProps}
6195
data-slot="select-select-item"
6296
classList={{
6397
...(local.classList ?? {}),
6498
[local.class ?? ""]: !!local.class,
6599
}}
66-
{...itemProps}
100+
onPointerEnter={() => move(itemProps.item.rawValue)}
101+
onPointerMove={() => move(itemProps.item.rawValue)}
67102
>
68103
<Kobalte.ItemLabel data-slot="select-select-item-label">
69104
{local.children
@@ -79,6 +114,11 @@ export function Select<T>(props: SelectProps<T> & ButtonProps) {
79114
)}
80115
onChange={(v) => {
81116
local.onSelect?.(v ?? undefined)
117+
stop()
118+
}}
119+
onOpenChange={(open) => {
120+
local.onOpenChange?.(open)
121+
if (!open) stop()
82122
}}
83123
>
84124
<Kobalte.Trigger

0 commit comments

Comments
 (0)