Term.jl

repository·master·Indexed 19 days ago

https://github.com/fedeclaudi/term.jl

A Julia library for creating rich, styled, and structured terminal user interfaces. It provides tools for layout management, Markdown parsing, color manipulation, and visual inspection of Julia objects. Key features include stylized error and stacktrace handling, a custom global logger (TermLogger), progress bars with the @track macro, and introspection tools like inspect, typestree, and expressiontree for visualizing Julia types and expressions.

Tokens
22.6K
Snippets
109
Records
116
Agent score
67%

What's inside Term.jl

  1. Overview of Term.jl capabilities

    master

    Term.jl is a Julia library designed for producing styled, beautiful terminal output. It provides two primary ways to create visual terminal content:

    1. Markup Syntax: A simple markup system used to add style information directly to standard Julia strings.
    2. Renderable Objects: Structured components like Panel and TextBox. These objects can be styled, contain styled text, and can be nested or stacked to create complex visual layouts in the terminal.
  2. Use predefined widgets in Term.jl

    master

    Widgets are the fundamental building blocks of Apps in Term.jl. They represent single pieces of content with specific functions (e.g., displaying text, acting as a button). Most predefined widgets are available via the Term.LiveWidgets module. You can preview a widget's appearance by piping it to the frame function.

    using Term.LiveWidgets
    TextWidget("Hello world!") |> frame
  3. Create complex layouts with Compositor

    master

    The Compositor allows you to define complex terminal layouts using Julia Expression syntax. Instead of manually nesting vstack, hstack, or grid with many placeholders, you define a layout template where placeholders are represented by single-character names followed by their dimensions (e.g., A(height, width)).

    Key features:

    • Placeholder Syntax: Use single characters (like A, B, C) to represent slots in the layout. Dimensions are passed as (height, width).
    • Stacking Operators: Use standard stacking operators like * (horizontal/side-by-side) and / (vertical/stacked) within the expression.
    • Fractional Sizing: To make elements responsive to terminal size, use Float64 values for dimensions. A value of 0.75 will occupy 75% of the available space.
    • Interpolation: You can break down complex layouts by using Julia's interpolation ($) to compose smaller layout expressions into a larger one.
    using Term.Compositors
    
    # Simple layout with placeholders
    layout = :(A(5, 45) * B(5, 45))
    Compositor(layout)
    
    # Responsive layout using fractional widths
    layout_responsive = :(A(20, $(0.75)) * B(20, $(0.25)))
    Compositor(layout_responsive)
    
    # Complex layout using interpolation
    first_column = :(vstack(A(5, 40), B(5, 40)))
    second_column = :(vstack(F(5, 40), G(5, 40))
    layout = :( ($first_column * E(20, 5) * $second_column) / O(5, 85) )
    Compositor(layout)
  4. How stacking affects Renderable Measure

    master

    Every Renderable has a measure property that stores its terminal width and height. When you stack renderables, the resulting Measure is calculated as follows:

    • Horizontal stacking (*): The width is the sum of the widths of the two renderables, and the height is the height of the tallest renderable.
    • Vertical stacking (/): The width is the width of the widest renderable, and the height is the sum of the heights of the two renderables.
    import Term: Panel
    p1 = Panel(height=5, width=5)
    p2 = Panel(height=5, width=8)
    
    h = p1 * p2
    println("Horizontal width: ", h.measure.width) # Sum of widths
    println("Horizontal height: ", h.measure.height) # Max height
    
    v = p1 / p2
    println("Vertical width: ", v.measure.width) # Max width
    println("Vertical height: ", v.measure.height) # Sum of heights
  5. Implement keyboard input for widgets

    master

    To allow a widget to capture user input, you must define a controls attribute. This attribute is a dictionary that maps specific keys to handler functions. When a key is pressed while the widget is active, the corresponding function is executed.

    Key Types

    The dictionary keys can be one of two types:

    1. Char: A single character (e.g., 'q', 'h', or ']').
    2. KeyInput: Special keys such as ArrowLeft(), HomeKey(), EndKey(), Esc(), or SpaceBar().

    Function Signature

    Each handler function must follow the signature fn(w, k), where:

    • w: The widget instance the function is assigned to.
    • k: The key that was pressed.

    Because a single function might handle multiple different keys (e.g., both ArrowRight() and ']' triggering a page down), the function signature should use a Union type to accommodate the possible key inputs.

    # Example: Defining a handler that accepts multiple key types
    next_page(p::Pager, ::Union{PageDownKey, ArrowRight, Char})
    
    # Example: Mapping keys to handlers in the controls dictionary
    pager_controls = Dict(
        ArrowRight() => next_page,
        ']' => next_page,
        ArrowLeft() => prev_page,
        '[' => prev_page,
        ArrowDown() => next_line,
        '.' => next_line,
        ArrowUp() => prev_line,
        ',' => prev_line,
        HomeKey() => home,
        EndKey() => toend,
        Esc() => quit,
        'q' => quit,
    )
  6. Simulate terminal dimensions with the Console object

    master

    The Console object allows you to simulate a terminal with a specific width (number of columns), regardless of the user's actual terminal size. This is useful for ensuring consistent output formatting across different environments.

    To use a simulated console, you must create a Console instance with the desired width and then use enable to activate it. Once activated, Term functions like tprintln and Renderable objects (like Panel) will be reshaped to fit the simulated width. Use disable to return to the actual terminal dimensions.

    using Term: tprintln
    using Term.Consoles: Console, enable, disable
    
    # Create a console 40 columns wide
    myc = Console(40)
    
    # Activate the simulated console
    myc |> enable
    
    # Output will now be reshaped to 40 columns
    tprintln("This is a very long text"^10)
    
    # De-activate to return to real terminal size
    myc |> disable
  7. Understand Renderables in Term.jl

    master

    In Term.jl, complex terminal outputs are constructed using Renderables, which are subtypes of AbstractRenderable. Unlike simple styled strings, renderables allow you to create structured UI elements like panels, boxes, and trees.

    Every renderable is composed of two fundamental components:

    1. Segment: Represents a single line of text. A renderable is essentially a collection of segments that are printed sequentially, one per line, to create a single visual object.
    2. Measure: Defines the dimensions (width and height) of the renderable as it will appear in the terminal. The Measure is used by the layout engine to position and combine multiple renderables.

    Common renderable types include:

    • Panel: Creates a bordered container around content.
    • RenderableText: Handles text rendering within the console.
    • TextBox: A hybrid between text and structured containers.
    • Tree: For hierarchical data visualization.
    import Term: Panel
    
    # Example of a Panel with automatic fitting
    print(Panel("this is {red}RED{/red}"; fit=true))
  8. Create nested layouts using Panel nesting

    master

    The simplest way to create complex layouts in Term.jl is to nest one element inside another. You can pass a Panel as an argument to another Panel to create hierarchical structures. This allows you to combine different styles, boxes, and titles at different levels of the layout.

    import Term: Panel
    
    Panel(
        Panel(
            Panel(
                "Inner content",
                height=3,
                width=28,
                style="green"
            ),
            style="red", box=:HEAVY, title="ST"
        ),
        width=44, style="blue", box=:DOUBLE, title="NE"
    )
  9. What are Live widgets in Term.jl

    master
    Live widgets are a feature in Term.jl designed to make terminal applications more dynamic and interactive. Unlike static terminal graphics, live widgets can update over time and react to user input, allowing for the creation of more engaging terminal user interfaces (TUIs).
  10. Define layouts using Expressions

    master

    Layouts are defined using Julia Expr objects (syntax like :(...)). You specify the size and position of widgets using the pattern name(height, width).

    • height: Must be an Int representing the number of lines.
    • width: Can be an Int (number of columns) or a Float64 between 0 and 1 (representing a fraction of the available space).
    • * operator: Places elements "to the side of" each other.
    • / operator: Places elements "above" each other.

    Using Float64 for width enables responsive layouts that resize when the terminal size changes. To allow the app to expand to fill the entire terminal when enlarged, use the expand keyword argument in the App constructor.

    To visualize a layout without actual widgets, you can create an empty app with placeholders: App(layout) |> frame.

    # Example: a is 10 lines high and half width, b is same, c is 10 lines high and full width, with c below a and b
    layout = :( (a(10, .5) * b(10, .5)) / c(10, 1) )
    
    # Example: single element taking 10 lines and 50% width
    :r(10, 0.5)
  11. Use quotes and admonitions in Markdown

    master

    Term.jl supports Markdown quotes and admonitions to highlight specific information or call attention to warnings and tips.

    Quotes: Use > to create a blockquote.

    Admonitions: Use the !!! syntax followed by a type to create callouts. Supported types include:

    • !!! note
    • !!! warning
    • !!! danger
    • !!! tip "Title" (titles can be provided in quotes)
    tprint(md"""
    > This is a quote.
    
    !!! warning
        This is a warning admonition.
    
    !!! tip "Wow!"
        This is a tip with a custom title.
    """)