opentui-solid
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseOpenTUI SolidJS Integration
OpenTUI 与 SolidJS 集成
Expert assistance for building terminal UIs with OpenTUI and SolidJS.
为使用OpenTUI和SolidJS构建终端UI提供专业支持。
Quick Start
快速开始
bash
undefinedbash
undefinedInstall dependencies
Install dependencies
bun install @opentui/core @opentui/solid solid-js
undefinedbun install @opentui/core @opentui/solid solid-js
undefinedBasic Setup
基础配置
tsx
import { createCliRenderer } from "@opentui/core"
import { createRoot } from "@opentui/solid"
function App() {
return <text>Hello, OpenTUI SolidJS!</text>
}
async function main() {
const renderer = await createCliRenderer()
createRoot(renderer).render(() => <App />)
}
main()tsx
import { createCliRenderer } from "@opentui/core"
import { createRoot } from "@opentui/solid"
function App() {
return <text>Hello, OpenTUI SolidJS!</text>
}
async function main() {
const renderer = await createCliRenderer()
createRoot(renderer).render(() => <App />)
}
main()SolidJS Reactivity
SolidJS 响应式特性
Signals (Core Primitive)
Signals(核心原语)
Signals are the core of SolidJS reactivity:
tsx
import { createSignal } from "solid-js"
function Counter() {
const [count, setCount] = createSignal(0)
return (
<text>
Count: {count()}
</text>
)
}Key Concepts:
- - Access signal value (getter)
count() - - Update signal value (setter)
setCount(newValue) - Signals automatically track dependencies
Signals是SolidJS响应式的核心:
tsx
import { createSignal } from "solid-js"
function Counter() {
const [count, setCount] = createSignal(0)
return (
<text>
Count: {count()}
</text>
)
}核心概念:
- - 获取Signal值(getter)
count() - - 更新Signal值(setter)
setCount(newValue) - Signals会自动追踪依赖
Derived Signals (Memo)
派生Signals(Memo)
tsx
import { createMemo } from "solid-js"
function DoubleCounter() {
const [count, setCount] = createSignal(0)
const doubleCount = createMemo(() => count() * 2)
return (
<box flexDirection="column">
<text>Count: {count()}</text>
<text>Double: {doubleCount()}</text>
</box>
)
}tsx
import { createMemo } from "solid-js"
function DoubleCounter() {
const [count, setCount] = createSignal(0)
const doubleCount = createMemo(() => count() * 2)
return (
<box flexDirection="column">
<text>Count: {count()}</text>
<text>Double: {doubleCount()}</text>
</box>
)
}Effects (createEffect)
Effects(createEffect)
tsx
import { createEffect } from "solid-js"
function Logger() {
const [count, setCount] = createSignal(0)
createEffect(() => {
console.log("Count changed:", count())
})
return <text>Count: {count()}</text>
}tsx
import { createEffect } from "solid-js"
function Logger() {
const [count, setCount] = createSignal(0)
createEffect(() => {
console.log("Count changed:", count())
})
return <text>Count: {count()}</text>
}OpenTUI SolidJS Hooks
OpenTUI SolidJS Hooks
useKeyboard
useKeyboard
tsx
import { useKeyboard } from "@opentui/solid"
function App() {
useKeyboard((key) => {
if (key.name === "c" && key.ctrl) {
process.exit(0)
}
})
return <text>Press Ctrl+C to exit</text>
}tsx
import { useKeyboard } from "@opentui/solid"
function App() {
useKeyboard((key) => {
if (key.name === "c" && key.ctrl) {
process.exit(0)
}
})
return <text>Press Ctrl+C to exit</text>
}useRenderer
useRenderer
tsx
import { useRenderer } from "@opentui/solid"
function Component() {
const renderer = useRenderer()
const exit = () => {
renderer.destroy()
process.exit(0)
}
return <box onClick={exit}>Exit</box>
}tsx
import { useRenderer } from "@opentui/solid"
function Component() {
const renderer = useRenderer()
const exit = () => {
renderer.destroy()
process.exit(0)
}
return <box onClick={exit}>Exit</box>
}useTerminalDimensions
useTerminalDimensions
tsx
import { useTerminalDimensions } from "@opentui/solid"
function Responsive() {
const dimensions = useTerminalDimensions()
return (
<box>
<text>Size: {dimensions().width}x{dimensions().height}</text>
</box>
)
}tsx
import { useTerminalDimensions } from "@opentui/solid"
function Responsive() {
const dimensions = useTerminalDimensions()
return (
<box>
<text>Size: {dimensions().width}x{dimensions().height}</text>
</box>
)
}useTimeline
useTimeline
tsx
import { useTimeline } from "@opentui/solid"
import { onMount } from "solid-js"
function AnimatedBox() {
let boxRef: any
const timeline = useTimeline({
duration: 1000,
easing: (t) => t,
})
onMount(() => {
timeline.to(boxRef, {
backgroundColor: { r: 255, g: 0, b: 0 },
})
timeline.play()
})
return (
<box ref={boxRef!}>
<text>Animated</text>
</box>
)
}tsx
import { useTimeline } from "@opentui/solid"
import { onMount } from "solid-js"
function AnimatedBox() {
let boxRef: any
const timeline = useTimeline({
duration: 1000,
easing: (t) => t,
})
onMount(() => {
timeline.to(boxRef, {
backgroundColor: { r: 255, g: 0, b: 0 },
})
timeline.play()
})
return (
<box ref={boxRef!}>
<text>Animated</text>
</box>
)
}SolidJS Components
SolidJS 组件
All OpenTUI components available as JSX:
所有OpenTUI组件均支持JSX形式使用:
tsx
import {
text,
box,
input,
select,
scrollbox,
code,
} from "@opentui/solid"
function Form() {
const [name, setName] = createSignal("")
return (
<box flexDirection="column" gap={1}>
<text decoration="bold">User Information</text>
<input
value={name()}
onInput={(e) => setName(e.value)}
placeholder="Name"
/>
<box borderStyle="single">
<text>Submit</text>
</box>
</box>
)
}tsx
import {
text,
box,
input,
select,
scrollbox,
code,
} from "@opentui/solid"
function Form() {
const [name, setName] = createSignal("")
return (
<box flexDirection="column" gap={1}>
<text decoration="bold">User Information</text>
<input
value={name()}
onInput={(e) => setName(e.value)}
placeholder="Name"
/>
<box borderStyle="single">
<text>Submit</text>
</box>
</box>
)
}Styling in SolidJS
SolidJS 中的样式
Styles are passed as props:
tsx
function StyledComponent() {
return (
<box
borderStyle="double"
borderColor={{ r: 100, g: 149, b: 237 }}
backgroundColor={{ r: 30, g: 30, b: 30 }}
padding={1}
>
<text
foregroundColor={{ r: 255, g: 255, b: 255 }}
decoration="bold"
>
Styled Text
</text>
</box>
)
}Color format:
{ r: number, g: number, b: number, a?: number }样式通过props传递:
tsx
function StyledComponent() {
return (
<box
borderStyle="double"
borderColor={{ r: 100, g: 149, b: 237 }}
backgroundColor={{ r: 30, g: 30, b: 30 }}
padding={1}
>
<text
foregroundColor={{ r: 255, g: 255, b: 255 }}
decoration="bold"
>
Styled Text
</text>
</box>
)
}颜色格式:
{ r: number, g: number, b: number, a?: number }State Management
状态管理
Local Signals
本地Signals
tsx
function Counter() {
const [count, setCount] = createSignal(0)
const [step, setStep] = createSignal(1)
const increment = () => setCount(c => c + step())
const decrement = () => setCount(c => c - step())
useKeyboard((key) => {
if (key.name === "up") increment()
if (key.name === "down") decrement()
})
return (
<box flexDirection="column">
<text>Count: {count()}</text>
<input
value={String(step())}
onInput={(e) => setStep(parseInt(e.value) || 1)}
/>
<text>Use arrow keys</text>
</box>
)
}tsx
function Counter() {
const [count, setCount] = createSignal(0)
const [step, setStep] = createSignal(1)
const increment = () => setCount(c => c + step())
const decrement = () => setCount(c => c - step())
useKeyboard((key) => {
if (key.name === "up") increment()
if (key.name === "down") decrement()
})
return (
<box flexDirection="column">
<text>Count: {count()}</text>
<input
value={String(step())}
onInput={(e) => setStep(parseInt(e.value) || 1)}
/>
<text>Use arrow keys</text>
</box>
)
}Stores (Objects)
Stores(对象)
tsx
import { createStore, produce } from "solid-js/store"
function Form() {
const [formData, setFormData] = createStore({
name: "",
email: "",
password: "",
})
const updateField = (field: string) => (value: string) => {
setFormData(field, value)
}
return (
<box flexDirection="column" gap={1}>
<input
value={formData.name}
onInput={(e) => updateField("name")(e.value)}
placeholder="Name"
/>
<input
value={formData.email}
onInput={(e) => updateField("email")(e.value)}
placeholder="Email"
/>
<input
value={formData.password}
onInput={(e) => updateField("password")(e.value)}
placeholder="Password"
password
/>
</box>
)
}tsx
import { createStore, produce } from "solid-js/store"
function Form() {
const [formData, setFormData] = createStore({
name: "",
email: "",
password: "",
})
const updateField = (field: string) => (value: string) => {
setFormData(field, value)
}
return (
<box flexDirection="column" gap={1}>
<input
value={formData.name}
onInput={(e) => updateField("name")(e.value)}
placeholder="Name"
/>
<input
value={formData.email}
onInput={(e) => updateField("email")(e.value)}
placeholder="Email"
/>
<input
value={formData.password}
onInput={(e) => updateField("password")(e.value)}
placeholder="Password"
password
/>
</box>
)
}Context
Context
tsx
import { createContext, useContext } from "solid-js"
const ThemeContext = createContext({
theme: "dark",
setTheme: (theme: string) => {},
})
function ThemeProvider(props: any) {
const [theme, setTheme] = createSignal("dark")
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{props.children}
</ThemeContext.Provider>
)
}
function ThemedComponent() {
const { theme } = useContext(ThemeContext)
return (
<text>Current theme: {theme()}</text>
)
}tsx
import { createContext, useContext } from "solid-js"
const ThemeContext = createContext({
theme: "dark",
setTheme: (theme: string) => {},
})
function ThemeProvider(props: any) {
const [theme, setTheme] = createSignal("dark")
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{props.children}
</ThemeContext.Provider>
)
}
function ThemedComponent() {
const { theme } = useContext(ThemeContext)
return (
<text>Current theme: {theme()}</text>
)
}Common Patterns
常见模式
List with Selection
带选择功能的列表
tsx
function SelectList(props: { items: string[] }) {
const [selectedIndex, setSelectedIndex] = createSignal(0)
useKeyboard((key) => {
const len = props.items.length
if (key.name === "down" || (key.name === "tab" && !key.shift)) {
setSelectedIndex(i => Math.min(i + 1, len - 1))
}
if (key.name === "up" || (key.name === "tab" && key.shift)) {
setSelectedIndex(i => Math.max(i - 1, 0))
}
})
return (
<scrollbox height={20}>
<For each={props.items}>
{(item, index) => (
<box
backgroundColor={
index() === selectedIndex()
? { r: 100, g: 149, b: 237 }
: { r: 30, g: 30, b: 30 }
}
>
<text>
{index() === selectedIndex() ? "> " : " "}{item}
</text>
</box>
)}
</For>
</scrollbox>
)
}tsx
function SelectList(props: { items: string[] }) {
const [selectedIndex, setSelectedIndex] = createSignal(0)
useKeyboard((key) => {
const len = props.items.length
if (key.name === "down" || (key.name === "tab" && !key.shift)) {
setSelectedIndex(i => Math.min(i + 1, len - 1))
}
if (key.name === "up" || (key.name === "tab" && key.shift)) {
setSelectedIndex(i => Math.max(i - 1, 0))
}
})
return (
<scrollbox height={20}>
<For each={props.items}>
{(item, index) => (
<box
backgroundColor={
index() === selectedIndex()
? { r: 100, g: 149, b: 237 }
: { r: 30, g: 30, b: 30 }
}
>
<text>
{index() === selectedIndex() ? "> " : " "}{item}
</text>
</box>
)}
</For>
</scrollbox>
)
}Show/Hide (Conditional Rendering)
显示/隐藏(条件渲染)
tsx
function Modal(props: { isOpen: boolean, onClose: () => void }) {
return (
<Show when={props.isOpen}>
<box
position="absolute"
backgroundColor={{ r: 0, g: 0, b: 0, a: 0.5 }}
onClick={props.onClose}
>
<text>Modal Content</text>
</box>
</Show>
)
}tsx
function Modal(props: { isOpen: boolean, onClose: () => void }) {
return (
<Show when={props.isOpen}>
<box
position="absolute"
backgroundColor={{ r: 0, g: 0, b: 0, a: 0.5 }}
onClick={props.onClose}
>
<text>Modal Content</text>
</box>
</Show>
)
}Switch (Multiple Conditions)
Switch(多条件分支)
tsx
function StatusIndicator(props: { status: "loading" | "success" | "error" }) {
return (
<Switch fallback={<text>Unknown status</text>}>
<Match when={props.status === "loading"}>
<text>Loading...</text>
</Match>
<Match when={props.status === "success"}>
<text foregroundColor={{ r: 46, g: 204, b: 113 }}>Success!</text>
</Match>
<Match when={props.status === "error"}>
<text foregroundColor={{ r: 231, g: 76, b: 60 }}>Error!</text>
</Match>
</Switch>
)
}tsx
function StatusIndicator(props: { status: "loading" | "success" | "error" }) {
return (
<Switch fallback={<text>Unknown status</text>}>
<Match when={props.status === "loading"}>
<text>Loading...</text>
</Match>
<Match when={props.status === "success"}>
<text foregroundColor={{ r: 46, g: 204, b: 113 }}>Success!</text>
</Match>
<Match when={props.status === "error"}>
<text foregroundColor={{ r: 231, g: 76, b: 60 }}>Error!</text>
</Match>
</Switch>
)
}Dynamic Components
动态组件
tsx
function DynamicTab(props: { component: () => any }) {
return (
<box>
<Dynamic component={props.component} />
</box>
)
}tsx
function DynamicTab(props: { component: () => any }) {
return (
<box>
<Dynamic component={props.component} />
</box>
)
}When to Use This Skill
何时使用该技能
Use for:
/opentui-solid- Building TUIs with SolidJS
- Using signals and fine-grained reactivity
- JSX-style component development
- High-performance reactive UIs
- SolidJS-specific patterns
For vanilla TypeScript/JavaScript, use
For React development, use
For project scaffolding, use
/opentui/opentui-react/opentui-projects使用的场景:
/opentui-solid- 用SolidJS构建TUI
- 使用Signals和细粒度响应式
- JSX风格组件开发
- 高性能响应式UI
- SolidJS专属模式
对于原生TypeScript/JavaScript,使用
对于React开发,使用
对于项目脚手架,使用
/opentui/opentui-react/opentui-projectsKey SolidJS Differences from React
SolidJS与React的核心差异
| Feature | React | SolidJS |
|---|---|---|
| State | | |
| Effects | | |
| Memos | | |
| Refs | | Direct variable assignment |
| Lists | | |
| Conditionals | | |
| Context | | |
| 特性 | React | SolidJS |
|---|---|---|
| 状态 | | |
| 副作用 | | |
| 记忆化 | | |
| 引用 | | 直接变量赋值 |
| 列表 | | |
| 条件渲染 | | |
| 上下文 | | |
Resources
资源
- Signals & Reactivity - Deep dive on signals
- Components - SolidJS component patterns
- Performance - Optimization techniques
- Integration - SolidJS with OpenTUI
- Signals & 响应式 - Signals深度解析
- 组件 - SolidJS组件模式
- 性能 - 优化技巧
- 集成 - OpenTUI与SolidJS集成
Key Knowledge Sources
核心知识来源
- OpenTUI SolidJS GitHub: https://github.com/sst/opentui/tree/main/packages/solid
- SolidJS Docs: https://www.solidjs.com/docs
- Context7: - SolidJS integration queries
/sst/opentui - Research:
.search-data/research/opentui/
- OpenTUI SolidJS GitHub: https://github.com/sst/opentui/tree/main/packages/solid
- SolidJS 文档: https://www.solidjs.com/docs
- Context7: - SolidJS集成相关查询
/sst/opentui - 研究资料:
.search-data/research/opentui/