Something went wrong. Try again.
source dump of claude code forked from oppi.li/claude-code
Something went wrong. Try again.
4.2 kB · 130 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131import type { DOMElement } from './dom.js'import { ClickEvent } from './events/click-event.js'import type { EventHandlerProps } from './events/event-handlers.js'import { nodeCache } from './node-cache.js'
/** * Find the deepest DOM element whose rendered rect contains (col, row). * * Uses the nodeCache populated by renderNodeToOutput — rects are in screen * coordinates with all offsets (including scrollTop translation) already * applied. Children are traversed in reverse so later siblings (painted on * top) win. Nodes not in nodeCache (not rendered this frame, or lacking a * yogaNode) are skipped along with their subtrees. * * Returns the hit node even if it has no onClick — dispatchClick walks up * via parentNode to find handlers. */export function hitTest( node: DOMElement, col: number, row: number,): DOMElement | null { const rect = nodeCache.get(node) if (!rect) return null if ( col < rect.x || col >= rect.x + rect.width || row < rect.y || row >= rect.y + rect.height ) { return null } // Later siblings paint on top; reversed traversal returns topmost hit. for (let i = node.childNodes.length - 1; i >= 0; i--) { const child = node.childNodes[i]! if (child.nodeName === '#text') continue const hit = hitTest(child, col, row) if (hit) return hit } return node}
/** * Hit-test the root at (col, row) and bubble a ClickEvent from the deepest * containing node up through parentNode. Only nodes with an onClick handler * fire. Stops when a handler calls stopImmediatePropagation(). Returns * true if at least one onClick handler fired. */export function dispatchClick( root: DOMElement, col: number, row: number, cellIsBlank = false,): boolean { let target: DOMElement | undefined = hitTest(root, col, row) ?? undefined if (!target) return false
// Click-to-focus: find the closest focusable ancestor and focus it. // root is always ink-root, which owns the FocusManager. if (root.focusManager) { let focusTarget: DOMElement | undefined = target while (focusTarget) { if (typeof focusTarget.attributes['tabIndex'] === 'number') { root.focusManager.handleClickFocus(focusTarget) break } focusTarget = focusTarget.parentNode } } const event = new ClickEvent(col, row, cellIsBlank) let handled = false while (target) { const handler = target._eventHandlers?.onClick as | ((event: ClickEvent) => void) | undefined if (handler) { handled = true const rect = nodeCache.get(target) if (rect) { event.localCol = col - rect.x event.localRow = row - rect.y } handler(event) if (event.didStopImmediatePropagation()) return true } target = target.parentNode } return handled}
/** * Fire onMouseEnter/onMouseLeave as the pointer moves. Like DOM * mouseenter/mouseleave: does NOT bubble — moving between children does * not re-fire on the parent. Walks up from the hit node collecting every * ancestor with a hover handler; diffs against the previous hovered set; * fires leave on the nodes exited, enter on the nodes entered. * * Mutates `hovered` in place so the caller (App instance) can hold it * across calls. Clears the set when the hit is null (cursor moved into a * non-rendered gap or off the root rect). */export function dispatchHover( root: DOMElement, col: number, row: number, hovered: Set<DOMElement>,): void { const next = new Set<DOMElement>() let node: DOMElement | undefined = hitTest(root, col, row) ?? undefined while (node) { const h = node._eventHandlers as EventHandlerProps | undefined if (h?.onMouseEnter || h?.onMouseLeave) next.add(node) node = node.parentNode } for (const old of hovered) { if (!next.has(old)) { hovered.delete(old) // Skip handlers on detached nodes (removed between mouse events) if (old.parentNode) { ;(old._eventHandlers as EventHandlerProps | undefined)?.onMouseLeave?.() } } } for (const n of next) { if (!hovered.has(n)) { hovered.add(n) ;(n._eventHandlers as EventHandlerProps | undefined)?.onMouseEnter?.() } }}