fjtui/SYSTEM.md

9.4 KiB

Gitea TUI (fjtui) System Architecture

This document provides a comprehensive overview of the Gitea/Forgejo TUI Dashboard's system architecture, component layout, and user flows. It uses Mermaid.js diagrams to detail the application structure and page lifecycle transitions.


1. High-Level Architecture

The Gitea TUI client is a modular CLI application written in TypeScript and Node.js. It operates as a local state machine that communicates with a Gitea or Forgejo instance REST API over HTTPS.

graph TD
    subgraph Client ["Gitea TUI Application Context"]
        EP["index.ts (CLI Entry Point)"]
        ConfigManager["config.ts (Configuration Manager)"]
        TuiEngine["tui.ts (TuiEngine Controller)"]
        APILayer["api.ts (API Service Layer)"]
        Types["types.ts (State & Entity Types)"]
    end

    subgraph External ["External Resources"]
        LocalConfig["Local Config (./fjtui.json)"]
        GlobalConfig["Global Config (~/.config/fjtui/fjtui.json)"]
        GiteaAPI["Gitea/Forgejo Instance REST API"]
    end

    subgraph IO ["Terminal User Interface"]
        Stdin["stdin (Raw Keypress Listeners)"]
        Stdout["stdout (Chalk Styled Rendering)"]
    end

    %% Key Relationships
    EP -->|Reads Config / Parses Args| ConfigManager
    EP -->|Direct Connect Check| APILayer
    EP -->|Initializes & Starts| TuiEngine
    
    ConfigManager <-->|Read / Write| LocalConfig
    ConfigManager <-->|Read / Write| GlobalConfig

    TuiEngine -->|Subscribes to Events| Stdin
    TuiEngine -->|Paints Screens| Stdout
    TuiEngine -->|Fetches & Mutates Data| APILayer
    TuiEngine -->|Saves Config| ConfigManager

    APILayer -->|HTTP / REST Requests (Axios)| GiteaAPI

    TuiEngine -.->|Operates on AppState| Types

2. Screen & Page Transition Flow

The terminal application operates as a single-page terminal application (SPTA) with a central screen router state. Below is the transition flow (state machine) representing how keypress actions route the user between screens.

stateDiagram-v2
    [*] --> Launch : Run gitea-tui (Bootstrap or Direct Connect)
    Launch --> Setup : No config / finished or skipped
    Launch --> List : Config exists / direct connect (finished or skipped)

    state Setup {
        [*] --> EnteringCredentials
        EnteringCredentials --> Validating : Enter/Submit Form
        Validating --> EnteringCredentials : Connection Error
    }

    Setup --> RepoPicker : Validated & Repositories Loaded

    state RepoPicker {
        [*] --> BrowsingRepos
        BrowsingRepos --> BrowsingRepos : Filter / Search
    }

    RepoPicker --> Setup : Escape (Go Back)
    RepoPicker --> List : Select Repo (Enter) & Load Issues

    state List {
        [*] --> DisplayingIssues
        DisplayingIssues --> DisplayingIssues : Toggle Filters / Sort / Paginate / Search
    }

    List --> Setup : Change Connection ('o' key)
    List --> CreateIssue : Create Issue ('c' key)
    List --> Details : View Issue Details (Enter)

    state CreateIssue {
        [*] --> FillingIssueForm
        FillingIssueForm --> SavingIssue : Ctrl+S (Submit)
        SavingIssue --> DisplayingIssues : Success (List reloads)
        SavingIssue --> FillingIssueForm : Failure (Shows error)
    }
    CreateIssue --> List : Escape (Cancel)

    state Details {
        [*] --> ViewingDetails
        ViewingDetails --> ViewingDetails : Scroll description & comments
    }

    Details --> List : Escape / Backspace / Q (Go Back)
    Details --> Setup : Change Connection ('o' key)
    Details --> AddComment : Add Comment ('c' key)
    Details --> EditIssue : Edit Issue ('e' key)
    Details --> AddTime : Add Tracked Time ('t' key)
    Details --> SetAssignees : Set Assignees ('a' key)
    Details --> ConfirmStateChange : Toggle Issue State ('x' key)

    state AddComment {
        [*] --> EnteringComment
        EnteringComment --> SubmittingComment : Ctrl+S (Submit)
        SubmittingComment --> ViewingDetails : Success (Comments reload)
        SubmittingComment --> EnteringComment : Failure (Shows error)
    }
    AddComment --> Details : Escape (Cancel)

    state EditIssue {
        [*] --> EditingIssueForm
        EditingIssueForm --> SubmittingEdit : Ctrl+S (Submit)
        SubmittingEdit --> ViewingDetails : Success (Details reload)
        SubmittingEdit --> EditingIssueForm : Failure (Shows error)
    }
    EditIssue --> Details : Escape (Cancel)

    state AddTime {
        [*] --> EnteringTime
        EnteringTime --> SubmittingTime : Ctrl+S (Submit)
        SubmittingTime --> ViewingDetails : Success (Time reloads)
        SubmittingTime --> EnteringTime : Failure (Shows error)
    }
    AddTime --> Details : Escape (Cancel)

    state SetAssignees {
        [*] --> EnteringAssignees
        EnteringAssignees --> SubmittingAssignees : Enter (Submit)
        SubmittingAssignees --> ViewingDetails : Success (Assignees reload)
        SubmittingAssignees --> EnteringAssignees : Failure (Shows error)
    }
    SetAssignees --> Details : Escape (Cancel)

    state ConfirmStateChange {
        [*] --> AwaitingConfirmation
        AwaitingConfirmation --> Details : No / Escape (Cancel)
        AwaitingConfirmation --> AnimatingChange : Yes ('y' key)
        
        state AnimatingChange {
            [*] --> AnimatingClose : If Closing (Gravestones)
            [*] --> AnimatingReopen : If Reopening (Zombies)
            AnimatingClose --> ExecutingStateChangeAPI : Finish Frames (1.5s)
            AnimatingReopen --> ExecutingStateChangeAPI : Finish Frames (1.8s)
        }
        ExecutingStateChangeAPI --> ViewingDetails : Done & Reloaded
    }

3. Data Interaction & Rerender Lifecycle

When the user interacts with the interface, keyboard events are processed by the active screen key handler, state variables are updated, API calls are made if necessary, and a full screen redraw is triggered on the terminal buffer.

The following sequence diagram details the lifecyle of adding a comment to an issue:

sequenceDiagram
    autonumber
    actor User as User Terminal
    participant TUI as TuiEngine (tui.ts)
    participant API as API Layer (api.ts)
    participant Gitea as Gitea REST API

    User->>TUI: Keypress (e.g. 'c' on Details screen)
    TUI->>TUI: Update screen state to 'add-comment'
    TUI->>TUI: Render text input area (addCommentForm)
    TUI-->>User: Display empty comment field
    
    User->>TUI: Type comment text + Ctrl+S
    TUI->>TUI: Set state.loading = true & render spinner
    TUI->>API: createIssueComment(config, issueNumber, commentBody)
    API->>Gitea: POST /repos/{owner}/{repo}/issues/{index}/comments
    Gitea-->>API: 201 Created (Comment entity JSON)
    API-->>TUI: Comment Object
    
    TUI->>TUI: Set screen state to 'details'
    TUI->>TUI: Trigger loadComments(issue) (Async)
    TUI->>API: fetchIssueComments(config, issueNumber)
    API->>Gitea: GET /repos/{owner}/{repo}/issues/{index}/comments
    Gitea-->>API: 200 OK (Comments list JSON)
    API-->>TUI: Comment[] Array
    TUI->>TUI: Set state.loading = false
    TUI->>TUI: Render details screen + updated comments
    TUI-->>User: Display issue details and new comment

4. Module Directories & Code Structure

The project code is organized into a clean multi-file architecture located under src:

  1. index.ts:

    • Sets up Command-line parsing with the commander package.
    • Automatically bootstraps the configuration and constructs the default AppState.
    • Checks CLI credentials. If valid, validates them directly, bypassing the setup page and going straight to the issue dashboard.
    • Instantiates the TuiEngine and starts the app loop.
  2. tui.ts:

    • Contains the core TuiEngine class which manages key listeners on process.stdin (in raw mode).
    • Manages loading spinners and UI animations (Gravestones for closed issues and Zombies for reopened issues).
    • Performs terminal buffer manipulation (e.g. entering alternate screen buffer \x1B[?1049h, hiding cursor \x1B[?25l).
    • Delegates the rendering of specific screens using modular helper functions (e.g., renderSetupScreen, renderListScreen, renderDetailsScreen).
  3. api.ts:

    • Hosts the REST API layer built on top of axios.
    • Interacts with Forgejo/Gitea endpoints such as /repos, /issues, /comments, and /times.
    • Handles network errors and formats them into user-friendly diagnostic messages.
  4. config.ts:

    • Manages persistence of credentials.
    • Reads configuration locally from fjtui.json and globally from ~/.config/fjtui/fjtui.json. Local configurations take priority.
    • Writes updated credentials back to the user's home configuration safely.
  5. types.ts:

    • Declares the TypeScript contracts and interfaces representing the application state (AppState), target forms (SetupForm, CreateIssueForm, etc.), and returned entities (User, Issue, Label, Comment, RepoItem).
  6. ASCII Art Assets:

    • ascii-art.txt: ASCII art loaded at startup to perform the color-shifting launch animation.