Skip to content

elowbe/JinConsole

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

50 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

JinConsole

JinConsole is a Java 17 Swing-based console UI toolkit for building keyboard-driven desktop apps with a retro text-console feel. It provides a JinCanvas app lifecycle, a character-cell drawing API, focusable widgets, simple dialogs, file drop handling, and optional pixel rendering for richer components such as images.

SCR-20260528-sjtg SCR-20260528-sihk

Features

  • Character-cell rendering with JinGraphics
  • Swing window management with fullscreen and multi-monitor shortcuts
  • Focusable widgets with keyboard navigation
  • Built-in UI widgets for buttons, inputs, tables, trees, directories, images, graphs, and diff review
  • Color tags in text strings, such as <#green>Hello
  • Optional AI-assisted text completion through OllamaAPI
  • Maven project setup with Java 17

Requirements

  • Java 17 or newer
  • Maven 3.8 or newer

Installation

Clone the repository and compile the project:

git clone <repository-url>
cd JinConsole
mvn compile

The project uses Maven and keeps source files under src/.

Running The Example App

Run the included starter app:

mvn exec:java -Dexec.mainClass=lib.console.main.MyJinConsoleApp

You can also run the calculator or login examples:

mvn exec:java -Dexec.mainClass=lib.console.main.Calculator
mvn exec:java -Dexec.mainClass=lib.console.main.MyLoginScreen

Getting Started

Create a class that extends JinCanvas, implement the lifecycle methods, then start it with JinConsole.start(...).

package lib.console.main;

import java.awt.Graphics2D;
import java.awt.event.KeyEvent;

import lib.console.widgets.ButtonWidget;

public class MyApp extends JinCanvas {
    private ButtonWidget quitButton;

    public static void main(String[] args) {
        JinConsole.start(new MyApp(), "My App", 40, 30, 1280, 720);
    }

    @Override
    public void init() {
        quitButton = new ButtonWidget("Quit", 2, 2, 1);
        quitButton.setCallback(() -> System.exit(0));
        quitButton.takeFocus();
        addWidget(quitButton);
    }

    @Override
    public void tick(float delta) {
        // Update app state here.
    }

    @Override
    public void draw(JinGraphics t2d) {
        t2d.drawString("<#green>Hello JinConsole", 2, 1);
    }

    @Override
    public void render(Graphics2D g2d) {
        // Optional pixel-level drawing after the console is rendered.
    }

    @Override
    public void destroy() {
        // Cleanup here.
    }

    @Override
    public void scroll(int amount) {
    }

    @Override
    public void keyDown(KeyEvent e) {
    }

    @Override
    public void keyUp(KeyEvent e) {
    }
}

App Lifecycle

JinCanvas gives each app a small set of lifecycle hooks:

  • init() creates widgets and sets initial focus.
  • tick(float delta) updates app state once per frame.
  • draw(JinGraphics t2d) draws console-cell content over widgets.
  • render(Graphics2D g2d) draws pixel graphics after the console is rendered.
  • destroy() cleans up before the app exits.
  • scroll(int amount) handles scroll-wheel events.
  • keyDown(KeyEvent e) and keyUp(KeyEvent e) handle app-level keyboard input.
  • onFilesDropped(List<File> files) can be overridden to handle drag-and-drop files.

Useful Controls

  • Tab: move focus to the next linked widget.
  • Shift + Tab: move focus to the previous linked widget.
  • Enter or Space: activate focused buttons, options, checkboxes, and table rows.
  • F11: toggle fullscreen.
  • F12: move the window to the next display.

Link focusable widgets with setNext(...) and setPrevious(...):

usernameInput.setNext(passwordInput);
passwordInput.setNext(loginButton);
usernameInput.takeFocus();

Drawing With JinGraphics

JinGraphics draws in console cells. The top-left cell is (0, 0).

t2d.drawString("Plain text", 2, 2);
t2d.drawString("<#green>Green text", 2, 3);
t2d.drawBox(1, 1, 20, 5);
t2d.drawLine('-', 1, 8, 20, 8);
t2d.drawChar('*', 10, 10);

Use ColorExtractor.cleanString(...) when measuring strings that contain color tags:

String text = "<#green>Hello JinConsole";
String clean = ColorExtractor.cleanString(text);
t2d.drawString(text, JinConsole.getColumns() / 2 - clean.length() / 2, 5);

Dialog Helpers

JinConsole includes simple blocking dialog helpers:

String name = JinConsole.prompt("Name:");
boolean confirmed = JinConsole.confirmDialog("Continue?");
int choice = JinConsole.optionsDialog("Pick one", "New", "Open", "Exit");
JinConsole.messageBox("Saved!");

Widget Guide

All widgets live in lib.console.widgets. Add widgets with addWidget(widget), and call takeFocus() on the widget that should receive keyboard input first.

ButtonWidget

A focusable button that runs a callback when the user presses Enter or Space.

ButtonWidget button = new ButtonWidget("Save", 2, 2, 1);
button.setCallback(() -> JinConsole.messageBox("Saved"));
button.takeFocus();
addWidget(button);

Constructor:

new ButtonWidget(String title, int x, int y, int padding)

InputWidget

A single-line or multi-line text input with cursor movement, selection, clipboard operations, undo/redo, optional line numbers, optional command history, and optional AI assistance.

InputWidget input = new InputWidget("Command:", 2, 5, 40);
input.commandHistoryEnabled = true;
input.onNewLine = () -> {
    String command = input.getValue();
    input.addCommandToHistory(command);
    input.setValue("");
};
input.takeFocus();
addWidget(input);

Multi-line input:

InputWidget editor = new InputWidget("Notes", 2, 8, 60, 10);
editor.legalCharacters += "\n\t";
editor.displayLineNumbers = true;
addWidget(editor);

Constructors:

new InputWidget(String title, int x, int y, int width)
new InputWidget(String title, int x, int y, int width, int height)

Useful fields and methods:

  • value, getValue(), setValue(String value)
  • obscure for password-style input
  • legalCharacters to control accepted characters
  • displayLineNumbers
  • commandHistoryEnabled and addCommandToHistory(...)
  • canAssist for AI-assisted completion
  • onNewLine callback

MultilineInput

A simple multi-line text editor widget backed by multiple InputWidget rows.

MultilineInput multiline = new MultilineInput(2, 2, 60, 8);
multiline.value = "Line one\nLine two";
multiline.updateInputs();
multiline.takeFocus();
addWidget(multiline);

Constructor:

new MultilineInput(int x, int y, int width, int height)

TextWidget

Displays static or dynamically updated text.

TextWidget status = new TextWidget(2, 2, 40, 3);
status.text = "Ready";
addWidget(status);

Constructor:

new TextWidget(int column, int row, int width, int height)

OptionsWidget

A keyboard-selectable list of options. Use Up and Down to move, then Enter or Space to select.

OptionsWidget options = new OptionsWidget(2, 4, "New File", "Open", "Exit");
options.setCallback((index, option) -> {
    JinConsole.messageBox("Selected: " + option);
});
options.takeFocus();
addWidget(options);

Scrollable panel mode:

options.title = "Actions";
options.panelWidth = 30;
options.panelHeight = 8;
options.onCancel = () -> options.blur();

Constructor:

new OptionsWidget(int x, int y, String... options)

CheckboxWidget

A toggleable checkbox with an optional callback.

CheckboxWidget checkbox = new CheckboxWidget(2, 2, "Enable logs");
checkbox.setOnToggle(() -> {
    System.out.println("Checked: " + checkbox.isChecked());
});
checkbox.takeFocus();
addWidget(checkbox);

Constructor:

new CheckboxWidget(int x, int y, String label)

TableWidget

Displays rows of data with selectable rows. Use Up and Down to move. Override onEnter() for row actions.

TableWidget table = new TableWidget("Users", 2, 2, "Name", "Role") {
    @Override
    public void onEnter() {
        Object[] row = data.get(getSelected());
        JinConsole.messageBox("Selected " + row[0]);
    }
};

table.data.add(new Object[] {"Ada", "Admin"});
table.data.add(new Object[] {"Grace", "Developer"});
table.maxRows = 6;
table.takeFocus();
addWidget(table);

Constructor:

new TableWidget(String title, int x, int y, String... headers)

Useful fields and methods:

  • data
  • setData(ArrayList<Object[]> data)
  • maxRows
  • alignment with TableWidget.LEFT, CENTER, or RIGHT
  • getSelected() and setSelected(int selected)

EditableTableWidget

An editable table with typed columns. Press Enter on a row to edit the first editable column, then use Enter to advance or commit and Escape to cancel.

EditableTableWidget table = new EditableTableWidget("Inventory", 2, 2, "Item", "Count", "Price");
table.data.add(new Object[] {"Keyboard", 3, 49.99});
table.data.add(new Object[] {"Mouse", 8, 19.99});
table.setColumnTypes(String.class, Integer.class, Double.class);
table.setColumnEditable(1, true);
table.setColumnEditable(2, true);
table.setColumnUnit(2, "USD");
table.takeFocus();
addWidget(table);

Constructor:

new EditableTableWidget(String title, int x, int y, String... headers)

DirectoryWidget

A file browser built on TableWidget. Directories are shown first, ... moves to the parent directory, and handleFile(...) can be overridden for file actions.

DirectoryWidget files = new DirectoryWidget("Files", new File("."), 2, 2, 40) {
    @Override
    public void handleFile(File file) {
        JinConsole.messageBox("Opened: " + file.getName());
    }
};

files.setFilter(file -> !file.isHidden());
files.takeFocus();
addWidget(files);

Constructor:

new DirectoryWidget(String title, File dir, int x, int y, int width)

TreeViewWidget

Displays a collapsible JSON-backed tree. Use Up and Down to move, Right to expand, Left to collapse, and Enter to toggle.

JSONObject treeData = new JSONObject()
    .put("name", "Project")
    .put("expanded", true)
    .put("children", new JSONArray()
        .put(new JSONObject().put("name", "src").put("expanded", true))
        .put(new JSONObject().put("name", "README.md")));

TreeViewWidget tree = new TreeViewWidget(2, 2, 40, 10);
tree.loadFromJSON(treeData);
tree.takeFocus();
addWidget(tree);

Constructor:

new TreeViewWidget(int x, int y, int width, int height)

LineGraphWidget

Draws a simple line graph from ArrayList<Point.Double> data.

ArrayList<Point.Double> points = new ArrayList<>();
points.add(new Point.Double(0, 1));
points.add(new Point.Double(1, 4));
points.add(new Point.Double(2, 2));

LineGraphWidget graph = new LineGraphWidget(2, 2, 30, 10);
graph.setData(points);
addWidget(graph);

Constructor:

new LineGraphWidget(int x, int y, int width, int height)

ImageWidget

Displays a BufferedImage or Base64-encoded image with optional aspect-ratio preservation. It draws a console border and renders the image at pixel level.

BufferedImage image = ImageIO.read(new File("image.png"));

ImageWidget imageWidget = new ImageWidget(2, 2);
imageWidget.setImage(image);
imageWidget.imgWidth = 320;
imageWidget.imgHeight = 180;
imageWidget.keepAspectRatio = true;
imageWidget.setCallback(() -> JinConsole.messageBox("Image selected"));
addWidget(imageWidget);

Constructor:

new ImageWidget(int x, int y)

TimeWidget

Displays the current date and time once per second.

TimeWidget clock = new TimeWidget(2, 2);
addWidget(clock);

Constructor:

new TimeWidget(int x, int y)

MonthsTableWidget

A ready-made TableWidget example containing month names and day counts.

MonthsTableWidget months = new MonthsTableWidget();
months.takeFocus();
addWidget(months);

Constructor:

new MonthsTableWidget()

GitDiffReviewWidget

A full-screen diff review panel with accept, reject, accept-all, and cancel actions.

GitDiffReviewWidget review = new GitDiffReviewWidget(0, 0, JinConsole.getColumns(), JinConsole.getRows());
review.setChange(0, 1, "src/App.java", diffText);
review.listener = new GitDiffReviewWidget.Listener() {
    @Override
    public void onAccept() {
        review.destroy();
    }

    @Override
    public void onReject() {
        review.destroy();
    }

    @Override
    public void onAcceptAll() {
        review.destroy();
    }

    @Override
    public void onCancel() {
        review.destroy();
    }
};
review.takeFocus();
addWidget(review);

Constructor:

new GitDiffReviewWidget(int column, int row, int width, int height)

Controls:

  • Y: accept
  • N: reject
  • A: accept all
  • Escape: cancel
  • Up, Down, Page Up, Page Down: scroll

Focus, Visibility, And Pages

Every Widget inherits focus and visibility helpers:

widget.takeFocus();
widget.blur();
widget.setVisible(false);
widget.destroy();

Widgets also inherit JinCanvas page support. A widget is visible when its page matches the parent canvas page.

settingsButton.setPage("SETTINGS");
setPage("SETTINGS");

Project Structure

src/lib/console/main      Core window, canvas, rendering, and example apps
src/lib/console/widgets   Built-in widgets
src/lib/console/util      Utility classes for colors, files, syntax, HTTP, and AI APIs
pom.xml                   Maven build configuration

Dependencies

JinConsole currently uses:

  • org.seleniumhq.selenium:selenium-java
  • org.json:json

See pom.xml for exact versions.

Notes

  • The default console size is controlled by the JinConsole.start(...) arguments.
  • Use JinConsole.getColumns() and JinConsole.getRows() to position widgets relative to the current console dimensions.
  • Settings controls font, cell size, spacing, and background.
  • OllamaAPI defaults to a local Ollama server at http://localhost:11434 and also contains provider support for OpenAI, Claude, and LM Studio.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages