Getting Started
This guide walks through a generated Charming app. For deeper explanations, use the topic docs linked throughout this page.
Install Charming
Install the CLI gem on your machine:
gem install charming
Generate and run an app:
charming new my_app
cd my_app
bundle install
bundle exec exe/my_app
Generated Files
Generated apps use Rails-like folders:
app/components/
app/controllers/
app/state/
app/views/home/show_view.rb
app/views/layouts/application_layout.rb
config/routes.rb
exe/my_app
lib/my_app.rb
lib/my_app/application.rb
spec/
The executable boots the app with:
Charming.run(MyApp::Application.new)
The generated app flow is:
Route -> Controller Action -> View -> Layout -> Renderer -> Terminal
Read more in Core Concepts.
Routes
Routes live in config/routes.rb:
MyApp::Application.routes do
root "home#show"
screen "/cities/:id", to: "cities#show", title: "City"
end
root maps / to a controller action. screen maps a path to controller#action. Dynamic route segments are available through params.
Read more in Routing.
Controllers And Views
Generated controllers render views by symbol:
module MyApp
class HomeController < ApplicationController
key "ctrl+p", :open_command_palette, scope: :global
key "q", :quit, scope: :global
def show
render :show, home: home, palette: command_palette
end
private
def home
state(:home, HomeState)
end
end
end
There are three pieces in this generated screen:
HomeStatestores durable in-memory state.HomeController#homereturns that state object.render :show, home: homepasses the state object into the view as thehomeassign.
The left side of home: home is the assign name. The right side calls the controller’s home method. Inside the view, that assign becomes the home method, so home.title reads from HomeState#title.
render :show resolves the view from the controller/action name, not from the state class. For HomeController#show, it resolves:
app/views/home/show_view.rb
Views are Ruby classes with access to assigns and view helpers:
module MyApp
module Home
class ShowView < Charming::View
def render
column(
text(home.title, style: theme.title),
text("Press ctrl+p for commands, q to quit.", style: theme.muted),
gap: 1
)
end
end
end
end
Read more in Controllers & Views.
Layouts
Generated apps use a Ruby layout class:
module MyApp
class ApplicationController < Charming::Controller
layout Layouts::ApplicationLayout
focus_ring :sidebar, :content
end
end
That resolves:
app/views/layouts/application_layout.rb
Layouts use yield_content to place the current screen inside shared UI:
def render
screen_layout do
split :horizontal, gap: 1 do
pane(:sidebar, width: 24, border: :rounded, padding: [1, 2]) { "Home\nSettings" }
pane(:content, grow: 1, border: :rounded, padding: [1, 2]) { yield_content }
end
end
end
Read more in Layouts.
State
State classes store durable in-memory TUI state:
module MyApp
class HomeState < ApplicationState
attribute :title, :string, default: "Home"
attribute :count, :integer, default: 0
end
end
Controllers are ephemeral, so use state(:home, HomeState) for state that must survive dispatches.
Read more in State.
Components
Generate reusable widgets with charming generate component NAME, then render them from views with render_component:
def render
render_component StatusBadgeComponent.new(status: home.status, theme: theme)
end
Components can be static renderable objects or interactive widgets that implement handle_key(event) and handle_mouse(event).
Read more in Components.
Themes
Generated apps register built-in themes and default to :phosphor:
class MyApp::Application < Charming::Application
Charming::UI::Theme.built_in_names.each do |theme_name|
theme theme_name.to_sym, built_in: theme_name
end
default_theme :phosphor
end
Templates and components should use semantic tokens:
text "Title", style: theme.title
text "Help", style: theme.muted
Read more in Themes.
Generate More Files
Inside an app:
charming generate screen forecast
charming generate controller forecast show
charming generate view forecast show
charming generate component forecast_card
charming generate model city name:string # database apps
charming generate migration add_region_to_cities region:string
generate can be shortened to g.
Console
Open IRB with the app loaded (Zeitwerk autoloading active, database connected when configured), from the app root:
charming console # or: charming c
Loading development environment (Charming 0.1.0)
irb> app.routes.all
irb> MyApp::City.count
app returns a memoized application instance.
Run Tests
Generated apps include RSpec specs. Run them with:
bundle exec rspec
Database apps run their specs against an isolated db/test.sqlite3 — the generated spec/spec_helper.rb pins CHARMING_ENV=test, loads the schema, and rolls back each example in a transaction.
For controller, template, component, timer, task, and runtime testing patterns, see Testing.
See A Complete App
The framework repository ships a full demo at examples/journal — a Markdown journal exercising forms, hooks, async tasks with progress, modals, toasts, themes, and journey specs. See Example App: Journal.