wip(app): line selection

This commit is contained in:
Adam
2026-01-21 18:36:31 -06:00
parent 1e1872aada
commit 99e15caaf6
3 changed files with 115 additions and 31 deletions

View File

@@ -1,5 +1,5 @@
import { type FileContents, File, FileOptions, LineAnnotation, type SelectedLineRange } from "@pierre/diffs"
import { ComponentProps, createEffect, createMemo, onCleanup, splitProps } from "solid-js"
import { ComponentProps, createEffect, createMemo, createSignal, onCleanup, splitProps } from "solid-js"
import { createDefaultOptions, styleVariables } from "../pierre"
import { getWorkerPool } from "../pierre/worker"
@@ -9,7 +9,9 @@ export type CodeProps<T = {}> = FileOptions<T> & {
file: FileContents
annotations?: LineAnnotation<T>[]
selectedLines?: SelectedLineRange | null
commentedLines?: SelectedLineRange[]
onRendered?: () => void
onLineSelectionEnd?: (selection: SelectedLineRange | null) => void
class?: string
classList?: ComponentProps<"div">["classList"]
}
@@ -53,6 +55,8 @@ export function Code<T>(props: CodeProps<T>) {
let dragStart: number | undefined
let dragEnd: number | undefined
let dragMoved = false
let lastSelection: SelectedLineRange | null = null
let pendingSelectionEnd = false
const [local, others] = splitProps(props, [
"file",
@@ -60,9 +64,13 @@ export function Code<T>(props: CodeProps<T>) {
"classList",
"annotations",
"selectedLines",
"commentedLines",
"onRendered",
"onLineSelectionEnd",
])
const [rendered, setRendered] = createSignal(0)
const handleLineClick: FileOptions<T>["onLineClick"] = (info) => {
props.onLineClick?.(info)
@@ -95,6 +103,30 @@ export function Code<T>(props: CodeProps<T>) {
return root
}
const applyCommentedLines = (ranges: SelectedLineRange[]) => {
const root = getRoot()
if (!root) return
const existing = Array.from(root.querySelectorAll("[data-comment-selected]"))
for (const node of existing) {
if (!(node instanceof HTMLElement)) continue
node.removeAttribute("data-comment-selected")
}
for (const range of ranges) {
const start = Math.max(1, Math.min(range.start, range.end))
const end = Math.max(range.start, range.end)
for (let line = start; line <= end; line++) {
const nodes = Array.from(root.querySelectorAll(`[data-line="${line}"]`))
for (const node of nodes) {
if (!(node instanceof HTMLElement)) continue
node.setAttribute("data-comment-selected", "")
}
}
}
}
const notifyRendered = () => {
if (!local.onRendered) return
@@ -203,7 +235,12 @@ export function Code<T>(props: CodeProps<T>) {
if (side) selected.side = side
if (endSide && side && endSide !== side) selected.endSide = endSide
file().setSelectedLines(selected)
setSelectedLines(selected)
}
const setSelectedLines = (range: SelectedLineRange | null) => {
lastSelection = range
file().setSelectedLines(range)
}
const scheduleSelectionUpdate = () => {
@@ -212,6 +249,10 @@ export function Code<T>(props: CodeProps<T>) {
selectionFrame = requestAnimationFrame(() => {
selectionFrame = undefined
updateSelection()
if (!pendingSelectionEnd) return
pendingSelectionEnd = false
props.onLineSelectionEnd?.(lastSelection)
})
}
@@ -221,7 +262,7 @@ export function Code<T>(props: CodeProps<T>) {
const start = Math.min(dragStart, dragEnd)
const end = Math.max(dragStart, dragEnd)
file().setSelectedLines({ start, end })
setSelectedLines({ start, end })
}
const scheduleDragUpdate = () => {
@@ -289,19 +330,22 @@ export function Code<T>(props: CodeProps<T>) {
const handleMouseUp = () => {
if (props.enableLineSelection !== true) return
if (dragStart === undefined) return
if (dragStart !== undefined) {
if (dragMoved) scheduleDragUpdate()
dragStart = undefined
dragEnd = undefined
dragMoved = false
if (dragMoved) {
pendingSelectionEnd = true
scheduleDragUpdate()
scheduleSelectionUpdate()
}
scheduleSelectionUpdate()
dragStart = undefined
dragEnd = undefined
dragMoved = false
}
const handleSelectionChange = () => {
if (props.enableLineSelection !== true) return
if (dragStart === undefined) return
const selection = window.getSelection()
if (!selection || selection.isCollapsed) return
@@ -328,11 +372,18 @@ export function Code<T>(props: CodeProps<T>) {
containerWrapper: container,
})
setRendered((value) => value + 1)
notifyRendered()
})
createEffect(() => {
file().setSelectedLines(local.selectedLines ?? null)
rendered()
const ranges = local.commentedLines ?? []
requestAnimationFrame(() => applyCommentedLines(ranges))
})
createEffect(() => {
setSelectedLines(local.selectedLines ?? null)
})
createEffect(() => {
@@ -367,6 +418,8 @@ export function Code<T>(props: CodeProps<T>) {
dragStart = undefined
dragEnd = undefined
dragMoved = false
lastSelection = null
pendingSelectionEnd = false
})
return (