Termcourse is a Go 1.26.6 terminal UI for browsing and posting to Discourse forums. It replaces the original Ruby implementation while retaining its browsing, posting, realtime, rendering, localization, theming, and image features.
- Browse Latest, Unread, Private Messages, Hot, New, and Top topic lists.
- Cycle Top periods: daily, weekly, monthly, quarterly, and yearly.
- Rounded, paginated compact/category/stats topic-list layouts, including PM-specific users/replies columns.
- Read complete topics with lazy post-stream loading, scrolling, read-state updates, and a progress footer.
- Create topics, select categories, reply to topics or posts, and like/unlike posts.
- Search posts and open the matching post context.
- Browse/filter notifications and jump to their topic/post.
- Persistent folder tabs above each screen panel: Topics, Search, Notifications, and Compose. Open topics and images remain within the destination that led to them.
- A contextual second rail for topic/notification filters plus search and composition stages, with one spacer row below the masthead.
- Theme-colored folder rails and responsive, clickable footer controls for screen-specific hotkeys, with pointer-hover highlighting and the active theme anchored at bottom right.
- Mouse-clickable tabs, footer controls, and rows plus wheel scrolling, with keyboard navigation retained throughout.
- Cookie login with username/password, TOTP, or backup codes.
- API-key fallback for sites where browser login is unsuitable.
- MessageBus list/topic updates, notification and PM badges, resume positions, and watchdog recovery for cookie sessions.
- Inline multiline composer with cursor movement, line breaks, character validation, submit/cancel controls.
- Built-in English, French, German, and Spanish UI translations.
- Built-in
default,slate,fairground,rust, andhackerthemes plus YAML overrides. - Truecolor, 256-color, and 16-color output.
- GFM Markdown rendering (including lists, quotes, code, tasks, and tables), OSC8 links, emoji substitution, and ANSI/grapheme-aware sizing.
- High-quality inline/fullscreen images through Kitty Unicode placements, with colored
chafasymbols (orviu) as the portable fallback and explicit size/quality limits. - Incremental screen repainting and resize-responsive layouts, with a versioned branded masthead, wide-terminal logo, rounded titled panels, and themed block gauges.
- Rate-limit errors show the server-provided retry duration and local deadline when available, and explicitly identify untimed responses.
Go 1.26.6 or newer is required when installing from source. The shortest installation path is:
go install github.com/merefield/termcourse/cmd/termcourse@latest
termcourse meta.discourse.orgReplace meta.discourse.org with the hostname or URL of any Discourse site. If no credentials are configured, Termcourse prompts for the missing username and password. Password input is hidden.
Confirm the installed release at any time with:
termcourse --versionIf the shell cannot find termcourse, add the Go binary directory to PATH. go install uses GOBIN when configured and otherwise uses $(go env GOPATH)/bin:
export PATH="$(go env GOPATH)/bin:$PATH"To build a local executable instead:
git clone https://github.com/merefield/termcourse.git
cd termcourse
make build
./termcourse meta.discourse.orgmake build creates ./termcourse in the repository root. Without make, use go build -o ./termcourse ./cmd/termcourse. You can also run directly from a checkout without keeping a binary:
go run ./cmd/termcourse meta.discourse.orgFor repeat use, credentials can be supplied in .env or the host-mapped credentials file described under Configuration. The examples below use an installed termcourse; replace it with ./termcourse when running a binary built in the repository. Username/password login enables realtime MessageBus updates:
DISCOURSE_USERNAME="you@example.com" \
DISCOURSE_PASSWORD="your_password" \
termcourse --theme slate --lang fr https://your.discourse.hostAPI-key login (HTTP features only):
DISCOURSE_API_KEY="your_key" \
DISCOURSE_API_USERNAME="your_username" \
termcourse --theme fairground https://your.discourse.hostList the built-in and configured themes, or preview one theme:
termcourse themes
termcourse themes hackerA local .env is loaded automatically. CLI credentials override host credentials from YAML, which override generic environment variables. If both login and API pairs exist, login is tried first unless the host entry selects auth: api.
For contributors, run the local checks with make test and make vet.
Termcourse uses semantic Git tags such as v0.2.0 as the release-version source of truth. Go embeds that module version in binaries installed with go install, while make build injects the current git describe value. termcourse --version and the wide masthead subtitle both use the same resolved build version. Untagged direct development builds append their embedded commit and dirty state to the development version declared in termcourse.go.
The Go version is a replacement rather than a separately configured application. Existing .env files, generic Discourse credential variables, and host entries in credentials.yml remain compatible. Authentication precedence is unchanged, so most users can replace bundle exec bin/termcourse HOST with termcourse HOST and keep their credentials as they are.
Be mindful of these differences:
- Ruby, Bundler, and the gem bundle are no longer required. The Go build produces one
termcourseexecutable; usetermcoursefor an installed binary or./termcoursefor one built in the repository. ./theme.ymlis no longer discovered automatically. Move it to the platform user configuration directory (~/.config/termcourse/theme.ymlon Linux), setTERMCOURSE_THEME_FILE, or pass--theme-file PATH. This prevents the active theme changing with the launch directory.- Existing theme names, all 12 color fields, partial overrides, and the legacy top-level theme-map YAML format remain supported. The newer format can also contain
theme: NAMEand athemes:map. Remove or leaveTERMCOURSE_THEMEblank if the file'stheme:selection should take effect. - Theme files are now checked strictly. Unknown fields, invalid colors, unreadable explicitly selected files, and unknown themes stop startup with an explanatory error instead of being silently ignored or replaced with the default theme.
- If an old
.envcontainsTERMCOURSE_IMAGE_MODE=stable, replace it withbalanced. The supported values arecompat,balanced, andhigh. - The default symbol-thumbnail size changed from 14 lines to 48 columns by 6 lines. Existing explicit
TERMCOURSE_IMAGE_LINESvalues still work; useTERMCOURSE_IMAGE_COLUMNSto set the width. - Automatic color handling now detects terminal capabilities instead of using the Ruby version's platform heuristic.
TERMCOURSE_COLOR_MODE=truecolor,256, or16still forces a specific mode. - New optional controls include
TERMCOURSE_MOUSE,TERMCOURSE_IMAGE_PROTOCOL, andTERMCOURSE_IMAGE_COLUMNS. They require no migration because their defaults preserve automatic behaviour.
There is no new monolithic Go configuration file. Configuration remains split between environment variables, credentials YAML, and theme YAML, and no effective Ruby theme color or authentication option has been removed.
The program looks for host credentials in:
TERMCOURSE_CREDENTIALS_FILE./credentials.yml~/.config/termcourse/credentials.yml
See credentials.example.yml and .env.example.
Theme selection uses the first available value:
--theme NAMETERMCOURSE_THEME- The
theme:value in the theme file default
The five built-in themes are default, slate, fairground, rust, and hacker; they work without any files. Theme files are loaded from --theme-file PATH, then TERMCOURSE_THEME_FILE, then the platform user configuration directory (~/.config/termcourse/theme.yml on Linux). The launch directory is deliberately not consulted.
See theme.example.yml for partial built-in overrides and custom themes. Supported keys are primary, background, highlighted, highlighted_text, borders, bar_backgrounds, separators, list_numbers, list_text, post_username, list_meta, and accent. Colors accept #rrggbb, indexes 0–255, black, white, red, green, blue, yellow, cyan, magenta, gray/grey, or none. Invalid fields, colors, files, and theme names produce actionable errors.
With TERMCOURSE_IMAGE_PROTOCOL=auto, Termcourse probes for Kitty graphics support and uses Unicode virtual placements when available in truecolor mode. This gives inline thumbnails that remain part of the terminal cell layout, plus resize-responsive fullscreen images. Kitty commands are passed through tmux and GNU Screen automatically.
When Kitty is unavailable, Termcourse uses chafa for colored symbol rendering and can use Sixel for fullscreen output on compatible terminals. If chafa is not installed, viu is the secondary fallback. These programs are optional external tools and must be available on PATH; image-free operation needs no external image-rendering tool.
Use TERMCOURSE_IMAGE_PROTOCOL=kitty to force Kitty or TERMCOURSE_IMAGE_PROTOCOL=symbols to disable it. TERMCOURSE_IMAGE_BACKEND selects the fallback tool. Image downloads retain the active Discourse authentication and are constrained by the configured byte, pixel, and terminal-cell limits.
| Variable | Purpose |
|---|---|
DISCOURSE_USERNAME, DISCOURSE_PASSWORD |
Cookie login credentials. |
DISCOURSE_API_KEY, DISCOURSE_API_USERNAME |
API authentication fallback. |
TERMCOURSE_CREDENTIALS_FILE |
Credentials YAML override. |
TERMCOURSE_THEME, TERMCOURSE_THEME_FILE |
Theme name/file; overridden by --theme and --theme-file. |
TERMCOURSE_LANG |
en, fr, de, or es; then LC_ALL, LC_MESSAGES, LANG. |
TERMCOURSE_COLOR_MODE |
auto, truecolor, 256, or 16; auto detects output capabilities. |
TERMCOURSE_LINKS, TERMCOURSE_EMOJI |
Set to 0 to disable. |
TERMCOURSE_MOUSE |
Set to 0 to disable click/wheel capture and retain ordinary terminal text selection. |
TERMCOURSE_IMAGES |
Set to 0 to disable previews. |
TERMCOURSE_IMAGE_PROTOCOL |
auto, kitty, or symbols; auto probes Kitty and falls back safely. |
TERMCOURSE_IMAGE_BACKEND |
auto, chafa, viu, or off. |
TERMCOURSE_IMAGE_MODE |
compat, balanced (default), or high for symbol fallback. |
TERMCOURSE_IMAGE_COLORS |
auto, none, 16, 240, 256, or full. |
TERMCOURSE_IMAGE_COLUMNS, TERMCOURSE_IMAGE_LINES |
Maximum thumbnail size, defaults 48×6 cells. |
TERMCOURSE_IMAGE_MAX_BYTES |
Per-image limit, default 5,242,880. |
TERMCOURSE_IMAGE_QUALITY_FILTER |
Set to 0 to allow noisy previews. |
TERMCOURSE_TICK_MS |
Input/resize poll interval, default 100ms. |
TERMCOURSE_HTTP_DEBUG |
Set to 1 for request status, timing, retry, and rate-limit diagnostics. |
TERMCOURSE_DEBUG, TERMCOURSE_IMAGE_DEBUG |
Set to 1 for UI/MessageBus or image diagnostics. |
Global navigation:
tcycles through the built-in and configured themes; the footer button is also available while typing.TabandShift+Tabmove between the four first-row destinations.- Click a primary or contextual folder tab to select it.
- Click a responsive footer button or use its displayed keyboard shortcut.
- Click a post or row to select it; click a selected list row again to open it.
- The mouse wheel moves through lists and scrolls the expanded post body.
- Hold the terminal's mouse-bypass modifier (commonly Shift) for text selection, or set
TERMCOURSE_MOUSE=0. - Navigation is visibly locked while editing topic titles and post bodies so an accidental click cannot discard a draft; the transient search query remains navigable.
Topic list:
- Arrows move; Enter or
1–0opens. ccreates,nopens notifications,ssearches.fcycles filters;pcycles Top periods.grefreshes;qor Escape quits.
Topic view:
- Up/Down selects posts; Left/Right scrolls the expanded post.
- Click the read-progress track to jump to the corresponding position in the complete topic.
ltoggles like;rreplies to the topic;preplies to the post.ssearches;nopens notifications;xopens an image.- Escape/Backspace goes back;
qquits.
Fullscreen image:
xor Escape closes the image and restores the topic view.- Kitty fullscreen images redraw at the new terminal dimensions when the terminal is resized.
Composer:
- Enter adds a line; arrows move; Backspace deletes.
- Ctrl+D submits; Escape cancels.
Notifications and search use arrows, Enter to open, Escape to return, and q to quit. Notifications use f to cycle filters; search results use n to open notifications.
Realtime updates require a browser-style cookie session, so they are enabled after username/password login. API-key mode retains all HTTP operations but intentionally does not create a realtime session. Login auth follows Discourse's CSRF/cookie flow and prompts for TOTP or backup codes when the server requests a second factor.
For Discourse rate-limit responses, Termcourse prefers the HTTP Retry-After value and falls back to JSON extras.wait_seconds or extras.time_left. The error panel shows a countdown and local retry time when possible. It says explicitly when the server reports that retry is already available or when the server provides no timing information; Termcourse does not invent an unreliable delay.
Set TERMCOURSE_DEBUG=1 to include the server's Discourse-Rate-Limit-Error-Code in the error panel. More detail is available through these opt-in logs in the system temporary directory:
| Variable | Log file |
|---|---|
TERMCOURSE_HTTP_DEBUG=1 |
termcourse_http_debug.txt |
TERMCOURSE_DEBUG=1 |
termcourse_debug.txt |
TERMCOURSE_IMAGE_DEBUG=1 |
termcourse_image_debug.txt |
On Linux the system temporary directory is normally /tmp, unless TMPDIR selects another location. HTTP diagnostics include response status, request duration, Retry-After, and the Discourse limiter code; credentials and response bodies are not logged.
The interface runs on the current Charm v2 stack. Bubble Tea owns raw mode, the alternate screen, synchronized incremental rendering, resize events, cursor state, terminal queries, color downsampling, window metadata, and supported native progress metadata. Bubbles supplies the themed single-line editor and multiline composer, including bracketed paste, word navigation, soft wrapping, cursor behavior, and viewport scrolling. Lip Gloss v2 provides pure theme/layout styles, while Glamour v2/Goldmark renders GFM and x/ansi provides ANSI-safe grapheme measurement, truncation, Kitty graphics encoding, capability responses, and virtual image placement. x/term remains limited to pre-TUI password input, terminal detection, and sizing fallback.
The Discourse MessageBus client remains protocol-specific because MessageBus uses chunk-framed HTTP long polling rather than WebSockets. Its lifecycle and resume semantics remain domain code, while terminal I/O is delegated to Bubble Tea.
Termcourse is available under the MIT License. See COPYRIGHT for the copyright notice.