cellix 0.5.5

The AI companion IDE inside your agent: a Claude Code mod, Codex plugin and Gemini CLI extension
Documentation
import type { ClientModule } from 'claude-code'

type Input = Record<string, unknown>
type State = { queue: Input[] }

/** Catches the pane's keys and pointer. A post is replaced if another comes in the same
 * frame, so input queues here and goes in one post per tick, a run of moves as its last. */
const PaneInput: ClientModule<unknown, State> = (_props, surface) => {
  if (surface.state === undefined) {
    const state: State = { queue: [] }
    surface.setState(state)
    surface.onKey(e => {
      state.queue.push({ type: 'key', key: e.key, ctrl: e.ctrl, shift: e.shift, meta: e.meta })
    })
    surface.onPointer(e => {
      if (e.type === 'enter' || e.type === 'leave') return
      // Only where the pointer ended up matters, so a move replaces the one before it.
      const last = state.queue[state.queue.length - 1]
      if (e.type === 'move' && last?.kind === 'move' && last.button === e.button) state.queue.pop()
      state.queue.push({
        type: 'pointer',
        kind: e.type,
        x: e.x,
        y: e.y,
        fine: e.fine?.x,
        button: e.button,
      })
    })
    surface.every(16, () => {
      if (state.queue.length > 0) {
        surface.post(state.queue.splice(0) as never)
      }
    })
  }
  return surface.elements.Box({ width: surface.columns, height: surface.rows })
}

export default PaneInput