jjui

repository·main·Indexed 24 days ago

https://github.com/idursun/jjui

A terminal user interface (TUI) for the Jujutsu (jj) version control system. It provides enhanced workflows for managing revisions, rebasing, squashing, and inspecting the operation log. Requires Jujutsu v0.37 or newer.

Tokens
3K
Snippets
3
Records
18
Agent score
85%

What's inside jjui

  1. Understand jjui features

    main

    Revision Tree Operations

    • Revset Auto-complete: Change revsets with real-time auto-complete and signature help.
    • Rebase: Rebase a revision or a branch onto another revision in the tree.
    • Squash: Combine revisions using the S key.
    • Bookmarks: Move bookmarks to the currently selected revision.

    Inspection and Preview

    • Revision Details: Use l to inspect a revision, allowing for file restoration, splitting, and diff viewing.
    • Op Log: Use o to view the operation log and r to restore operations.
    • Preview Window: Use p to see the output of jj show (for revisions), jj diff (for files), or jj op show (for operations).
  2. Understand the input and intent pipeline

    main

    jjui uses a decoupled input architecture to ensure that models remain independent of specific key bindings. The flow of an input event is:

    1. Key: The raw key press received from Bubble Tea.
    2. Binding: A scoped runtime binding (configured via the dispatcher) that maps a key or sequence to an Action.
    3. Action: A declarative identifier for a capability. Actions are bridged to intents via a generated catalog.
    4. Intent: The application-level abstraction of what should happen (e.g., Undo, NextRevision). Models implement the logic for these intents.
    5. Model Handler: The specific UI model that receives and executes the intent.

    Key Rule: Models should never handle raw key bindings; they should only implement behavior for Intents.

  3. How the jjui architecture works

    main

    jjui is built on bubbletea/v2 but uses an immediate-mode rendering model rather than a traditional string-returning model. The architecture is composed of several key layers:

    • Event Loop: Managed by Bubble Tea for message passing and process lifecycle.
    • Root Orchestrator: The root UI model (in internal/ui/ui.go) manages composition, routing, focus decisions, and top-level lifecycle actions (like quit, help, undo, redo).
    • Rendering: Uses immediate-mode rendering where UI components draw primitives into a shared DisplayContext instead of returning strings. This allows for compositional, layout-driven rendering.
    • Input Pipeline: Decouples raw input from behavior using a multi-stage pipeline: key $\rightarrow$ binding $\rightarrow$ action $\rightarrow$ intent $\rightarrow$ model handler.
    • Frame Scheduling: To maintain performance, the root UI is wrapped in a model that caches the last rendered frame and throttles re-renders to an 8ms tick, while still processing all incoming messages immediately.
  4. How immediate-mode rendering works in jjui

    main

    Instead of models returning strings, jjui uses a shared DisplayContext (from internal/ui/render) to accumulate draw operations.

    The Rendering Lifecycle:

    1. The root UI model chooses the active layout.
    2. Child models receive layout boxes.
    3. Each child draws primitives (text, lists, effects like dim/highlight) directly into the DisplayContext using ViewRect(...) methods.
    4. The root model renders the accumulated operations from the DisplayContext into an ultraviolet screen buffer, which is then converted into the final terminal string.

    Mouse Interaction: During the rendering phase, components register clickable or scrollable regions with the DisplayContext. When a mouse event occurs, the root model resolves the event against the current frame's registered interactions.

  5. Use Lua to invoke actions

    main

    Lua is integrated as a first-class way to invoke actions within the existing action/intent architecture. It is not a separate UI system.

    Capabilities available via Lua:

    • Builtin Actions: Any action generated via the //jjui:bind process is exposed under the jjui.builtin.* namespace.
    • Configured Actions: Actions configured to resolve to Lua scripts can be executed through the standard action pipeline.

    This ensures that Lua scripts follow the same resolution order as key bindings: active operation overrides $\rightarrow$ configured Lua action overrides $\rightarrow$ generated builtin action catalog.

  6. Install jjui

    main

    You can install jjui using several package managers depending on your operating system:

    Windows

    Using WinGet:

    winget install IbrahimDursun.jjui

    Using Scoop:

    scoop bucket add extras
    scoop install jjui

    macOS (Homebrew)

    brew install jjui

    Arch Linux (AUR)

    To install the pre-built binary:

    paru -S jjui-bin
    # OR
    yay -S jjui-bin

    To build from source via AUR:

    paru -S jjui
    # OR
    yay -S jjui

    Nix

    Run directly from nixpkgs:

    nix run nixpkgs#jjui

    Or use the repository flake:

    nix run github:idursun/jjui

    Go Install

    To install the latest released version:

    go install github.com/idursun/jjui/cmd/jjui@latest

    To install the latest commit from main:

    go install github.com/idursun/jjui/cmd/jjui@HEAD

    To install the latest commit from main bypassing the local cache:

    GOPROXY=direct go install github.com/idursun/jjui/cmd/jjui@HEAD
  7. Use jjui keyboard shortcuts

    main

    The following keyboard shortcuts are available within the jjui terminal interface:

    Revision Management

    • S: Squash revisions (automatically selects the following revision; use j/k to change selection).
    • n: Create a new revision.
    • s: Split a revision.
    • a: Abandon a revision.
    • A: Absorb a revision.
    • e: Edit a revision.
    • D: Edit the description of a revision.
    • u: Undo the last change.
    • U: Redo the last change.
    • v: Show evolog of a revision.
    • f: Jump to a revision using ace jump.
    • d: View the diff of a revision.
    • g: Git push/fetch.
    • l: Open the details view of the selected revision.
    • o: Switch to the Op Log view.
    • p: Open the preview window.
    • j / k: Navigate up/down (used for selection adjustment).

    Details View (after pressing l)

    • r: Restore selected files (press i in the dialog for interactive chunk restore).
    • s: Split selected files.
    • d: View diffs of highlighted items.

    Op Log View (after pressing o)

    • r: Restore the selected operation.

    Preview Window (after pressing p)

    • ctrl+n: Scroll one line down.
    • ctrl+p: Scroll one line up.
    • ctrl+d: Scroll half a page down.
    • ctrl+u: Scroll half a page up.
    • d: Show the contents of the preview in diff view.
  8. Generate the action and intent catalog

    main

    jjui uses code generation to bridge declarative action identifiers with concrete intent values and Lua surfaces.

    To define new capabilities:

    1. Annotate intent types with //jjui:bind directives in the internal/ui/intents package.
    2. Run the generator tool cmd/genactions.

    This process automatically generates:

    • The internal action-to-intent lookup in internal/ui/actions.
    • Builtin action metadata in internal/ui/actionmeta.
    • The builtin Lua action surface exposed under jjui.builtin.*.
  9. Install Lua type definitions for LuaLS

    main
    If you are using Lua for configuration and want autocomplete support in your editor (via Lua Language Server), run jjui with the --install-lua-types flag. This will write Lua type definitions to your configuration directory and may write a LuaLS configuration file if one does not already exist.
  10. Start and run the askpass Server

    main

    To begin handling password prompts, you must follow a specific lifecycle:

    1. Call StartListening() to initialize the Unix socket.
    2. Call Serve(askpass func(...)) to enter the main loop that waits for subprocess connections.

    The askpass function passed to Serve is the callback that will actually trigger your UI to prompt the user for a password. It receives the subprocess name, the prompt text, and a done channel that closes when the parent process no longer needs the password.

    Note: StartListening must be called before Serve.