Files
opencode/packages/desktop/src/utils/dom.ts
2025-10-03 09:04:28 -05:00

52 lines
2.0 KiB
TypeScript

export function getCharacterOffsetInLine(lineElement: Element, targetNode: Node, offset: number): number {
const r = document.createRange()
r.selectNodeContents(lineElement)
r.setEnd(targetNode, offset)
return r.toString().length
}
export function getNodeOffsetInLine(lineElement: Element, charIndex: number): { node: Node; offset: number } | null {
const walker = document.createTreeWalker(lineElement, NodeFilter.SHOW_TEXT, null)
let remaining = Math.max(0, charIndex)
let lastText: Node | null = null
let lastLen = 0
let node: Node | null
while ((node = walker.nextNode())) {
const len = node.textContent?.length || 0
lastText = node
lastLen = len
if (remaining <= len) return { node, offset: remaining }
remaining -= len
}
if (lastText) return { node: lastText, offset: lastLen }
if (lineElement.firstChild) return { node: lineElement.firstChild, offset: 0 }
return null
}
export function getSelectionInContainer(
container: HTMLElement,
): { sl: number; sch: number; el: number; ech: number } | null {
const s = window.getSelection()
if (!s || s.rangeCount === 0) return null
const r = s.getRangeAt(0)
const sc = r.startContainer
const ec = r.endContainer
const getLineElement = (n: Node) =>
(n.nodeType === Node.TEXT_NODE ? (n.parentElement as Element) : (n as Element))?.closest(".line")
const sle = getLineElement(sc)
const ele = getLineElement(ec)
if (!sle || !ele) return null
if (!container.contains(sle as Node) || !container.contains(ele as Node)) return null
const cc = container.querySelector("code") as HTMLElement | null
if (!cc) return null
const lines = Array.from(cc.querySelectorAll(".line"))
const sli = lines.indexOf(sle as Element)
const eli = lines.indexOf(ele as Element)
if (sli === -1 || eli === -1) return null
const sl = sli + 1
const el = eli + 1
const sch = getCharacterOffsetInLine(sle as Element, sc, r.startOffset)
const ech = getCharacterOffsetInLine(ele as Element, ec, r.endOffset)
return { sl, sch, el, ech }
}