Code
Code displays source text with Tree-sitter syntax highlighting. Use Markdown for documents or Diff for changed lines.
Availability#
| Field | Availability |
|---|---|
| Package | @opentui/core |
| Core renderable | CodeRenderable |
| React | <code> (automatic) |
| Solid | <code> (automatic) |
| Status | Built in |
Basic usage#
Renderable API#
import { CodeRenderable, createCliRenderer, SyntaxStyle, RGBA } from "@opentui/core"
const renderer = await createCliRenderer()
const syntaxStyle = SyntaxStyle.fromStyles({
default: { fg: RGBA.defaultForeground() },
keyword: { fg: RGBA.defaultForeground(), bold: true },
string: { fg: RGBA.fromIndex(247) },
comment: { fg: RGBA.fromIndex(244), italic: true },
})
const code = new CodeRenderable(renderer, {
id: "code",
content: `function hello() {
// This is a comment
const message = "Hello, world!"
return message
}`,
filetype: "javascript",
syntaxStyle,
width: 50,
height: 10,
})
renderer.root.add(code)Creating syntax styles#
Use SyntaxStyle.fromStyles() to define colors and attributes for syntax tokens:
import { SyntaxStyle, RGBA, parseColor } from "@opentui/core"
const syntaxStyle = SyntaxStyle.fromStyles({
// Basic tokens
keyword: { fg: RGBA.fromHex("#FF7B72"), bold: true },
"keyword.import": { fg: RGBA.fromHex("#FF7B72"), bold: true },
"keyword.operator": { fg: RGBA.fromHex("#FF7B72") },
string: { fg: RGBA.fromHex("#A5D6FF") },
comment: { fg: RGBA.fromHex("#8B949E"), italic: true },
number: { fg: RGBA.fromHex("#79C0FF") },
boolean: { fg: RGBA.fromHex("#79C0FF") },
constant: { fg: RGBA.fromHex("#79C0FF") },
// Functions and types
function: { fg: RGBA.fromHex("#D2A8FF") },
"function.call": { fg: RGBA.fromHex("#D2A8FF") },
"function.method.call": { fg: RGBA.fromHex("#D2A8FF") },
type: { fg: RGBA.fromHex("#FFA657") },
constructor: { fg: RGBA.fromHex("#FFA657") },
// Variables and properties
variable: { fg: RGBA.fromHex("#E6EDF3") },
"variable.member": { fg: RGBA.fromHex("#79C0FF") },
property: { fg: RGBA.fromHex("#79C0FF") },
// Operators and punctuation
operator: { fg: RGBA.fromHex("#FF7B72") },
punctuation: { fg: RGBA.fromHex("#F0F6FC") },
"punctuation.bracket": { fg: RGBA.fromHex("#F0F6FC") },
"punctuation.delimiter": { fg: RGBA.fromHex("#C9D1D9") },
// Default fallback
default: { fg: RGBA.fromHex("#E6EDF3") },
})Style properties#
Each style definition can include:
| Property | Type | Description |
|---|---|---|
fg |
RGBA |
Foreground (text) color |
bg |
RGBA |
Background color |
bold |
boolean |
Bold text |
italic |
boolean |
Italic text |
underline |
boolean |
Underlined text |
dim |
boolean |
Dimmed text |
Supported languages#
OpenTUI bundles Tree-sitter parsers for these languages:
- JavaScript and JSX
- TypeScript and TSX
- Markdown and Markdown inline
- Zig
Other grammars require configuration. See the Tree-sitter reference.
Streaming mode#
Enable streaming mode when content arrives incrementally, like LLM output:
const code = new CodeRenderable(renderer, {
id: "streaming-code",
content: "",
filetype: "typescript",
syntaxStyle,
streaming: true, // Enable streaming mode
})
// Later, append content
code.content += "const x = 1\n"
code.content += "const y = 2\n"Streaming mode changes intermediate rendering behavior during repeated updates. In particular, when drawUnstyledText is false, later updates keep the previous rendered buffer visible while a new one-shot highlight completes. Each highlight still processes the complete current content.
Text selection#
Text selection is enabled by default. Set selectable: false to disable it. Use selectionBg and selectionFg to customize selection colors:
const code = new CodeRenderable(renderer, {
id: "code",
content: sourceCode,
filetype: "typescript",
syntaxStyle,
selectable: true,
selectionBg: "#264F78",
selectionFg: "#FFFFFF",
})Concealment#
The conceal option controls whether certain syntax elements (like markdown formatting characters) are hidden:
const code = new CodeRenderable(renderer, {
id: "markdown",
content: "# Heading\n**bold** text",
filetype: "markdown",
syntaxStyle,
conceal: true, // Hide formatting characters
})With line numbers#
Use LineNumberRenderable to add line numbers:
import { CodeRenderable, LineNumberRenderable, RGBA, ScrollBoxRenderable, SyntaxStyle } from "@opentui/core"
const sourceCode = "const answer = 42\nconsole.log(answer)\n"
const syntaxStyle = SyntaxStyle.fromStyles({
default: { fg: RGBA.fromHex("#E6EDF3") },
})
const code = new CodeRenderable(renderer, {
id: "code",
content: sourceCode,
filetype: "typescript",
syntaxStyle,
width: "100%",
})
const lineNumbers = new LineNumberRenderable(renderer, {
id: "code-with-lines",
target: code,
minWidth: 3,
paddingRight: 1,
fg: "#6b7280",
bg: "#161b22",
width: "100%",
})
// Wrap in ScrollBox for scrolling
const scrollbox = new ScrollBoxRenderable(renderer, {
id: "scrollbox",
width: 60,
height: 20,
})
scrollbox.add(lineNumbers)
renderer.root.add(scrollbox)Properties#
| Property | Type | Default | Description |
|---|---|---|---|
content |
string |
"" |
Source code to display |
filetype |
string |
- | Language for syntax highlighting |
syntaxStyle |
SyntaxStyle |
required | Syntax highlighting theme |
streaming |
boolean |
false |
Preserve suitable intermediate rendering during repeated updates |
conceal |
boolean |
true |
Hide concealed syntax elements |
drawUnstyledText |
boolean |
true |
Show unstyled text while highlighting. Streaming limits this to the initial highlight |
treeSitterClient |
TreeSitterClient |
- | Custom Tree-sitter client instance |
Inherited from TextBufferRenderable#
| Property | Type | Default | Description |
|---|---|---|---|
fg |
string | RGBA |
- | Default foreground color |
bg |
string | RGBA |
- | Background color |
selectable |
boolean |
true |
Whether text selection is enabled |
selectionBg |
string | RGBA |
- | Selection background color |
selectionFg |
string | RGBA |
- | Selection foreground color |
wrapMode |
string |
"word" |
Text wrapping: "none", "char", "word" |
textAlign |
"left" | "center" | "right" |
"left" |
Per-line alignment within the text width |
tabIndicator |
string | number |
- | Tab display character or width |
Additional properties#
| Property | Type | Description |
|---|---|---|
lineCount |
number |
Number of lines in the current text buffer |
scrollY |
number |
Current vertical scroll position (get/set) |
scrollX |
number |
Current horizontal scroll position (get/set) |
scrollWidth |
number |
Total scrollable width (read-only) |
scrollHeight |
number |
Total scrollable height (read-only) |
isHighlighting |
boolean |
Whether highlighting is in progress |
plainText |
string |
Plain text in the current text buffer. It can differ from content after concealment or onChunks transforms |
Markdown styles#
For markdown highlighting, use markup-prefixed style names:
const markdownStyle = SyntaxStyle.fromStyles({
"markup.heading": { fg: RGBA.fromHex("#58A6FF"), bold: true },
"markup.heading.1": { fg: RGBA.fromHex("#00FF88"), bold: true, underline: true },
"markup.heading.2": { fg: RGBA.fromHex("#00D7FF"), bold: true },
"markup.bold": { fg: RGBA.fromHex("#F0F6FC"), bold: true },
"markup.strong": { fg: RGBA.fromHex("#F0F6FC"), bold: true },
"markup.italic": { fg: RGBA.fromHex("#F0F6FC"), italic: true },
"markup.list": { fg: RGBA.fromHex("#FF7B72") },
"markup.quote": { fg: RGBA.fromHex("#8B949E"), italic: true },
"markup.raw": { fg: RGBA.fromHex("#A5D6FF") },
"markup.raw.block": { fg: RGBA.fromHex("#A5D6FF") },
"markup.link": { fg: RGBA.fromHex("#58A6FF"), underline: true },
"markup.link.url": { fg: RGBA.fromHex("#58A6FF"), underline: true },
default: { fg: RGBA.fromHex("#E6EDF3") },
})Related components#
Use Markdown for document structure and fenced code. Add a Line number gutter to a code renderable. Use Diff for unified or split patches. Use TextTable for tabular content without syntax parsing.
Read Text and terminal cells for wrapping, graphemes, and display-cell widths.