Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ tuishell handles the common infrastructure of panel-based terminal UIs: layout c
- **Statusline** — mode indicator, project label, spinner, help keybindings
- **Task management** — loading states with automatic spinner and error handling
- **Theming** — 30 semantic color tokens, fully customizable
- **Global keybindings** — help modal, quit, panel toggle, dev-mode keys
- **Global keybindings** — help modal, quit, panel toggle, demo-mode keys

## Installation

Expand Down
2 changes: 1 addition & 1 deletion context.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const (
// Embed this in your app-specific context to add domain fields.
type AppContext struct {
Window tea.WindowSizeMsg
DevMode bool
DemoMode bool
FocusedPanel FocusedPanel
PanelHeight int
}
8 changes: 4 additions & 4 deletions docs/shell.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type Config struct {
RightPanel tea.Model // Optional: details panel
AppIcon string // Optional: icon shown in statusline (e.g. "🎫")
Keybinds help.KeyMap // Required: keybindings for help display
DevMode bool // Optional: enables dev-mode keys
DemoMode bool // Optional: enables demo-mode keys
LeftPanelWidth int // Optional: default 30
LeftPanelStyle lipgloss.Style // Required: style for left panel border/padding
RightPanelStyle lipgloss.Style // Optional: style for right panel
Expand All @@ -28,7 +28,7 @@ type Config struct {
| `MainFrameStyle` | Normal border with `theme.Border` color |
| `RightPanel` | `nil` (no right panel) |
| `AppIcon` | `""` (empty statusline label) |
| `DevMode` | `false` |
| `DemoMode` | `false` |

## Minimal Example

Expand Down Expand Up @@ -64,8 +64,8 @@ s := shell.New(shell.Config{
MainPanel: &issuesPanel{},
RightPanel: &detailsPanel{},
AppIcon: "🦊",
Keybinds: tuishell.GlobalKeys(cfg.DevMode),
DevMode: cfg.DevMode,
Keybinds: tuishell.GlobalKeys(cfg.DemoMode),
DemoMode: cfg.DemoMode,
LeftPanelWidth: 30,
LeftPanelStyle: leftStyle,
RightPanelStyle: rightStyle,
Expand Down
4 changes: 2 additions & 2 deletions docs/theming.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ type Theme struct {
StatusNormal color.Color // Normal mode background
StatusLoading color.Color // Loading mode background
StatusError color.Color // Error mode background
StatusDev color.Color // Dev mode indicator
StatusDemo color.Color // Demo mode indicator
StatusAccent1 color.Color // Accent segment 1
StatusAccent2 color.Color // Accent segment 2
}
Expand Down Expand Up @@ -82,7 +82,7 @@ myTheme := style.Theme{
StatusNormal: lipgloss.Color("#0052CC"),
StatusLoading: lipgloss.Color("#0065FF"),
StatusError: lipgloss.Color("#FF5630"),
StatusDev: lipgloss.Color("#36B37E"),
StatusDemo: lipgloss.Color("#36B37E"),
StatusAccent1: lipgloss.Color("#6554C0"),
StatusAccent2: lipgloss.Color("#403294"),
}
Expand Down
8 changes: 4 additions & 4 deletions keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var CommonKeys = []key.Binding{
GlobalKeys(false).ToggleLeftPanel, GlobalKeys(false).OpenModal, GlobalKeys(false).CloseRightPanel, GlobalKeys(false).Help, GlobalKeys(false).Quit,
}

// DevKeys are additional keybindings shown in dev mode.
// DemoKeys are additional keybindings shown in demo mode.
var DevKeys = []key.Binding{
GlobalKeys(true).ThrowError, GlobalKeys(true).MockFetch,
}
Expand All @@ -42,8 +42,8 @@ func (k GlobalKeyMap) FullHelp() [][]key.Binding {
return [][]key.Binding{CommonKeys}
}

// GlobalKeys returns the global keybindings, optionally including dev-mode keys.
func GlobalKeys(devMode bool) GlobalKeyMap {
// GlobalKeys returns the global keybindings, optionally including demo-mode keys.
func GlobalKeys(demoMode bool) GlobalKeyMap {
keymap := GlobalKeyMap{
Help: key.NewBinding(
key.WithKeys("?"),
Expand All @@ -67,7 +67,7 @@ func GlobalKeys(devMode bool) GlobalKeyMap {
),
}

if devMode {
if demoMode {
keymap.ThrowError = key.NewBinding(
key.WithKeys("E"),
key.WithHelp("E", "throw error"),
Expand Down
2 changes: 1 addition & 1 deletion popover/examples/confirm/theme.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func defaultTheme() style.Theme {
StatusNormal: lipgloss.Color("#6914ff"),
StatusLoading: lipgloss.Color("#1A7A94"),
StatusError: lipgloss.Color("#CE3060"),
StatusDev: lipgloss.Color("#4E8212"),
StatusDemo: lipgloss.Color("#4E8212"),
StatusAccent1: lipgloss.Color("#A550DF"),
StatusAccent2: lipgloss.Color("#6124DF"),
}
Expand Down
2 changes: 1 addition & 1 deletion popover/examples/input/theme.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func defaultTheme() style.Theme {
StatusNormal: lipgloss.Color("#6914ff"),
StatusLoading: lipgloss.Color("#1A7A94"),
StatusError: lipgloss.Color("#CE3060"),
StatusDev: lipgloss.Color("#4E8212"),
StatusDemo: lipgloss.Color("#4E8212"),
StatusAccent1: lipgloss.Color("#A550DF"),
StatusAccent2: lipgloss.Color("#6124DF"),
}
Expand Down
2 changes: 1 addition & 1 deletion popover/examples/list/theme.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func defaultTheme() style.Theme {
StatusNormal: lipgloss.Color("#6914ff"),
StatusLoading: lipgloss.Color("#1A7A94"),
StatusError: lipgloss.Color("#CE3060"),
StatusDev: lipgloss.Color("#4E8212"),
StatusDemo: lipgloss.Color("#4E8212"),
StatusAccent1: lipgloss.Color("#A550DF"),
StatusAccent2: lipgloss.Color("#6124DF"),
}
Expand Down
2 changes: 1 addition & 1 deletion shell/example/theme.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func defaultTheme() style.Theme {
StatusNormal: lipgloss.Color("#6914ff"),
StatusLoading: lipgloss.Color("#1A7A94"),
StatusError: lipgloss.Color("#CE3060"),
StatusDev: lipgloss.Color("#4E8212"),
StatusDemo: lipgloss.Color("#4E8212"),
StatusAccent1: lipgloss.Color("#A550DF"),
StatusAccent2: lipgloss.Color("#6124DF"),
}
Expand Down
6 changes: 3 additions & 3 deletions shell/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type Config struct {
RightPanel tea.Model // optional
AppIcon string // e.g. "🎫" - shown in statusline, combined with selected item
Keybinds help.KeyMap
DevMode bool
DemoMode bool
LeftPanelWidth int // default 30
LeftPanelStyle lipgloss.Style
RightPanelStyle lipgloss.Style
Expand Down Expand Up @@ -77,8 +77,8 @@ func New(cfg Config) Model {
BorderForeground(t.Border)
}

ctx := tuishell.AppContext{FocusedPanel: tuishell.LeftPanel, DevMode: cfg.DevMode}
sl := statusline.New(t, cfg.DevMode, cfg.Keybinds)
ctx := tuishell.AppContext{FocusedPanel: tuishell.LeftPanel, DemoMode: cfg.DemoMode}
sl := statusline.New(t, cfg.DemoMode, cfg.Keybinds)
sl.ProjectLabel = cfg.AppIcon

return Model{
Expand Down
10 changes: 5 additions & 5 deletions shell/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
m.taskErr = msg.Err
} else {
mode := statusline.ModesEnum.Normal
if m.Ctx.DevMode {
mode = statusline.ModesEnum.Dev
if m.Ctx.DemoMode {
mode = statusline.ModesEnum.Demo
}
m.Statusline.Status = mode
m.Statusline.Content = ""
Expand Down Expand Up @@ -197,7 +197,7 @@ func (m Model) Theme() style.Theme { return m.theme }
// When handled is true, the caller should return immediately.
func (m Model) handleGlobalKeys(msg tea.KeyPressMsg) (Model, tea.Cmd, bool) {
match := tuishell.KeyMatcher(msg)
gk := tuishell.GlobalKeys(m.Ctx.DevMode)
gk := tuishell.GlobalKeys(m.Ctx.DemoMode)

switch {
case match(gk.Quit):
Expand Down Expand Up @@ -240,12 +240,12 @@ func (m Model) handleGlobalKeys(msg tea.KeyPressMsg) (Model, tea.Cmd, bool) {
}, true
}

case m.Ctx.DevMode && match(gk.ThrowError):
case m.Ctx.DemoMode && match(gk.ThrowError):
return m, func() tea.Msg {
return tuishell.FinishTaskMsg{Err: fmt.Errorf("simulated error for testing")}
}, true

case m.Ctx.DevMode && match(gk.MockFetch):
case m.Ctx.DemoMode && match(gk.MockFetch):
return m, func() tea.Msg {
return tuishell.StartTaskMsg{Cmd: func() tea.Msg {
time.Sleep(2 * time.Second)
Expand Down
18 changes: 9 additions & 9 deletions statusline/statusline.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ type Modes struct {
Normal string
Loading string
Error string
Dev string
Demo string
}

// ModesEnum contains the available status bar mode values.
var ModesEnum = Modes{
Normal: "NORMAL",
Loading: "LOADING",
Error: "ERROR",
Dev: "DEVELOP",
Demo: "DEMO",
}

// Model holds the state for the status bar.
Expand All @@ -36,21 +36,21 @@ type Model struct {
SpinnerView string
Help help.Model
Keybinds help.KeyMap
devMode bool
demoMode bool
theme style.Theme
}

// New creates a new status bar model.
func New(theme style.Theme, devMode bool, keybinds help.KeyMap) Model {
func New(theme style.Theme, demoMode bool, keybinds help.KeyMap) Model {
status := ModesEnum.Normal
if devMode {
status = ModesEnum.Dev
if demoMode {
status = ModesEnum.Demo
}
return Model{
Status: status,
Keybinds: keybinds,
Help: help.New(),
devMode: devMode,
demoMode: demoMode,
theme: theme,
}
}
Expand Down Expand Up @@ -115,8 +115,8 @@ func (m Model) modeBackground() color.Color {
return t.StatusLoading
case ModesEnum.Error:
return t.StatusError
case ModesEnum.Dev:
return t.StatusDev
case ModesEnum.Demo:
return t.StatusDemo
default:
return t.StatusNormal
}
Expand Down
14 changes: 7 additions & 7 deletions style/presets.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ var Presets = map[string]Theme{
StatusNormal: lipgloss.Color("#45475a"), // Surface1
StatusLoading: lipgloss.Color("#94e2d5"), // Teal
StatusError: lipgloss.Color("#f38ba8"), // Red
StatusDev: lipgloss.Color("#a6e3a1"), // Green
StatusDemo: lipgloss.Color("#a6e3a1"), // Green
StatusAccent1: lipgloss.Color("#cba6f7"), // Mauve
StatusAccent2: lipgloss.Color("#b4befe"), // Lavender
},
Expand Down Expand Up @@ -71,7 +71,7 @@ var Presets = map[string]Theme{
StatusNormal: lipgloss.Color("#494d64"), // Surface1
StatusLoading: lipgloss.Color("#8bd5ca"), // Teal
StatusError: lipgloss.Color("#ed8796"), // Red
StatusDev: lipgloss.Color("#a6da95"), // Green
StatusDemo: lipgloss.Color("#a6da95"), // Green
StatusAccent1: lipgloss.Color("#c6a0f6"), // Mauve
StatusAccent2: lipgloss.Color("#b7bdf8"), // Lavender
},
Expand Down Expand Up @@ -107,7 +107,7 @@ var Presets = map[string]Theme{
StatusNormal: lipgloss.Color("#51576d"), // Surface1
StatusLoading: lipgloss.Color("#81c8be"), // Teal
StatusError: lipgloss.Color("#e78284"), // Red
StatusDev: lipgloss.Color("#a6d189"), // Green
StatusDemo: lipgloss.Color("#a6d189"), // Green
StatusAccent1: lipgloss.Color("#ca9ee6"), // Mauve
StatusAccent2: lipgloss.Color("#babbf1"), // Lavender
},
Expand Down Expand Up @@ -143,7 +143,7 @@ var Presets = map[string]Theme{
StatusNormal: lipgloss.Color("#bcc0cc"), // Surface1
StatusLoading: lipgloss.Color("#179299"), // Teal
StatusError: lipgloss.Color("#d20f39"), // Red
StatusDev: lipgloss.Color("#40a02b"), // Green
StatusDemo: lipgloss.Color("#40a02b"), // Green
StatusAccent1: lipgloss.Color("#8839ef"), // Mauve
StatusAccent2: lipgloss.Color("#7287fd"), // Lavender
},
Expand Down Expand Up @@ -179,7 +179,7 @@ var Presets = map[string]Theme{
StatusNormal: lipgloss.Color("#21202e"), // Surface
StatusLoading: lipgloss.Color("#9ccfd8"), // Foam
StatusError: lipgloss.Color("#eb6f92"), // Love
StatusDev: lipgloss.Color("#3e8fb0"), // Pine
StatusDemo: lipgloss.Color("#3e8fb0"), // Pine
StatusAccent1: lipgloss.Color("#c4a7e7"), // Iris
StatusAccent2: lipgloss.Color("#ebbcba"), // Rose
},
Expand Down Expand Up @@ -215,7 +215,7 @@ var Presets = map[string]Theme{
StatusNormal: lipgloss.Color("#292e42"), // Bg Highlight
StatusLoading: lipgloss.Color("#73daca"), // Teal
StatusError: lipgloss.Color("#f7768e"), // Red
StatusDev: lipgloss.Color("#9ece6a"), // Green
StatusDemo: lipgloss.Color("#9ece6a"), // Green
StatusAccent1: lipgloss.Color("#bb9af7"), // Purple
StatusAccent2: lipgloss.Color("#7aa2f7"), // Blue
},
Expand Down Expand Up @@ -251,7 +251,7 @@ var Presets = map[string]Theme{
StatusNormal: lipgloss.Color("#44475a"), // Current Line
StatusLoading: lipgloss.Color("#8be9fd"), // Cyan
StatusError: lipgloss.Color("#ff5555"), // Red
StatusDev: lipgloss.Color("#50fa7b"), // Green
StatusDemo: lipgloss.Color("#50fa7b"), // Green
StatusAccent1: lipgloss.Color("#bd93f9"), // Purple
StatusAccent2: lipgloss.Color("#ff79c6"), // Pink
},
Expand Down
2 changes: 1 addition & 1 deletion style/theme.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type Theme struct {
StatusNormal color.Color
StatusLoading color.Color
StatusError color.Color
StatusDev color.Color
StatusDemo color.Color
StatusAccent1 color.Color
StatusAccent2 color.Color
}
Loading