@yum-tty/cinnamon-bun
v0.2.0
Published
The fun, functional and stateful way to build terminal apps. A TypeScript port of Bubble Tea for Bun.
Maintainers
Readme
Cinnamon Bun
The fun, functional and stateful way to build terminal apps. A TypeScript port of Bubble Tea for Bun.
Cinnamon Bun is based on The Elm Architecture, which happens to work nicely with TypeScript. It's a delightful way to build terminal applications.
Installation
bun add github:yum-tty/cinnamon-bunOr install from a specific package:
bun add @yum-tty/cinnamon-bunQuick Start
import { NewProgram, Quit, type Model, type Msg, type Cmd } from "cinnamon-bun"
// Define your model
interface CounterModel {
count: number
}
// Initialize the model
function init(): [Model, Cmd] {
return [{ count: 0 }, null]
}
// Handle messages
function update(model: CounterModel, msg: Msg): [Model, Cmd] {
if (msg?.type === "key") {
switch (msg.name) {
case "up":
return [{ ...model, count: model.count + 1 }, null]
case "down":
return [{ ...model, count: model.count - 1 }, null]
case "q":
return [model, Quit()]
}
}
return [model, null]
}
// Render the UI
function view(model: CounterModel): string {
return `
Count: ${model.count}
Press up to increment, down to decrement, q to quit.
`
}
// Run the program
NewProgram({ model: { init, update, view } }).run()Architecture
Cinnamon Bun follows The Elm Architecture:
- Model - Your application state
- Init - Returns the initial model and an optional command
- Update - Handles messages and returns an updated model
- View - Renders the UI based on the model
The Model
The model is any type that holds your application state:
interface MyModel {
items: string[]
cursor: number
selected: Set<number>
}Commands
Commands are IO operations that return messages when complete:
import { Batch, Sequence, Every, Tick } from "cinnamon-bun"
// Run multiple commands concurrently
const cmd = Batch(fetchData(), loadConfig())
// Run commands in sequence
const cmd = Sequence(saveFile(), sendNotification())
// Run a command every second
const cmd = Every(1000, (time) => ({ type: "tick", time }))
// Run a command after a delay
const cmd = Tick(500, () => ({ type: "delayed", data: null }))Messages
Messages are events that trigger updates:
type Msg =
| { type: "increment" }
| { type: "decrement" }
| { type: "set"; value: number }
| { type: "quit" }Options
import { NewProgram, MouseMode } from "cinnamon-bun"
NewProgram({
model: { init, update, view },
altScreen: true, // Use alternate screen buffer
fps: 60, // Set max FPS
mouseMode: MouseMode.Cell, // Enable mouse events
}).run()Components
Cinnamon Bun works with Cinnamon components:
import { TextInput, Focus } from "cinnamon"
import { NewProgram, type Model, type Msg, type Cmd } from "cinnamon-bun"
const input = TextInput()
function init(): [Model, Cmd] {
const [focused, cmd] = Focus(input)
return [{ input: focused }, cmd]
}
function update(model: any, msg: Msg): [any, Cmd] {
// Forward messages to components
const [newInput, cmd] = TextInputUpdate(model.input, msg)
return [{ ...model, input: newInput }, cmd]
}
function view(model: any): string {
return TextInputView(model.input)
}
NewProgram({ model: { init, update, view } }).run()Styling
Use Caramel for styling:
import { NewStyle } from "caramel"
const style = NewStyle()
.bold(true)
.foreground("#7f00ff")
.padding(1, 2)
function view(model: any): string {
return style.render(`Count: ${model.count}`)
}Debugging
Logging
You can't log to stdout with Cinnamon Bun because your TUI is busy occupying that. Log to a file instead:
import { appendFileSync } from "fs"
if (process.env.DEBUG) {
appendFileSync("debug.log", `${new Date().toISOString()} ${message}\n`)
}Terminal Reset
If your program crashes, the terminal may be in a bad state. Run:
resetExamples
See the examples directory for complete working examples:
Ecosystem
| Package | Description | |---------|-------------| | Caramel | Style definitions (Lip Gloss port) | | Cinnamon | UI components (Bubbles port) | | Cinnamon Sprinkles | Logging (Log port) | | Cinnamon Prompts | Interactive prompts (Huh port) | | Cinnamon Marshmallow | Markdown rendering (Glamour port) |
Contributing
Contributions are welcome! Please read our Contributing Guide first.
License
Based on Bubble Tea by Charm.
