summaryrefslogtreecommitdiffhomepage
path: root/packages/ui/src
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-02 14:22:10 -0600
committerAdam <[email protected]>2026-02-02 14:26:09 -0600
commitc002ca03ba7a617090ab104c5d2a07f1c8be2958 (patch)
tree6d3e2938c03fc6cd71e9275dab3f798630b2ac1e /packages/ui/src
parentbefb5d54fbfd8df9706c49159095b1ef7f2ec23d (diff)
downloadopencode-c002ca03ba7a617090ab104c5d2a07f1c8be2958.tar.gz
opencode-c002ca03ba7a617090ab104c5d2a07f1c8be2958.zip
feat(app): search through sessions
Diffstat (limited to 'packages/ui/src')
-rw-r--r--packages/ui/src/components/list.tsx33
1 files changed, 31 insertions, 2 deletions
diff --git a/packages/ui/src/components/list.tsx b/packages/ui/src/components/list.tsx
index 6c654cbb7..abd557220 100644
--- a/packages/ui/src/components/list.tsx
+++ b/packages/ui/src/components/list.tsx
@@ -57,6 +57,7 @@ export function List<T>(props: ListProps<T> & { ref?: (ref: ListRef) => void })
const i18n = useI18n()
const [scrollRef, setScrollRef] = createSignal<HTMLDivElement | undefined>(undefined)
const [internalFilter, setInternalFilter] = createSignal("")
+ let inputRef: HTMLInputElement | HTMLTextAreaElement | undefined
const [store, setStore] = createStore({
mouseActive: false,
})
@@ -176,6 +177,14 @@ export function List<T>(props: ListProps<T> & { ref?: (ref: ListRef) => void })
if (e.key === "Enter" && !e.isComposing) {
e.preventDefault()
if (selected) handleSelect(selected, index)
+ } else if (props.search) {
+ if (e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey && (e.key === "n" || e.key === "p")) {
+ onKeyDown(e)
+ return
+ }
+ if (e.key === "ArrowDown" || e.key === "ArrowUp") {
+ onKeyDown(e)
+ }
} else {
onKeyDown(e)
}
@@ -247,7 +256,21 @@ export function List<T>(props: ListProps<T> & { ref?: (ref: ListRef) => void })
<div data-component="list" classList={{ [props.class ?? ""]: !!props.class }}>
<Show when={!!props.search}>
<div data-slot="list-search-wrapper">
- <div data-slot="list-search" classList={{ [searchProps().class ?? ""]: !!searchProps().class }}>
+ <div
+ data-slot="list-search"
+ classList={{ [searchProps().class ?? ""]: !!searchProps().class }}
+ onPointerDown={(event) => {
+ const container = event.currentTarget
+ if (!(container instanceof HTMLElement)) return
+
+ const node = container.querySelector("input, textarea")
+ const input = node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement ? node : inputRef
+ input?.focus()
+
+ // Prevent global listeners (e.g. dnd sensors) from cancelling focus.
+ event.stopPropagation()
+ }}
+ >
<div data-slot="list-search-container">
<Show when={!searchProps().hideIcon}>
<Icon name="magnifying-glass" />
@@ -257,6 +280,9 @@ export function List<T>(props: ListProps<T> & { ref?: (ref: ListRef) => void })
variant="ghost"
data-slot="list-search-input"
type="text"
+ ref={(el: HTMLInputElement | HTMLTextAreaElement) => {
+ inputRef = el
+ }}
value={internalFilter()}
onChange={(value) => applyFilter(value)}
onKeyDown={handleKey}
@@ -271,7 +297,10 @@ export function List<T>(props: ListProps<T> & { ref?: (ref: ListRef) => void })
<IconButton
icon="circle-x"
variant="ghost"
- onClick={() => applyFilter("")}
+ onClick={() => {
+ setInternalFilter("")
+ queueMicrotask(() => inputRef?.focus())
+ }}
aria-label={i18n.t("ui.list.clearFilter")}
/>
</Show>