diff --git a/.agents/skills/opentui/SKILL.md b/.agents/skills/opentui/SKILL.md new file mode 100644 index 0000000..49dc315 --- /dev/null +++ b/.agents/skills/opentui/SKILL.md @@ -0,0 +1,198 @@ +--- +name: opentui +description: Comprehensive OpenTUI skill for building terminal user interfaces. Covers the core imperative API, React reconciler, and Solid reconciler. Use for any TUI development task including components, layout, keyboard handling, animations, and testing. +metadata: + references: core, react, solid +--- + +# OpenTUI Platform Skill + +Consolidated skill for building terminal user interfaces with OpenTUI. Use decision trees below to find the right framework and components, then load detailed references. + +## Critical Rules + +**Follow these rules in all OpenTUI code:** + +1. **Use `create-tui` for new projects.** See framework `REFERENCE.md` quick starts. +2. **`create-tui` options must come before arguments.** `bunx create-tui -t react my-app` works, `bunx create-tui my-app -t react` does NOT. +3. **Never call `process.exit()` directly.** Use `renderer.destroy()` (see `core/gotchas.md`). +4. **Text styling requires nested tags in React/Solid.** Use modifier elements, not props (see `components/text-display.md`). + +## How to Use This Skill + +### Reference File Structure + +Framework references follow a 5-file pattern. Cross-cutting concepts are single-file guides. + +Each framework in `./references//` contains: + +| File | Purpose | When to Read | +|------|---------|--------------| +| `REFERENCE.md` | Overview, when to use, quick start | **Always read first** | +| `api.md` | Runtime API, components, hooks | Writing code | +| `configuration.md` | Setup, tsconfig, bundling | Configuring a project | +| `patterns.md` | Common patterns, best practices | Implementation guidance | +| `gotchas.md` | Pitfalls, limitations, debugging | Troubleshooting | + +Cross-cutting concepts in `./references//` have `REFERENCE.md` as the entry point. + +### Reading Order + +1. Start with `REFERENCE.md` for your chosen framework +2. Then read additional files relevant to your task: + - Building components -> `api.md` + `components/.md` + - Setting up project -> `configuration.md` + - Layout/positioning -> `layout/REFERENCE.md` + - Keyboard/input handling -> `keyboard/REFERENCE.md` + - Animations -> `animation/REFERENCE.md` + - Troubleshooting -> `gotchas.md` + `testing/REFERENCE.md` + +### Example Paths + +``` +./references/react/REFERENCE.md # Start here for React +./references/react/api.md # React components and hooks +./references/solid/configuration.md # Solid project setup +./references/components/inputs.md # Input, Textarea, Select docs +./references/core/gotchas.md # Core debugging tips +``` + +### Runtime Notes + +OpenTUI runs on Bun and uses Zig for native builds. Read `./references/core/gotchas.md` for runtime requirements and build guidance. + +## Quick Decision Trees + +### "Which framework should I use?" + +``` +Which framework? +├─ I want full control, maximum performance, no framework overhead +│ └─ core/ (imperative API) +├─ I know React, want familiar component patterns +│ └─ react/ (React reconciler) +├─ I want fine-grained reactivity, optimal re-renders +│ └─ solid/ (Solid reconciler) +└─ I'm building a library/framework on top of OpenTUI + └─ core/ (imperative API) +``` + +### "I need to display content" + +``` +Display content? +├─ Plain or styled text -> components/text-display.md +├─ Container with borders/background -> components/containers.md +├─ Scrollable content area -> components/containers.md (scrollbox) +├─ ASCII art banner/title -> components/text-display.md (ascii-font) +├─ Code with syntax highlighting -> components/code-diff.md +├─ Diff viewer (unified/split) -> components/code-diff.md +├─ Line numbers with diagnostics -> components/code-diff.md +└─ Markdown content (streaming) -> components/code-diff.md (markdown) +``` + +### "I need user input" + +``` +User input? +├─ Single-line text field -> components/inputs.md (input) +├─ Multi-line text editor -> components/inputs.md (textarea) +├─ Select from a list (vertical) -> components/inputs.md (select) +├─ Tab-based selection (horizontal) -> components/inputs.md (tab-select) +└─ Custom keyboard shortcuts -> keyboard/REFERENCE.md +``` + +### "I need layout/positioning" + +``` +Layout? +├─ Flexbox-style layouts (row, column, wrap) -> layout/REFERENCE.md +├─ Absolute positioning -> layout/patterns.md +├─ Responsive to terminal size -> layout/patterns.md +├─ Centering content -> layout/patterns.md +└─ Complex nested layouts -> layout/patterns.md +``` + +### "I need animations" + +``` +Animations? +├─ Timeline-based animations -> animation/REFERENCE.md +├─ Easing functions -> animation/REFERENCE.md +├─ Property transitions -> animation/REFERENCE.md +└─ Looping animations -> animation/REFERENCE.md +``` + +### "I need to handle input" + +``` +Input handling? +├─ Keyboard events (keypress, release) -> keyboard/REFERENCE.md +├─ Focus management -> keyboard/REFERENCE.md +├─ Paste events -> keyboard/REFERENCE.md +├─ Mouse events -> components/containers.md +└─ Text selection -> components/text-display.md +``` + +### "I need to test my TUI" + +``` +Testing? +├─ Snapshot testing -> testing/REFERENCE.md +├─ Interaction testing -> testing/REFERENCE.md +├─ Test renderer setup -> testing/REFERENCE.md +└─ Debugging tests -> testing/REFERENCE.md +``` + +### "I need to debug/troubleshoot" + +``` +Troubleshooting? +├─ Runtime errors, crashes -> /gotchas.md +├─ Layout issues -> layout/REFERENCE.md + layout/patterns.md +├─ Input/focus issues -> keyboard/REFERENCE.md +└─ Repro + regression tests -> testing/REFERENCE.md +``` + +### Troubleshooting Index + +- Terminal cleanup, crashes -> `core/gotchas.md` +- Text styling not applying -> `components/text-display.md` +- Input focus/shortcuts -> `keyboard/REFERENCE.md` +- Layout misalignment -> `layout/REFERENCE.md` +- Flaky snapshots -> `testing/REFERENCE.md` + +For component naming differences and text modifiers, see `components/REFERENCE.md`. + +## Product Index + +### Frameworks +| Framework | Entry File | Description | +|-----------|------------|-------------| +| Core | `./references/core/REFERENCE.md` | Imperative API, all primitives | +| React | `./references/react/REFERENCE.md` | React reconciler for declarative TUI | +| Solid | `./references/solid/REFERENCE.md` | SolidJS reconciler for declarative TUI | + +### Cross-Cutting Concepts +| Concept | Entry File | Description | +|---------|------------|-------------| +| Layout | `./references/layout/REFERENCE.md` | Yoga/Flexbox layout system | +| Components | `./references/components/REFERENCE.md` | Component reference by category | +| Keyboard | `./references/keyboard/REFERENCE.md` | Keyboard input handling | +| Animation | `./references/animation/REFERENCE.md` | Timeline-based animations | +| Testing | `./references/testing/REFERENCE.md` | Test renderer and snapshots | + +### Component Categories +| Category | Entry File | Components | +|----------|------------|------------| +| Text & Display | `./references/components/text-display.md` | text, ascii-font, styled text | +| Containers | `./references/components/containers.md` | box, scrollbox, borders | +| Inputs | `./references/components/inputs.md` | input, textarea, select, tab-select | +| Code & Diff | `./references/components/code-diff.md` | code, line-number, diff, markdown | + +## Resources + +**Repository**: https://github.com/anomalyco/opentui +**Core Docs**: https://github.com/anomalyco/opentui/tree/main/packages/core/docs +**Examples**: https://github.com/anomalyco/opentui/tree/main/packages/core/src/examples +**Awesome List**: https://github.com/msmps/awesome-opentui diff --git a/.agents/skills/opentui/references/animation/REFERENCE.md b/.agents/skills/opentui/references/animation/REFERENCE.md new file mode 100644 index 0000000..26dd954 --- /dev/null +++ b/.agents/skills/opentui/references/animation/REFERENCE.md @@ -0,0 +1,431 @@ +# Animation System + +OpenTUI provides a timeline-based animation system for smooth property transitions. + +## Overview + +Animations in OpenTUI use: +- **Timeline**: Orchestrates multiple animations +- **Animation Engine**: Manages timelines and rendering +- **Easing Functions**: Control animation curves + +## When to Use + +Use this reference when you need timeline-driven animations, easing curves, or progressive transitions. + +## Basic Usage + +### React + +```tsx +import { useTimeline } from "@opentui/react" +import { useEffect, useState } from "react" + +function AnimatedBox() { + const [width, setWidth] = useState(0) + + const timeline = useTimeline({ + duration: 2000, + }) + + useEffect(() => { + timeline.add( + { width: 0 }, + { + width: 50, + duration: 2000, + ease: "easeOutQuad", + onUpdate: (anim) => { + setWidth(Math.round(anim.targets[0].width)) + }, + } + ) + }, []) + + return ( + + ) +} +``` + +### Solid + +```tsx +import { useTimeline } from "@opentui/solid" +import { createSignal, onMount } from "solid-js" + +function AnimatedBox() { + const [width, setWidth] = createSignal(0) + + const timeline = useTimeline({ + duration: 2000, + }) + + onMount(() => { + timeline.add( + { width: 0 }, + { + width: 50, + duration: 2000, + ease: "easeOutQuad", + onUpdate: (anim) => { + setWidth(Math.round(anim.targets[0].width)) + }, + } + ) + }) + + return ( + + ) +} +``` + +### Core + +```typescript +import { createCliRenderer, Timeline, engine } from "@opentui/core" + +const renderer = await createCliRenderer() +engine.attach(renderer) + +const timeline = new Timeline({ + duration: 2000, + autoplay: true, +}) + +timeline.add( + { x: 0 }, + { + x: 50, + duration: 2000, + ease: "easeOutQuad", + onUpdate: (anim) => { + box.setLeft(Math.round(anim.targets[0].x)) + }, + } +) + +engine.addTimeline(timeline) +``` + +## Timeline Options + +```typescript +const timeline = useTimeline({ + duration: 2000, // Total duration in ms + loop: false, // Loop the timeline + autoplay: true, // Start automatically + onComplete: () => {}, // Called when timeline completes + onPause: () => {}, // Called when timeline pauses +}) +``` + +## Timeline Methods + +```typescript +// Add animation +timeline.add(target, properties, startTime?) + +// Control playback +timeline.play() // Start/resume +timeline.pause() // Pause +timeline.restart() // Restart from beginning + +// State +timeline.progress // Current progress (0-1) +timeline.duration // Total duration +``` + +## Animation Properties + +```typescript +timeline.add( + { value: 0 }, // Target object with initial values + { + value: 100, // Final value + duration: 1000, // Animation duration in ms + ease: "linear", // Easing function + delay: 0, // Delay before starting + onUpdate: (anim) => { + // Called each frame + const current = anim.targets[0].value + }, + onComplete: () => { + // Called when this animation completes + }, + }, + 0 // Start time in timeline (optional) +) +``` + +## Easing Functions + +Available easing functions: + +### Linear + +| Name | Description | +|------|-------------| +| `linear` | Constant speed | + +### Quad (Power of 2) + +| Name | Description | +|------|-------------| +| `easeInQuad` | Slow start | +| `easeOutQuad` | Slow end | +| `easeInOutQuad` | Slow start and end | + +### Cubic (Power of 3) + +| Name | Description | +|------|-------------| +| `easeInCubic` | Slower start | +| `easeOutCubic` | Slower end | +| `easeInOutCubic` | Slower start and end | + +### Quart (Power of 4) + +| Name | Description | +|------|-------------| +| `easeInQuart` | Even slower start | +| `easeOutQuart` | Even slower end | +| `easeInOutQuart` | Even slower start and end | + +### Expo (Exponential) + +| Name | Description | +|------|-------------| +| `easeInExpo` | Exponential start | +| `easeOutExpo` | Exponential end | +| `easeInOutExpo` | Exponential start and end | + +### Back (Overshoot) + +| Name | Description | +|------|-------------| +| `easeInBack` | Pull back, then forward | +| `easeOutBack` | Overshoot, then settle | +| `easeInOutBack` | Both | + +### Elastic + +| Name | Description | +|------|-------------| +| `easeInElastic` | Elastic start | +| `easeOutElastic` | Elastic end (bouncy) | +| `easeInOutElastic` | Both | + +### Bounce + +| Name | Description | +|------|-------------| +| `easeInBounce` | Bounce at start | +| `easeOutBounce` | Bounce at end | +| `easeInOutBounce` | Both | + +## Patterns + +### Progress Bar + +```tsx +function ProgressBar({ progress }: { progress: number }) { + const [width, setWidth] = useState(0) + const maxWidth = 50 + + const timeline = useTimeline() + + useEffect(() => { + timeline.add( + { value: width }, + { + value: (progress / 100) * maxWidth, + duration: 300, + ease: "easeOutQuad", + onUpdate: (anim) => { + setWidth(Math.round(anim.targets[0].value)) + }, + } + ) + }, [progress]) + + return ( + + Progress: {progress}% + + + + + ) +} +``` + +### Fade In + +```tsx +function FadeIn({ children }) { + const [opacity, setOpacity] = useState(0) + + const timeline = useTimeline() + + useEffect(() => { + timeline.add( + { opacity: 0 }, + { + opacity: 1, + duration: 500, + ease: "easeOutQuad", + onUpdate: (anim) => { + setOpacity(anim.targets[0].opacity) + }, + } + ) + }, []) + + return ( + + {children} + + ) +} +``` + +### Looping Animation + +```tsx +function Spinner() { + const [frame, setFrame] = useState(0) + const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] + + useEffect(() => { + const interval = setInterval(() => { + setFrame(f => (f + 1) % frames.length) + }, 80) + + return () => clearInterval(interval) + }, []) + + return {frames[frame]} Loading... +} +``` + +### Staggered Animation + +```tsx +function StaggeredList({ items }) { + const [visibleCount, setVisibleCount] = useState(0) + + useEffect(() => { + let count = 0 + const interval = setInterval(() => { + count++ + setVisibleCount(count) + if (count >= items.length) { + clearInterval(interval) + } + }, 100) + + return () => clearInterval(interval) + }, [items.length]) + + return ( + + {items.slice(0, visibleCount).map((item, i) => ( + {item} + ))} + + ) +} +``` + +### Slide In + +```tsx +function SlideIn({ children, from = "left" }) { + const [offset, setOffset] = useState(from === "left" ? -20 : 20) + + const timeline = useTimeline() + + useEffect(() => { + timeline.add( + { offset: from === "left" ? -20 : 20 }, + { + offset: 0, + duration: 300, + ease: "easeOutCubic", + onUpdate: (anim) => { + setOffset(Math.round(anim.targets[0].offset)) + }, + } + ) + }, []) + + return ( + + {children} + + ) +} +``` + +## Performance Tips + +### Batch Updates + +Timeline automatically batches updates within the render loop. + +### Use Integer Values + +Round animated values for character-based positioning: + +```typescript +onUpdate: (anim) => { + setX(Math.round(anim.targets[0].x)) +} +``` + +### Clean Up Timelines + +Hooks automatically clean up, but for core: + +```typescript +// When done with timeline +engine.removeTimeline(timeline) +``` + +## Gotchas + +### Terminal Refresh Rate + +Terminal UIs typically refresh at 60 FPS max. Very fast animations may appear choppy. + +### Character Grid + +Animations are constrained to character cells. Sub-pixel positioning isn't possible. + +### Cleanup in Effects + +Always clean up intervals and timelines: + +```tsx +useEffect(() => { + const interval = setInterval(...) + return () => clearInterval(interval) +}, []) +``` + +## See Also + +- [React API](../react/api.md) - `useTimeline` hook reference +- [Solid API](../solid/api.md) - `useTimeline` hook reference +- [Core API](../core/api.md) - `AnimationEngine` and `Timeline` classes +- [Layout Patterns](../layout/patterns.md) - Animated positioning and transitions diff --git a/.agents/skills/opentui/references/components/REFERENCE.md b/.agents/skills/opentui/references/components/REFERENCE.md new file mode 100644 index 0000000..0834960 --- /dev/null +++ b/.agents/skills/opentui/references/components/REFERENCE.md @@ -0,0 +1,143 @@ +# OpenTUI Components + +Reference for all OpenTUI components, organized by category. Components are available in all three frameworks (Core, React, Solid) with slight API differences. + +## When to Use + +Use this reference when you need to find the right component category or compare naming across Core, React, and Solid. + +## Component Categories + +| Category | Components | File | +|----------|------------|------| +| Text & Display | text, ascii-font, styled text | [text-display.md](./text-display.md) | +| Containers | box, scrollbox, borders | [containers.md](./containers.md) | +| Inputs | input, textarea, select, tab-select | [inputs.md](./inputs.md) | +| Code & Diff | code, line-number, diff, markdown | [code-diff.md](./code-diff.md) | + +## Component Chooser + +``` +Need a component? +├─ Styled text or ASCII art -> text-display.md +├─ Containers, borders, scrolling -> containers.md +├─ Forms or input controls -> inputs.md +└─ Code blocks, diffs, line numbers, markdown -> code-diff.md +``` + +## Component Naming + +Components have different names across frameworks: + +| Concept | Core (Class) | React (JSX) | Solid (JSX) | +|---------|--------------|-------------|-------------| +| Text | `TextRenderable` | `` | `` | +| Box | `BoxRenderable` | `` | `` | +| ScrollBox | `ScrollBoxRenderable` | `` | `` | +| Input | `InputRenderable` | `` | `` | +| Textarea | `TextareaRenderable` | `