Files
tf_code/packages/ui/src/pierre/file-selection.ts
2026-02-26 18:23:04 -06:00

86 lines
2.8 KiB
TypeScript

import { type SelectedLineRange } from "@pierre/diffs"
import { toRange } from "./selection-bridge"
export function findElement(node: Node | null): HTMLElement | undefined {
if (!node) return
if (node instanceof HTMLElement) return node
return node.parentElement ?? undefined
}
export function findFileLineNumber(node: Node | null): number | undefined {
const el = findElement(node)
if (!el) return
const line = el.closest("[data-line]")
if (!(line instanceof HTMLElement)) return
const value = parseInt(line.dataset.line ?? "", 10)
if (Number.isNaN(value)) return
return value
}
export function findDiffLineNumber(node: Node | null): number | undefined {
const el = findElement(node)
if (!el) return
const line = el.closest("[data-line], [data-alt-line]")
if (!(line instanceof HTMLElement)) return
const primary = parseInt(line.dataset.line ?? "", 10)
if (!Number.isNaN(primary)) return primary
const alt = parseInt(line.dataset.altLine ?? "", 10)
if (!Number.isNaN(alt)) return alt
}
export function findCodeSelectionSide(node: Node | null): SelectedLineRange["side"] {
const el = findElement(node)
if (!el) return
const code = el.closest("[data-code]")
if (!(code instanceof HTMLElement)) return
if (code.hasAttribute("data-deletions")) return "deletions"
return "additions"
}
export function readShadowLineSelection(opts: {
root: ShadowRoot
lineForNode: (node: Node | null) => number | undefined
sideForNode?: (node: Node | null) => SelectedLineRange["side"]
preserveTextSelection?: boolean
}) {
const selection =
(opts.root as unknown as { getSelection?: () => Selection | null }).getSelection?.() ?? window.getSelection()
if (!selection || selection.isCollapsed) return
const domRange =
(
selection as unknown as {
getComposedRanges?: (options?: { shadowRoots?: ShadowRoot[] }) => StaticRange[]
}
).getComposedRanges?.({ shadowRoots: [opts.root] })?.[0] ??
(selection.rangeCount > 0 ? selection.getRangeAt(0) : undefined)
const startNode = domRange?.startContainer ?? selection.anchorNode
const endNode = domRange?.endContainer ?? selection.focusNode
if (!startNode || !endNode) return
if (!opts.root.contains(startNode) || !opts.root.contains(endNode)) return
const start = opts.lineForNode(startNode)
const end = opts.lineForNode(endNode)
if (start === undefined || end === undefined) return
const startSide = opts.sideForNode?.(startNode)
const endSide = opts.sideForNode?.(endNode)
const side = startSide ?? endSide
const range: SelectedLineRange = { start, end }
if (side) range.side = side
if (endSide && side && endSide !== side) range.endSide = endSide
return {
range,
text: opts.preserveTextSelection && domRange ? toRange(domRange).cloneRange() : undefined,
}
}