All skills
msmps avatar

/opentui

@0d61d6d
by Matt Simpsonmsmps/opentui-skill225 stars
9

OpenTUI skill for building terminal user interfaces with the Core, React, or Solid APIs. Use for any TUI task including components, layout, keyboard and keymap handling, animations, and testing.

Use this Skill: https://skilld.dev/gh/msmps/opentui-skill/opentui

This session only. Nothing lands on disk.

referencescomponentscode-diff.md

≈2.4k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Code & Diff Components

Components for source code, line-number gutters, unified diffs, Markdown, and text tables.

Code Component

CodeRenderable displays plain text immediately and applies asynchronous Tree-sitter highlighting when filetype and a parser are available.

Basic Usage

// React / Solid
<code content={sourceCode} filetype="typescript" syntaxStyle={syntaxStyle} />

// Core
const code = new CodeRenderable(renderer, {
  content: sourceCode,
  filetype: "typescript",
  syntaxStyle,
  wrapMode: "none", // "none" | "char" | "word"
})

OpenTUI bundles parsers for JavaScript/JSX, TypeScript/TSX, Markdown, Markdown-inline, and Zig. Other grammars require Tree-sitter asset configuration. Without filetype, Code renders unhighlighted text.

Highlight Hooks

onHighlight can replace the syntax ranges before styling. It receives SimpleHighlight[] tuples and { content, filetype, syntaxStyle }; return an array or undefined, synchronously or asynchronously.

<code
  content={sourceCode}
  filetype="typescript"
  syntaxStyle={syntaxStyle}
  onHighlight={(highlights, context) =>
    highlights.filter((highlight) => highlight[2] !== "comment")
  }
/>

onChunks runs afterward and can replace the resolved TextChunk[]. Its context also includes highlights.

import { detectLinks } from "@opentui/core"

<code
  content={markdown}
  filetype="markdown"
  syntaxStyle={syntaxStyle}
  onChunks={(chunks, context) => detectLinks(chunks, context)}
/>

detectLinks applies links for recognized Markdown/URL highlight scopes.

TextTable Component

TextTableRenderable is Core-only. It displays styled chunk cells with borders, wrapping, width fitting, and selection.

import {
  TextTableRenderable,
  bold,
  fg,
  type TextChunk,
  type TextTableContent,
} from "@opentui/core"

const cell = (text: string): TextChunk[] => [{ __isChunk: true, text }]
const content: TextTableContent = [
  [[bold("Service")], [bold("Status")], [bold("Notes")]],
  [cell("api"), [fg("#00d4aa")("OK")], cell("latency 28ms")],
]

const table = new TextTableRenderable(renderer, {
  content,
  wrapMode: "word",
  columnWidthMode: "full",   // "content" | "full"
  columnFitter: "balanced",  // "proportional" | "balanced"
  cellPadding: 1,
  border: true,
  outerBorder: true,
  borderStyle: "rounded",
  selectable: true,
})
Option Type Default Description
content TextTableContent - Rows of styled chunk cells
wrapMode none | char | word none Cell wrapping
columnWidthMode content | full full Natural or available-width sizing
columnFitter proportional | balanced proportional Distribute constrained width
cellPadding number 0 Horizontal cell padding
border, outerBorder boolean true Inner and outer borders
borderStyle single | double | rounded | heavy single Border glyph set
borderColor ColorInput - Border color
selectable boolean false Participate in text selection

TextTableCellContent is TextChunk[] | null | undefined. Each literal chunk needs __isChunk: true; styled-text helpers such as bold() and fg() already return valid chunks. getSelectedText() and hasSelection() expose selection; vertical drags within one column retain columnar selection.

Line Number Component

LineNumberRenderable is a gutter for another renderable that implements LineInfoProvider; it does not accept source code itself.

// React (use <line_number> in Solid)
<line-number
  ref={lineNumbersRef}
  fg="#6b7280"
  bg="#161b22"
  minWidth={3}
  paddingRight={1}
  lineNumberOffset={0}
>
  <code content={sourceCode} filetype="typescript" syntaxStyle={syntaxStyle} />
</line-number>
// Core
const code = new CodeRenderable(renderer, {
  content: sourceCode,
  filetype: "typescript",
  syntaxStyle,
})
const lineNumbers = new LineNumberRenderable(renderer, {
  target: code,
  minWidth: 3,
  paddingRight: 1,
})

Use methods rather than nonexistent diagnostics, addedLines, removedLines, or highlightedLines props:

lineNumbers.setLineColor(4, "#1a4d1a")
lineNumbers.setLineSign(4, { after: " +", afterColor: "#22c55e" })
lineNumbers.highlightLines(9, 11, "#4d1a1a")
lineNumbers.clearHighlightLines(9, 11)

Other methods include clearLineColor(), setLineColors(), clearAllLineColors(), clearLineSign(), setLineSigns(), and clearAllLineSigns(). lineNumberOffset changes displayed numbering.

Diff Component

DiffRenderable accepts a unified diff string. It does not compute a diff from old and new source strings.

// React / Solid
<diff
  diff={unifiedPatch}
  filetype="typescript"
  syntaxStyle={syntaxStyle}
  view="split"
  syncScroll
  showLineNumbers
/>

// Core
const diff = new DiffRenderable(renderer, {
  diff: unifiedPatch,
  filetype: "typescript",
  syntaxStyle,
  view: "unified",
})

Options

Option Type / Default Description
diff string Unified patch input
view unified | split / unified Display mode
syncScroll boolean / false Keep split panes aligned
filetype string Tree-sitter language
syntaxStyle SyntaxStyle Highlight style
wrapMode word | char | none Source wrapping
conceal boolean / false Conceal syntax tokens
showLineNumbers boolean / true Show line-number gutters
addedBg, removedBg, contextBg ColorInput Whole-line backgrounds
addedContentBg, removedContentBg, contextContentBg ColorInput Changed-content backgrounds
addedLineNumberBg, removedLineNumberBg, lineNumberBg ColorInput Gutter backgrounds
addedSignColor, removedSignColor ColorInput + and - colors

Use view, not mode; use addedBg/removedBg/contextBg, not addedLineColor/removedLineColor/unchangedLineColor. Context lines are already encoded in the patch, so there is no context option. For multi-file input, Diff currently displays only the first parsed file patch.

Programmatic Line Highlighting

diff.setLineColor(10, "#FFFF0030")
diff.clearLineColor(10)
diff.setLineColors(new Map([
  [5, "#FF000030"],
  [10, { bg: "#00FF0030", fg: "#FFFFFF" }],
]))
diff.highlightLines(20, 25, "#0000FF30")
diff.clearHighlightLines(20, 25)
diff.clearAllLineColors()

getHunkRowOffsets() returns display-row offsets for parsed hunks. Re-read it after changing the patch, view, wrapping, or dimensions.

Markdown Component

MarkdownRenderable parses Markdown into styled renderables. Pass a SyntaxStyle for fenced code highlighting.

<markdown
  content={markdownText}
  syntaxStyle={syntaxStyle}
  conceal
  concealCode={false}
  streaming={false}
  internalBlockMode="coalesced"
/>
Option Type Default Description
content string "" Markdown source
syntaxStyle SyntaxStyle - Syntax colors
treeSitterClient TreeSitterClient shared Parser client
conceal boolean true Hide Markdown markers
concealCode boolean false Hide fenced-code markers
streaming boolean false Optimize append-only content
internalBlockMode coalesced | top-level coalesced Internal block grouping
tableOptions MarkdownTableOptions - Table layout and border options

Custom Node Rendering

renderNode receives (token, context). Return a custom renderable, context.defaultRender() for the built-in representation, or null/ undefined as appropriate.

const markdown = new MarkdownRenderable(renderer, {
  content: "# Custom Heading",
  syntaxStyle,
  renderNode(token, context) {
    if (token.type === "heading") {
      return new TextRenderable(renderer, { content: `>> ${token.text} <<` })
    }
    return context.defaultRender()
  },
})

For fenced-code specialization, use createMarkdownCodeBlockRenderer() to dispatch normalized filetypes to custom renderers while retaining the default renderer for unmatched tokens.

Streaming Markdown

<markdown
  content={streamedContent}
  syntaxStyle={syntaxStyle}
  streaming={isStreaming}
  internalBlockMode="top-level"
/>

Keep streaming true while appending and set it false when complete so the final parse can settle. top-level mode exposes stable top-level blocks, which is useful for LLM output and incremental views.

Gotchas

  • React uses <line-number>; Solid uses <line_number>.
  • Code uses content and filetype, not code and language.
  • Diff uses one unified diff string and view, not old/new strings and mode.
  • Line Number wraps a target; it does not render source code by itself.
  • Tree-sitter loading is asynchronous. Use OTUI_TREE_SITTER_WORKER_PATH when packaging requires a custom worker path.
  • Put large Code/Line Number views inside a height-constrained ScrollBox.

See Also

Source: SKILL.md on GitHub

No alerts17d5 checks · Risk SAFE
  • Gen Agent Trust Hub17d

    This skill contains documentation and usage patterns for the OpenTUI framework, designed for building terminal user interfaces across Core, React, and SolidJS. The analysis confirmed that the skill provides legitimate development guidance; no malicious code, persistence mechanisms, or obfuscation were detected. Findings are limited to standard platform capabilities and recommended scaffolding tools.

  • Socket17d

    No alerts

  • Snyk17d

    Risk: LOW · No issues

  • Runlayer7mo

    1/26 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 0d61d6d. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last month.

Steadyupdated last month
Other metadata
metadata
{
  "references": "core, react, solid, components, layout, keyboard, keymap, animation, testing"
}
  • React
  • Testing
  • terminal-ui
  • tui
  • solid
  • components
  • keyboard-input
  • animations
  • layout

README badge

README badge for msmps/opentui-skill

Builds terminal user interfaces with OpenTUI using an imperative core API or React/Solid reconcilers. Covers layout, keyboard handling, animations, components, and testing for full-featured TUI applications.

Generated from the current SKILL.md.

Does this skill cover all three OpenTUI frameworks?
Yes. The skill consolidates the core imperative API, React reconciler, and Solid reconciler. Use the decision tree in the skill to pick the right framework for your task.
How do I start a new OpenTUI project?
Use `create-tui` with options before arguments: `bunx create-tui -t react my-app` (not `bunx create-tui my-app -t react`). Then read the REFERENCE.md for your chosen framework.
What runtime does OpenTUI require?
OpenTUI runs on Bun and uses Zig for native builds. See `./references/core/gotchas.md` for runtime requirements and build guidance.
Can I use `process.exit()` to shut down my TUI?
No. Always use `renderer.destroy()` instead. Calling `process.exit()` directly is a common pitfall covered in `core/gotchas.md`.
How do I apply text styling in React or Solid?
Use nested modifier elements, not props. See `components/text-display.md` for the correct pattern.

Generated from the current SKILL.md. These answers refresh after source changes.