# How CloddsBot Displays Boot Progress in TTY Mode: Terminal UI Deep Dive

> Discover how CloddsBot shows boot progress in TTY mode. Explore its TUI, real-time updates, and state machine for a seamless startup experience.

- Repository: [AL/CloddsBot](https://github.com/alsk1992/CloddsBot)
- Tags: deep-dive
- Published: 2026-09-13

---

**CloddsBot renders a live, multi-step boot progress indicator in TTY mode by cycling Unicode spinner frames every 80 milliseconds while updating an in-memory state machine that tracks startup phases from configuration validation to HTTP gateway initialization.**

When starting the CloddsBot from an interactive terminal, the application transforms standard console output into a dynamic dashboard. This terminal UI provides real-time visual feedback during the bot's initialization sequence, replacing static log lines with animated progress indicators that update as each subsystem comes online.

## TTY Detection and Conditional Rendering

The boot display logic lives in [`src/index.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/index.ts) and begins with an environment check. Before rendering any visual elements, the code verifies that stdout is attached to a TTY:

```typescript
if (!process.stdout.isTTY) return;

```

As implemented in [src/index.ts#L52](https://github.com/alsk1992/CloddsBot/blob/main/src/index.ts#L52), this guard clause ensures that non-interactive environments—such as Docker containers or CI pipelines—bypass the spinner entirely. When `process.stdout.isTTY` evaluates to false, CloddsBot falls back to the standard logger defined in [`src/utils/logger.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/utils/logger.ts), emitting plain text INFO messages instead of animated UI elements.

## The Boot Progress State Machine

The visual progression relies on a lightweight state management system that tracks discrete startup phases through structured data objects.

### Step Tracking with startupSteps

At the core of the system lies the `startupSteps` array, which holds objects conforming to the `StartupStep` interface:

```typescript
interface StartupStep {
  name: string;
  status: 'pending' | 'active' | 'success' | 'error';
  detail?: string;
}

```

Located at [src/index.ts#L27-L44](https://github.com/alsk1992/CloddsBot/blob/main/src/index.ts#L27-L44), this structure supports two primary mutation functions: `addStep()` to register new phases and `updateStep()` to modify their status. The lifecycle begins by pre-populating six critical boot phases: **Validating configuration**, **Loading config**, **Connecting to database**, **Starting market feeds**, **Connecting channels**, and **Starting HTTP gateway**.

### Spinner Animation Frames

The visual motion comes from a static array of Unicode Braille patterns cycled by the render loop:

```typescript
const spinnerFrames = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];

```

Defined at [src/index.ts#L35](https://github.com/alsk1992/CloddsBot/blob/main/src/index.ts#L35), these characters create the illusion of rotation when printed sequentially. The animation advances every 80 milliseconds while the boot process remains incomplete.

## Rendering the Live Progress Interface

The terminal UI updates through a coordinated rendering system that clears and redraws the console buffer to create seamless animation.

### The renderProgress() Method

The `renderProgress()` function, spanning lines 51-88 in [src/index.ts](https://github.com/alsk1992/CloddsBot/blob/main/src/index.ts#L51-L88), handles the actual drawing logic. It performs three distinct operations:

1. **Buffer Clearing**: Erases previous output to prevent scrollback accumulation
2. **Header Printing**: Displays the static banner "🚀 Starting Clodds..."
3. **Step Iteration**: Loops through `startupSteps` and prints context-aware icons:
   - **✓** (green checkmark) for completed steps
   - **✗** (red X) for failed steps  
   - **○** for pending steps
   - Current **spinner frame** for active steps

Each rendered line includes optional detail text populated via the `detail` property of the step object, allowing subsystem-specific status messages to appear alongside the visual indicator.

### The Spinner Timer Loop

Animation timing is controlled by `startSpinner()`, which initializes a `setInterval` timer at [src/index.ts#L90-L96](https://github.com/alsk1992/CloddsBot/blob/main/src/index.ts#L90-L96). This background process:

- Advances the current frame index through `spinnerFrames`
- Triggers `renderProgress()` to repaint the UI
- Continues execution until `stopSpinner()` is called upon boot completion or error

The 80-millisecond interval provides smooth animation without excessive CPU overhead during the intensive initialization sequence.

## Lifecycle Integration in main()

The `main()` function orchestrates the connection between business logic and visual feedback. During the startup sequence at [src/index.ts#L92-L104](https://github.com/alsk1992/CloddsBot/blob/main/src/index.ts#L92-L104), the code:

1. Pre-registers all six startup steps with `addStep()`
2. Calls `startSpinner()` to begin the animation loop
3. Updates individual step statuses via `updateStep()` as [`src/gateway/index.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/gateway/index.ts) initializes the database connections and market feeds
4. Invokes `stopSpinner()` to halt the timer once the HTTP gateway binds to its port

This tight integration ensures that the visual progress accurately reflects the internal state of the gateway subsystem, providing users with immediate feedback when specific components stall during initialization.

## Fallback Behavior for Non-TTY Environments

When executed without an attached terminal, the UI layer silently deactivates. In this mode, CloddsBot relies entirely on the Winston-based logger configured in [`src/utils/logger.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/utils/logger.ts), producing output suitable for log aggregation systems:

```bash
$ docker run alsk1992/clodds start
INFO  Starting Clodds...
INFO  Config loaded {port:18789}
INFO  Database connection established
INFO  Clodds is running!

```

This dual-mode architecture ensures that operational visibility remains intact regardless of execution context, whether running interactively in a local terminal or as a daemonized container in production.

## Summary

- **TTY Detection**: `process.stdout.isTTY` check at line 52 gates all visual rendering
- **State Management**: The `startupSteps` array tracks six predefined boot phases using `addStep()` and `updateStep()`
- **Animation**: Unicode Braille spinner frames cycle every 80ms via `setInterval` in `startSpinner()`
- **Rendering**: `renderProgress()` clears the terminal and redraws status icons (✓, ✗, ○, or spinner) for each step
- **Cleanup**: `stopSpinner()` terminates the interval timer upon successful boot or error
- **Fallback**: Non-TTY execution routes to standard logging in [`src/utils/logger.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/utils/logger.ts)

## Frequently Asked Questions

### How does CloddsBot detect whether to show the TTY progress UI?

CloddsBot checks `process.stdout.isTTY` at the entry point in [`src/index.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/index.ts). This Node.js property returns true only when stdout is connected to a terminal, allowing the code to bypass spinner initialization in Docker containers, systemd services, or redirected output scenarios.

### What happens if a boot step fails during the TTY animation?

When `updateStep()` receives an error status, `renderProgress()` displays a red **✗** icon next to that step's name. The spinner continues for remaining active steps, but the failed step remains visually marked, and the final static frame persists after `stopSpinner()` clears the interval timer.

### Can the spinner speed be configured or disabled?

The 80-millisecond interval is hardcoded in the `setInterval` call within `startSpinner()` at line 94. To modify the animation speed or disable motion entirely, you would need to edit this value directly in [`src/index.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/index.ts), as no external configuration hook currently exposes this timing parameter.

### Why does the TTY UI use Unicode Braille characters for the spinner?

The Braille pattern characters (⠋ through ⠏) provide optimal visual density and smooth rotation in monospace terminal fonts. These specific glyphs occupy a single column width while offering eight distinct animation frames, creating a smoother illusion of motion than traditional ASCII spinners (`|`, `/`, `-`, `\`) in modern terminal emulators.