summaryrefslogtreecommitdiffhomepage
path: root/packages/ui/src/components/code.tsx
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-01-01 08:48:35 -0600
committerAdam <[email protected]>2026-01-01 21:03:03 -0600
commit78940d5b7ee2f3e5020f87b400db1785b37a7d71 (patch)
tree5784212a5a219f9c648b2e3afc6c952ea1a2da46 /packages/ui/src/components/code.tsx
parentb84a1f714bf0b81efdf89a0dd6e35fa2b3e8692a (diff)
downloadopencode-78940d5b7ee2f3e5020f87b400db1785b37a7d71.tar.gz
opencode-78940d5b7ee2f3e5020f87b400db1785b37a7d71.zip
wip(app): file context
Diffstat (limited to 'packages/ui/src/components/code.tsx')
-rw-r--r--packages/ui/src/components/code.tsx105
1 files changed, 102 insertions, 3 deletions
diff --git a/packages/ui/src/components/code.tsx b/packages/ui/src/components/code.tsx
index fda08260f..ed7db368c 100644
--- a/packages/ui/src/components/code.tsx
+++ b/packages/ui/src/components/code.tsx
@@ -1,18 +1,52 @@
-import { type FileContents, File, FileOptions, LineAnnotation } from "@pierre/diffs"
-import { ComponentProps, createEffect, createMemo, splitProps } from "solid-js"
+import { type FileContents, File, FileOptions, LineAnnotation, type SelectedLineRange } from "@pierre/diffs"
+import { ComponentProps, createEffect, createMemo, onCleanup, splitProps } from "solid-js"
import { createDefaultOptions, styleVariables } from "../pierre"
import { getWorkerPool } from "../pierre/worker"
+type SelectionSide = "additions" | "deletions"
+
export type CodeProps<T = {}> = FileOptions<T> & {
file: FileContents
annotations?: LineAnnotation<T>[]
+ selectedLines?: SelectedLineRange | null
class?: string
classList?: ComponentProps<"div">["classList"]
}
+function findElement(node: Node | null): HTMLElement | undefined {
+ if (!node) return
+ if (node instanceof HTMLElement) return node
+ return node.parentElement ?? undefined
+}
+
+function findLineNumber(node: Node | null): number | undefined {
+ const element = findElement(node)
+ if (!element) return
+
+ const line = element.closest("[data-line]")
+ if (!(line instanceof HTMLElement)) return
+
+ const value = parseInt(line.dataset.line ?? "", 10)
+ if (Number.isNaN(value)) return
+
+ return value
+}
+
+function findSide(node: Node | null): SelectionSide | undefined {
+ const element = findElement(node)
+ if (!element) return
+
+ const code = element.closest("[data-code]")
+ if (!(code instanceof HTMLElement)) return
+
+ if (code.hasAttribute("data-deletions")) return "deletions"
+ return "additions"
+}
+
export function Code<T>(props: CodeProps<T>) {
let container!: HTMLDivElement
- const [local, others] = splitProps(props, ["file", "class", "classList", "annotations"])
+
+ const [local, others] = splitProps(props, ["file", "class", "classList", "annotations", "selectedLines"])
const file = createMemo(
() =>
@@ -25,6 +59,57 @@ export function Code<T>(props: CodeProps<T>) {
),
)
+ const getRoot = () => {
+ const host = container.querySelector("diffs-container")
+ if (!(host instanceof HTMLElement)) return
+
+ const root = host.shadowRoot
+ if (!root) return
+
+ return root
+ }
+
+ const handleMouseUp = () => {
+ if (props.enableLineSelection !== true) return
+
+ const root = getRoot()
+ if (!root) return
+
+ const selection = window.getSelection()
+ if (!selection || selection.isCollapsed) return
+
+ const anchor = selection.anchorNode
+ const focus = selection.focusNode
+ if (!anchor || !focus) return
+ if (!root.contains(anchor) || !root.contains(focus)) return
+
+ const start = findLineNumber(anchor)
+ const end = findLineNumber(focus)
+ if (start === undefined || end === undefined) return
+
+ const startSide = findSide(anchor)
+ const endSide = findSide(focus)
+ const side = startSide ?? endSide
+
+ const range: SelectedLineRange = {
+ start,
+ end,
+ }
+
+ if (side) range.side = side
+ if (endSide && side && endSide !== side) range.endSide = endSide
+
+ file().setSelectedLines(range)
+ }
+
+ createEffect(() => {
+ const current = file()
+
+ onCleanup(() => {
+ current.cleanUp()
+ })
+ })
+
createEffect(() => {
container.innerHTML = ""
file().render({
@@ -34,6 +119,20 @@ export function Code<T>(props: CodeProps<T>) {
})
})
+ createEffect(() => {
+ file().setSelectedLines(local.selectedLines ?? null)
+ })
+
+ createEffect(() => {
+ if (props.enableLineSelection !== true) return
+
+ container.addEventListener("mouseup", handleMouseUp)
+
+ onCleanup(() => {
+ container.removeEventListener("mouseup", handleMouseUp)
+ })
+ })
+
return (
<div
data-component="code"