惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
量子位
D
DataBreaches.Net
博客园 - 司徒正美
J
Java Code Geeks
博客园 - 【当耐特】
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
aimingoo的专栏
aimingoo的专栏
B
Blog
The Cloudflare Blog
D
Docker
I
InfoQ
爱范儿
爱范儿
MongoDB | Blog
MongoDB | Blog
腾讯CDC
月光博客
月光博客
Hugging Face - Blog
Hugging Face - Blog
Microsoft Azure Blog
Microsoft Azure Blog
Vercel News
Vercel News
阮一峰的网络日志
阮一峰的网络日志
小众软件
小众软件
S
SegmentFault 最新的问题
GbyAI
GbyAI
有赞技术团队
有赞技术团队

Hacker News: Show HN

PurrrrrFocus: Pomodoro Timer App - App Store Workflow Engine — Multi-Step Orchestration for Bun RapidPhoto: Pro Photo Editor App - App Store GitHub - DheerG/swarms: Achieve extraordinary results with claude code across a variety of tasks SPICE simulation → oscilloscope → verification with Claude Code — Lucas Gerads Show HN: VCoding – A 5 MB native Windows IDE with no dynamic dependencies Show HN: LLMs don't hallucinate because they're bad at math, it's the format GitHub - Agent-FM/agentfm-core: AgentFM is a peer-to-peer network that turns everyday computers into a decentralized AI supercomputer. AgentFM lets you run massive AI workloads directly across a global mesh of idle CPUs and GPUs. Show HN: Tracking Top US Science Olympiad Alumni over Last 25 Years GitHub - Potarix/agent-hub: One place to talk to all your agents Show HN: Runtime security for AI agents(injection,tool abuse, data exfiltration) GitHub - dubeyKartikay/lazyspotify: Terminal Spotify client for macOS and Linux GitHub - the-banana-tool/king-louie: Easy to use GUI Personal AI Assistant. Win/Linux/Mac. Show HN I made my vacation rental bookable by AI agents–no Airbnb, 0% commission GitHub - basteez/jsf-autoreload: maven plugin to enable hot reload on jsf projects uvm32/hosts/host-gdbstub at main · ringtailsoftware/uvm32 GitHub - labsai/EDDI: Config-driven engine that turns JSON into production-grade AI agents. Multi-agent orchestration, 12+ LLM providers, MCP/A2A protocols, RAG, persistent memory, and enterprise compliance (EU AI Act, GDPR, HIPAA). Built on Quarkus. GitHub - glitchnsec/fortyone-oss: AI Executive Assistant Platform Quickstart | Alien GitHub - muxshed/shed: One stream in, or many. Every destination, simultaneously. No cloud middleman, no per-channel fees, no limits. GitHub - ocrbase-hq/ocrbase: 📄 PDF/IMG ->.MD/JSON Document OCR API for PaddleOCR and GLMOCR. Self-hostable. GitHub - impactjo/home-memory: MCP server that lets your AI assistant remember everything about your home. GitHub - Sets88/dbcls: DbCls is a powerful terminal database client that supports various databases GitHub - neptun2000/heor-agent-mcp GitHub - SeanFDZ/macmind: Single-layer transformer in HyperTalk for the classic Macintosh RollQuation: Math Puzzles - Apps on Google Play GitHub - dropbox/witchcraft Show HN: Agent-cache – Multi-tier LLM/tool/session caching for Valkey and Redis GitHub - opentalon/opentalon: OpenTalon is an open-source platform built from the ground up in Go as a robust alternative to OpenClaw LinkedIn™ 职位抓取工具 - Chrome 应用商店
GitHub - re-marked/yokai: Serious React TUI renderer for ...
re-marked · 2026-04-30 · via Hacker News: Show HN

Serious React TUI renderer for serious CLI apps. Pure TypeScript Yoga layout, diff-based rendering, ScrollBox, alt buffer, mouse events, draggable and resizable components.

drag and drop in the terminal

Used by claude-corp. Forked from claude-code-kit, itself a fork of Ink.

Packages

Package Description
@yokai-tui/renderer React reconciler, component library, event system, terminal I/O
@yokai-tui/shared Pure-TypeScript Yoga layout engine, logging, env helpers

How it works

React component tree
  → Reconciler (React 19 host config)
  → DOM + Yoga layout
  → Tree traversal + text wrapping
  → Screen buffer (cell grid)
  → Frame diff → ANSI patches
  → stdout

The renderer double-buffers frames, diffs cell-by-cell, and emits only the minimal ANSI patch sequence each tick. A spinner update or a line of streamed text touches O(changed cells), not O(rows × cols).

Components

import { render, Box, Text, ScrollBox } from '@yokai-tui/renderer'

function App() {
  return (
    <Box flexDirection="column" height="100%">
      <Box flexGrow={1}>
        <ScrollBox stickyScroll>
          <Text>streaming content…</Text>
        </ScrollBox>
      </Box>
      <Box>
        <Text>footer</Text>
      </Box>
    </Box>
  )
}

render(<App />)

<Box> — flex container. Accepts all Yoga layout props: flexDirection, flexGrow, flexShrink, flexBasis, alignItems, justifyContent, gap, margin, padding, width, height, position, top, left, right, bottom, overflow, zIndex (only honored on position: 'absolute').

<Text> — text node. Accepts color, backgroundColor, bold, italic, underline, dimColor, wrap.

<ScrollBox> — scrollable container with imperative scroll API, sticky scroll, viewport culling, and DECSTBM hardware scroll hints.

<AlternateScreen> — enters the terminal alternate buffer with optional mouse tracking on mount, exits cleanly on unmount.

<Link> — OSC 8 hyperlink.

<Draggable> — gesture-captured drag primitive. Raise-on-press, drag-time z boost, optional bounds clamp, onDragStart / onDrag / onDragEnd. dragData payload forwarded to drop targets.

<DropTarget> — receiver side of drag-and-drop. Optional accept(data) => boolean filter, hover lifecycle (onDragEnter / onDragOver / onDragLeave), onDrop on the topmost target containing the cursor at release.

<Resizable> — resize primitive with s, e, se handles. Hover-highlighted chrome, minSize / maxSize clamping, onResizeStart / onResize / onResizeEnd. Defaults overflow: 'hidden' to keep content from bleeding outside the box.

<FocusGroup> — adds arrow-key navigation between focusable descendants without interfering with Tab. direction="row" | "column" | "both", optional wrap, optional isActive. Tab still cycles globally; arrows are bounded to the group.

<FocusRing> — focusable Box with built-in focus-visible border indicator (default cyan border on focus). Pair with <FocusGroup> for keyboard-navigable lists / menus.

<Checkbox>[x] / [ ] boolean toggle. Click / Enter / Space toggles. Defaults claimFocusOnClick={false} so toggling doesn't tear focus from a peer (e.g. live-bound <TextInput>).

<Radio>(x) / ( ) mutually-exclusive selection bound to a value. Same focus model as <Checkbox>. Compose with <FocusGroup> for arrow-key navigation.

<TextInput> — editable text. Single-line or multiline. Caret via the real terminal cursor (IME / a11y correct). Smart bracketed paste, undo/redo, word nav, selection, password masking. Focus-color border by default (configurable via borderColorFocus). Configurable cursor shape / blink / color via cursorStyle / cursorBlink / cursorColor. See docs/components/text-input.md.

ScrollBox API

const ref = useRef<ScrollBoxRef>(null)

<ScrollBox ref={ref} stickyScroll>
  {items}
</ScrollBox>

// Imperative scroll
ref.current.scrollTo(100)
ref.current.scrollBy(10)
ref.current.scrollToBottom()
ref.current.scrollToElement(elementRef, { offset: 2 })

Mouse and keyboard

Mouse tracking is enabled inside <AlternateScreen mouseTracking>. Events are dispatched through the component tree with capture and bubble phases, matching browser event semantics.

<Box
  onClick={(e) => console.log(e.col, e.row)}
  onMouseDown={(e) => console.log(e.col, e.row)}
  onMouseEnter={() => console.log('hover')}
  onMouseLeave={() => console.log('leave')}
  onKeyDown={(e) => console.log(e.key)}
>

Gesture capture

Inside onMouseDown, call event.captureGesture({ onMove, onUp }) to claim subsequent mouse-motion events and the eventual release for that one drag. Selection extension is suppressed for the duration; the captured handlers fire even when the cursor leaves the originally-pressed element's bounds.

<Box
  onMouseDown={(e) => {
    e.captureGesture({
      onMove: (m) => console.log('drag at', m.col, m.row),
      onUp: (u) => console.log('release at', u.col, u.row),
    })
  }}
/>

<Draggable>, <DropTarget>, and <Resizable> are all built on top of this primitive. Reach for them first; reach for raw captureGesture when none of the components fit your interaction.

Keyboard navigation

Tab / Shift+Tab cycle through every tabIndex >= 0 element in the tree (built into the renderer — no setup needed). Arrow-key navigation is opt-in via <FocusGroup>, scoped to its descendants — Tab still cycles globally, arrows are bounded.

import { FocusGroup, FocusRing, Text, useFocusManager } from '@yokai-tui/renderer'

function Menu({ items }) {
  return (
    <FocusGroup direction="column" wrap>
      {items.map((item) => (
        <FocusRing key={item.id} paddingX={1}>
          <Text>{item.label}</Text>
        </FocusRing>
      ))}
    </FocusGroup>
  )
}

<FocusRing> adds a focus-visible border (default cyan) to the focused item — a thin Box wrapper around useFocus that you can replace with custom chrome by inlining useFocus() directly.

For programmatic focus (modal pulling focus on open, status bar showing the focused element), use useFocusManager():

const { focused, focus, focusNext, focusPrevious, blur } = useFocusManager()

For per-element tracking, useFocus():

const { ref, isFocused, focus } = useFocus({ autoFocus: true })
return <Box ref={ref} tabIndex={0} ...>...</Box>

See pnpm demo:focus-nav for a live example with two <FocusGroup>s side-by-side, one column-direction and one row-direction.

Demos

pnpm demo:drag                # three overlapping draggable rectangles
pnpm demo:constrained-drag    # constrained-vs-free drag inside containers
pnpm demo:drag-and-drop       # kanban: cards into columns
pnpm demo:resizable           # two panels, three handles each
pnpm demo:focus-nav           # Tab + arrow navigation between FocusGroups
pnpm demo:text-input          # text input fields: single, multiline, password

Each demo lives in examples/ and is a small .tsx file you can read top-to-bottom — they're meant to be the first place you look when wiring a new interaction.

Install

pnpm add @yokai-tui/renderer react
# or
npm install @yokai-tui/renderer react

See release notes for what's in each version.

Hooks

Hook Description
useInput(handler, options?) Raw keyboard input
useApp() { exit }
useStdin() Stdin stream + isRawModeSupported
useStdout() Stdout stream + write
useTerminalViewport() { columns, rows }, updates on resize
useFocus(options?) { ref, isFocused, focus } — per-element focus tracking + imperative focus
useFocusManager() { focused, focus, focusNext, focusPrevious, blur } — global focus actions, reactive to changes
useInterval(fn, ms) Stable interval that cleans up on unmount

Development

pnpm install
pnpm build          # shared → renderer
pnpm typecheck      # both packages
pnpm lint           # biome
pnpm test           # vitest

Documentation

Full reference under docs/ — getting started, conceptual guides, per-component and per-hook reference, real-world patterns, migration from Ink, and an AGENTS.md for AI assistants writing code that uses yokai.

Contributing

See CONTRIBUTING.md for the rules of the road — branching, granular commits, co-authorship, quality bar, and release workflow.

License

MIT