summaryrefslogtreecommitdiffhomepage
path: root/packages/ui/src/components/code.tsx
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-02 11:42:05 -0600
committerAdam <[email protected]>2026-02-02 14:24:25 -0600
commit69f5f657f2b3b98d213a7bedd46624cda0e78bcd (patch)
tree77fc3b6d3177163e82a85665fc1a09338fd50992 /packages/ui/src/components/code.tsx
parent0405b425f528ce9042ff0eeb511512e239cb1b5f (diff)
downloadopencode-69f5f657f2b3b98d213a7bedd46624cda0e78bcd.tar.gz
opencode-69f5f657f2b3b98d213a7bedd46624cda0e78bcd.zip
chore: cleanup
Diffstat (limited to 'packages/ui/src/components/code.tsx')
-rw-r--r--packages/ui/src/components/code.tsx118
1 files changed, 91 insertions, 27 deletions
diff --git a/packages/ui/src/components/code.tsx b/packages/ui/src/components/code.tsx
index 38dfcd838..0b0646f0e 100644
--- a/packages/ui/src/components/code.tsx
+++ b/packages/ui/src/components/code.tsx
@@ -109,9 +109,8 @@ function installFindShortcuts() {
return
}
- if (isEditable(event.target)) return
-
- const host = hostForNode(document.activeElement) ?? findTarget ?? Array.from(findHosts)[0]
+ const host =
+ hostForNode(document.activeElement) ?? hostForNode(event.target) ?? findTarget ?? Array.from(findHosts)[0]
if (!host) return
event.preventDefault()
@@ -126,9 +125,11 @@ export function Code<T>(props: CodeProps<T>) {
let wrapper!: HTMLDivElement
let container!: HTMLDivElement
let findInput: HTMLInputElement | undefined
+ let findBar: HTMLDivElement | undefined
let findOverlay!: HTMLDivElement
let findOverlayFrame: number | undefined
let findOverlayScroll: HTMLElement[] = []
+ let findPositionFrame: number | undefined
let observer: MutationObserver | undefined
let renderToken = 0
let selectionFrame: number | undefined
@@ -290,6 +291,41 @@ export function Code<T>(props: CodeProps<T>) {
setFindIndex(0)
}
+ const getScrollParent = (el: HTMLElement): HTMLElement | null => {
+ let parent = el.parentElement
+ while (parent) {
+ const style = getComputedStyle(parent)
+ if (style.overflowY === "auto" || style.overflowY === "scroll") return parent
+ parent = parent.parentElement
+ }
+ return null
+ }
+
+ const positionFindBar = () => {
+ if (!findBar || !wrapper) return
+ const scrollParent = getScrollParent(wrapper)
+ if (!scrollParent) {
+ findBar.style.position = "absolute"
+ findBar.style.top = "8px"
+ findBar.style.right = "8px"
+ findBar.style.left = ""
+ return
+ }
+ const scrollTop = scrollParent.scrollTop
+ findBar.style.position = "absolute"
+ findBar.style.top = `${scrollTop + 8}px`
+ findBar.style.right = "8px"
+ findBar.style.left = ""
+ }
+
+ const scheduleFindPosition = () => {
+ if (findPositionFrame !== undefined) return
+ findPositionFrame = requestAnimationFrame(() => {
+ findPositionFrame = undefined
+ positionFindBar()
+ })
+ }
+
const scanFind = (root: ShadowRoot, query: string) => {
const needle = query.toLowerCase()
const out: Range[] = []
@@ -458,6 +494,7 @@ export function Code<T>(props: CodeProps<T>) {
if (!findOpen()) setFindOpen(true)
requestAnimationFrame(() => {
+ positionFindBar()
findInput?.focus()
findInput?.select()
})
@@ -482,6 +519,25 @@ export function Code<T>(props: CodeProps<T>) {
})
})
+ createEffect(() => {
+ if (!findOpen()) return
+ const scrollParent = getScrollParent(wrapper)
+ const target = scrollParent ?? window
+
+ const handler = () => scheduleFindPosition()
+ target.addEventListener("scroll", handler, { passive: true })
+ window.addEventListener("resize", handler, { passive: true })
+
+ onCleanup(() => {
+ target.removeEventListener("scroll", handler)
+ window.removeEventListener("resize", handler)
+ if (findPositionFrame !== undefined) {
+ cancelAnimationFrame(findPositionFrame)
+ findPositionFrame = undefined
+ }
+ })
+ })
+
const applyCommentedLines = (ranges: SelectedLineRange[]) => {
const root = getRoot()
if (!root) return
@@ -862,6 +918,11 @@ export function Code<T>(props: CodeProps<T>) {
dragFrame = undefined
}
+ if (findPositionFrame !== undefined) {
+ cancelAnimationFrame(findPositionFrame)
+ findPositionFrame = undefined
+ }
+
dragStart = undefined
dragEnd = undefined
dragMoved = false
@@ -888,19 +949,18 @@ export function Code<T>(props: CodeProps<T>) {
findTarget = host
}}
>
- <div ref={container} />
- <div ref={findOverlay} class="pointer-events-none absolute inset-0 z-0" />
<Show when={findOpen()}>
<div
- class="absolute top-2 right-2 z-10 flex items-center gap-1 rounded-md border border-border-weak-base bg-surface-raised-base px-2 py-1 shadow-xs-border"
+ ref={findBar}
+ class="z-50 flex h-8 items-center gap-2 rounded-md border border-border-base bg-background-base px-3 shadow-md"
onPointerDown={(e) => e.stopPropagation()}
>
- <Icon name="magnifying-glass" size="small" class="text-text-weak" />
+ <Icon name="magnifying-glass" size="small" class="text-text-weak shrink-0" />
<input
ref={findInput}
placeholder="Find"
value={findQuery()}
- class="w-48 bg-transparent outline-none text-12-regular text-text-strong placeholder:text-text-weak"
+ class="w-40 bg-transparent outline-none text-14-regular text-text-strong placeholder:text-text-weak"
onInput={(e) => {
setFindQuery(e.currentTarget.value)
setFindIndex(0)
@@ -917,30 +977,32 @@ export function Code<T>(props: CodeProps<T>) {
stepFind(e.shiftKey ? -1 : 1)
}}
/>
- <div class="px-1 text-12-regular text-text-weak tabular-nums">
+ <div class="shrink-0 text-12-regular text-text-weak tabular-nums">
{findCount() ? `${findIndex() + 1}/${findCount()}` : "0/0"}
</div>
+ <div class="flex items-center">
+ <button
+ type="button"
+ class="size-6 grid place-items-center rounded text-text-weak hover:bg-surface-base-hover hover:text-text-strong disabled:opacity-40 disabled:pointer-events-none"
+ disabled={findCount() === 0}
+ aria-label="Previous match"
+ onClick={() => stepFind(-1)}
+ >
+ <Icon name="chevron-down" size="small" class="rotate-180" />
+ </button>
+ <button
+ type="button"
+ class="size-6 grid place-items-center rounded text-text-weak hover:bg-surface-base-hover hover:text-text-strong disabled:opacity-40 disabled:pointer-events-none"
+ disabled={findCount() === 0}
+ aria-label="Next match"
+ onClick={() => stepFind(1)}
+ >
+ <Icon name="chevron-down" size="small" />
+ </button>
+ </div>
<button
type="button"
class="size-6 grid place-items-center rounded text-text-weak hover:bg-surface-base-hover hover:text-text-strong"
- disabled={findCount() === 0}
- aria-label="Previous match"
- onClick={() => stepFind(-1)}
- >
- <Icon name="chevron-down" size="small" class="rotate-180" />
- </button>
- <button
- type="button"
- class="size-6 grid place-items-center rounded text-text-weak hover:bg-surface-base-hover hover:text-text-strong"
- disabled={findCount() === 0}
- aria-label="Next match"
- onClick={() => stepFind(1)}
- >
- <Icon name="chevron-down" size="small" />
- </button>
- <button
- type="button"
- class="ml-1 size-6 grid place-items-center rounded text-text-weak hover:bg-surface-base-hover hover:text-text-strong"
aria-label="Close search"
onClick={closeFind}
>
@@ -948,6 +1010,8 @@ export function Code<T>(props: CodeProps<T>) {
</button>
</div>
</Show>
+ <div ref={container} />
+ <div ref={findOverlay} class="pointer-events-none absolute inset-0 z-0" />
</div>
)
}