1 Commits

Author SHA1 Message Date
dependabot[bot]
c38266706d ci: bump actions/checkout from 4 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-14 09:07:31 +00:00
20 changed files with 33 additions and 984 deletions

View File

@@ -16,7 +16,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Set up JDK 21
uses: actions/setup-java@v4
@@ -42,7 +42,7 @@ jobs:
javadoc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
@@ -55,7 +55,7 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Set up JDK 21
uses: actions/setup-java@v4

View File

@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Dependency Review
uses: actions/dependency-review-action@v4

View File

@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
environment: github-pages
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Setup Pages
uses: actions/configure-pages@v4

View File

@@ -17,7 +17,7 @@ jobs:
RELEASE_VERSION: ${{ github.ref_name }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Set up JDK 21
uses: actions/setup-java@v4

43
Jenkinsfile vendored
View File

@@ -1,43 +0,0 @@
pipeline {
agent any
environment {
GITHUB_ACTOR = 'jakubbbdev'
GITHUB_TOKEN = credentials('github-credentials')
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build') {
steps {
sh 'chmod +x gradlew && ./gradlew build'
}
}
stage('Test') {
steps {
sh './gradlew test'
}
}
stage('Publish') {
steps {
sh './gradlew publish'
}
}
}
post {
success {
echo '✅ Build erfolgreich!'
}
failure {
echo '❌ Build fehlgeschlagen!'
}
}
}

View File

@@ -9,8 +9,12 @@ Java library for terminal UI: tables, rules, colors, prompts, menus, SelectList,
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/jakubbbdev/terminal-ui)
**First time here?** → [NEXT_STEPS.md](NEXT_STEPS.md) has a short checklist (Pages, topics, release).
## Install
Published to [GitHub Packages](https://github.com/jakubbbdev/terminal-ui/packages). Replace `VERSION` with a [release](https://github.com/jakubbbdev/terminal-ui/releases) tag (e.g. `1.0.0`).
**Gradle (Kotlin DSL):**
```kotlin
@@ -49,6 +53,8 @@ dependencies {
</dependency>
```
For public packages, read access usually works without credentials. If your tool asks for auth, use a [GitHub PAT](https://github.com/settings/tokens) with `read:packages` as username and the token as password.
## Requirements
- **Java 21**
@@ -92,6 +98,12 @@ String choice = Terminal.menu()
.select();
```
## Docs & links
- **In-repo docs:** [docs/](docs/) Home, Install, Components, Examples.
- **GitHub Pages:** Enable in **Settings → Pages → Source: GitHub Actions** (uses workflow `pages.yml`). Site: `https://jakubbbdev.github.io/terminal-ui/`.
- **Wiki:** **Settings → Features** → enable **Wiki**; copy content from `docs/` if you like.
## License
See [LICENSE](LICENSE).

View File

@@ -5,10 +5,6 @@ plugins {
id("maven-publish")
}
application {
mainClass.set("dev.jakub.terminal.example.Examples")
}
tasks.named<JavaExec>("run") {
jvmArgs("-Ddev.jakub.terminal.ansi=true", "-Dfile.encoding=UTF-8")
}

View File

@@ -1,71 +0,0 @@
# Components
Overview of what terminal-ui provides. All entry points are on `Terminal.*`.
## Output & text
| API | Description |
|-----|-------------|
| `Terminal.print(String)` | Styled text (chain `.color()`, `.bold()`, `.println()`) |
| `Terminal.rule()` | Horizontal rule (e.g. `====`) |
| `Terminal.keyValue()` | Key-value pairs |
| `Terminal.box()` | Box around content |
## Tables & layout
| API | Description |
|-----|-------------|
| `Terminal.table()` | Tables with header/rows |
| `Terminal.tree()` | Tree structure |
| `Terminal.columns()` | Multi-column layout |
| `Terminal.steps()` | Step indicator |
| `Terminal.breadcrumb()` | Breadcrumb trail |
| `Terminal.toc()` | Table of contents |
## Interactive
| API | Description |
|-----|-------------|
| `Terminal.prompt(String)` | Text input (`.ask()`, `.masked().ask()`, `.validate(Predicate).retryMessage(String)`) |
| `Terminal.confirm(String)` | Y/n confirmation |
| `Terminal.menu()` | Numbered menu (`.option()`, `.select()`) |
| `Terminal.selectList()` | List with arrow keys + Enter |
| `Terminal.pager()` | Paged output (Enter/arrows/q) |
| `Terminal.help()` | CLI usage block (`.option(opt, desc)`, `.title()`) |
## Live / progress
| API | Description |
|-----|-------------|
| `Terminal.progressBar()` | Progress bar (`.total()`, `.build()`) |
| `Terminal.spinner()` | Spinning indicator (`.message()`, `.start()`) |
| `Terminal.dashboard()` | Refreshing widget panel |
## Data & visuals
| API | Description |
|-----|-------------|
| `Terminal.diff()` | Diff view |
| `Terminal.log()` | Log-style output |
| `Terminal.timeline()` | Timeline |
| `Terminal.heatmap()` | Heatmap |
| `Terminal.chart()` | Charts |
## Terminal control (ANSI)
| API | Description |
|-----|-------------|
| `Terminal.clearScreen()` | Clear entire screen |
| `Terminal.cursorTo(row, col)` | Move cursor (1-based) |
## Other
| API | Description |
|-----|-------------|
| `Terminal.badge(String, Color)` | Colored badge |
| `Terminal.notify(String, Type)` | Notification (info/success/warning/error) |
| `Terminal.code(String)` | Code block with language |
| `Terminal.sysinfo()` | System info (OS, Java, etc.) |
| `Terminal.markdown(String)` | Render markdown to terminal |
Use the fluent API from `Terminal.*`. For tests or to disable ANSI, inject a custom `TerminalSupport`.

View File

@@ -1,314 +0,0 @@
# Examples
All examples use `import dev.jakub.terminal.Terminal;` and `import dev.jakub.terminal.core.Color;` where needed. Use `System.out` or omit the argument where `.print()` has a no-arg overload.
---
## Text & rules
```java
Terminal.print("Hello").color(Color.CYAN).bold().println();
Terminal.rule().character('=').print();
Terminal.rule().doubles().prefix("-- ").suffix(" --").print();
```
## Key-value & box
```java
Terminal.keyValue()
.row("Name", "terminal-ui")
.row("Version", "1.0")
.separator(" = ")
.print(System.out);
Terminal.box()
.title("Info")
.line("First line")
.lines("Second", "Third")
.print(System.out);
```
## Table
```java
Terminal.table()
.header("Col A", "Col B", "Col C")
.row("1", "2", "3")
.row("4", "5", "6")
.print(System.out);
// From a list of rows (e.g. from CSV or DB)
List<String[]> rows = List.of(
new String[]{"Alice", "10"},
new String[]{"Bob", "20"}
);
Terminal.table()
.header("Name", "Score")
.rows(rows)
.print(System.out);
// One-liner with header + data
Terminal.table()
.fromRows(new String[]{"A", "B"}, List.of(new String[]{"1", "2"}, new String[]{"3", "4"}))
.print(System.out);
```
## Tree
```java
Terminal.tree()
.node("root")
.child("src")
.child("main").end()
.child("test").end()
.end()
.child("docs")
.end()
.print(System.out);
```
## Columns
```java
Terminal.columns()
.column("Left\ncolumn\ntext")
.column("Middle")
.column("Right")
.print(System.out);
```
## Steps (wizard-style)
```java
Terminal.steps()
.step("Setup", Steps.Status.DONE)
.step("Build", Steps.Status.RUNNING)
.step("Deploy", Steps.Status.PENDING)
.print(System.out);
```
(Use `import dev.jakub.terminal.components.Steps;` for `Steps.Status`.)
## Breadcrumb
```java
Terminal.breadcrumb()
.crumb("Home")
.crumb("Projects")
.crumb("terminal-ui")
.separator(" / ")
.print(System.out);
```
## Interactive: Prompt
```java
String name = Terminal.prompt("Name: ").ask();
String secret = Terminal.prompt("Password: ").masked().ask();
// With shared scanner (e.g. in a loop):
String line = Terminal.prompt("Input: ").ask(sharedScanner);
// With validation (asks again until input matches):
String age = Terminal.prompt("Age: ").validate(s -> s.matches("\\d+")).ask();
String email = Terminal.prompt("Email: ")
.validate(s -> s.contains("@"))
.retryMessage("Invalid email, try again: ")
.ask();
```
## Interactive: Confirm
```java
boolean yes = Terminal.confirm("Continue?").defaultYes().ask();
boolean no = Terminal.confirm("Delete?").defaultNo().ask();
boolean answer = Terminal.confirm("Proceed?").ask(sharedScanner);
```
## Interactive: Menu
```java
String env = Terminal.menu()
.title("Choose environment")
.option("Development")
.option("Staging")
.option("Production")
.select();
```
## Interactive: SelectList (arrows + Enter)
```java
String choice = Terminal.selectList()
.title("Pick one")
.option("Option A")
.option("Option B")
.option("Option C")
.select();
```
## Interactive: Pager
```java
Terminal.pager()
.lines("Line 1", "Line 2", "Line 3", "Line 4", "Line 5")
.pageSize(3)
.interactive();
```
## Progress bar
```java
ProgressBar bar = Terminal.progressBar()
.total(100)
.width(40)
.style(ProgressBar.Style.BLOCK)
.build();
for (int i = 0; i <= 100; i++) {
bar.update(i);
Thread.sleep(30);
}
```
(Use `import dev.jakub.terminal.live.ProgressBar;`.)
## Spinner
```java
Spinner spin = Terminal.spinner().message("Loading...").start();
// ... do work ...
spin.stop();
```
(Use `import dev.jakub.terminal.live.Spinner;`.)
## Diff
```java
Terminal.diff()
.before("old line 1\nold line 2")
.after("new line 1\nnew line 2\nnew line 3")
.print(System.out);
```
## Log
```java
Terminal.log()
.info("Started")
.warn("Deprecation notice")
.error("Something failed")
.withTimestamp()
.print(System.out);
// Only WARN and ERROR (e.g. for production)
Terminal.log()
.info("Hidden")
.warn("Visible")
.error("Visible")
.minLevel(Log.Level.WARN)
.print(System.out);
```
(Use `import dev.jakub.terminal.components.Log;` for `Log.Level`.)
## Timeline
```java
Terminal.timeline()
.event("2024-01-01", "First release")
.event("2024-06-01", "Added SelectList")
.event("2025-01-01", "GitHub Packages")
.print(System.out);
```
## Heatmap
```java
Terminal.heatmap()
.row("Mon", 10, 20, 5, 30)
.row("Tue", 15, 25, 10, 20)
.row("Wed", 5, 15, 25, 35)
.print(System.out);
```
## Chart (sparkline / bar)
```java
Terminal.chart()
.data(1.0, 2.0, 1.5, 3.0, 2.5)
.height(5)
.print(System.out);
```
## Badge & notification
```java
Terminal.badge("OK", Color.GREEN).println();
Terminal.badge("WARN", Color.YELLOW).print(System.out);
Terminal.notify("Done!", Notification.Type.SUCCESS);
Terminal.notify("Warning", Notification.Type.WARNING);
Terminal.notify("Error", Notification.Type.ERROR);
Terminal.notify("Info", Notification.Type.INFO);
```
(Use `import dev.jakub.terminal.components.Notification;`.)
## Code block
```java
Terminal.code("java")
.line("public static void main(String[] args) {")
.line(" System.out.println(\"Hello\");")
.line("}")
.lineNumbers()
.print(System.out);
```
## Help (CLI usage)
```java
Terminal.help()
.title("Options")
.option("-v, --verbose", "Enable verbose output")
.option("-h, --help", "Show this help")
.option("--file <path>", "Input file path")
.print(System.out);
```
## Clear screen & cursor
```java
Terminal.clearScreen(); // Clear terminal (ANSI)
Terminal.cursorTo(1, 1); // Move cursor to row 1, col 1 (1-based)
```
No-op when ANSI is disabled.
## SysInfo
```java
Terminal.sysinfo().print(System.out);
```
## Markdown
```java
Terminal.markdown("# Title\n\n**Bold** and *italic*.\n\n- Item 1\n- Item 2\n\n---")
.print(System.out);
```
## Table of contents
```java
Terminal.toc()
.section("Introduction").sub("Overview").sub("Install")
.section("API").sub("Reference")
.section("Examples")
.print(System.out);
```
---
For more, see the [README](https://github.com/jakubbbdev/terminal-ui#usage-excerpt) and the source.

View File

@@ -1,25 +0,0 @@
# terminal-ui
Java library for building terminal UIs: tables, rules, colors, prompts, menus, SelectList, pager, and more. Fluent API, ANSI support, testable.
## Quick links
- **[Install](Install)** Gradle / Maven dependency
- **[Components](Components)** What's in the library
- **[Examples](Examples)** Code snippets
## Requirements
- Java 21
- Gradle 9.x or Maven (wrapper included in repo)
## Build from source
```bash
./gradlew build
./gradlew test
```
## License
MIT see [LICENSE](https://github.com/jakubbbdev/terminal-ui/blob/master/LICENSE) in the repo.

View File

@@ -1,45 +0,0 @@
# Install
Published to [GitHub Packages](https://github.com/jakubbbdev/terminal-ui/packages). Use a [release](https://github.com/jakubbbdev/terminal-ui/releases) version (e.g. `1.0.1`) for `VERSION`.
## Gradle (Kotlin DSL)
```kotlin
repositories {
mavenCentral()
maven { url = uri("https://maven.pkg.github.com/jakubbbdev/terminal-ui") }
}
dependencies {
implementation("dev.jakub.terminal:terminal-ui:VERSION")
}
```
## Gradle (Groovy)
```groovy
repositories {
mavenCentral()
maven { url "https://maven.pkg.github.com/jakubbbdev/terminal-ui" }
}
dependencies {
implementation "dev.jakub.terminal:terminal-ui:VERSION"
}
```
## Maven
```xml
<repository>
<id>github</id>
<url>https://maven.pkg.github.com/jakubbbdev/terminal-ui</url>
</repository>
<dependency>
<groupId>dev.jakub.terminal</groupId>
<artifactId>terminal-ui</artifactId>
<version>VERSION</version>
</dependency>
```
## Auth (if required)
For public packages, resolution often works without credentials. If your build asks for auth, use a [GitHub PAT](https://github.com/settings/tokens) with `read:packages`: username = your GitHub username, password = token.

View File

@@ -1,10 +0,0 @@
# terminal-ui
Java library for terminal UIs: tables, prompts, menus, and more.
- **[Home](Home)** overview
- **[Install](Install)** Gradle / Maven
- **[Components](Components)** what's in the library
- **[Examples](Examples)** runnable code snippets
[GitHub repo](https://github.com/jakubbbdev/terminal-ui) · [Releases](https://github.com/jakubbbdev/terminal-ui/releases) · [Packages](https://github.com/jakubbbdev/terminal-ui/packages)

View File

@@ -205,27 +205,6 @@ public final class Terminal {
return new Pager(DEFAULT.getSupport());
}
/** Returns a help/usage builder for CLI --help output. */
public static Help help() {
return new Help(DEFAULT.getSupport());
}
/** Clears the terminal screen (ANSI). No-op if ANSI is disabled. */
public static void clearScreen() {
if (DEFAULT.getSupport().isAnsiEnabled()) {
System.out.print(dev.jakub.terminal.core.Ansi.CLEAR_SCREEN);
System.out.flush();
}
}
/** Moves the cursor to the given 1-based row and column (ANSI). No-op if ANSI is disabled. */
public static void cursorTo(int row, int col) {
if (DEFAULT.getSupport().isAnsiEnabled()) {
System.out.print(dev.jakub.terminal.core.Ansi.cursorTo(row, col));
System.out.flush();
}
}
TerminalSupport getSupport() {
return support;
}

View File

@@ -1,87 +0,0 @@
package dev.jakub.terminal.components;
import dev.jakub.terminal.Terminal;
import dev.jakub.terminal.core.TerminalSupport;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
/**
* CLI help / usage block. Use via {@link Terminal#help()}.
* Formats options and descriptions for a typical --help output.
*/
public final class Help {
private static final int DEFAULT_OPTION_WIDTH = 24;
private final TerminalSupport support;
private final List<String> options = new ArrayList<>();
private final List<String> descriptions = new ArrayList<>();
private String title = "Usage";
private int optionWidth = DEFAULT_OPTION_WIDTH;
private PrintStream out = System.out;
public Help(TerminalSupport support) {
this.support = support;
}
/**
* Sets the title (e.g. "Usage" or "Options").
*/
public Help title(String title) {
this.title = title != null ? title : "";
return this;
}
/**
* Adds an option with description (e.g. "-v, --verbose" and "Enable verbose output").
*/
public Help option(String option, String description) {
options.add(option != null ? option : "");
descriptions.add(description != null ? description : "");
return this;
}
/**
* Sets the width of the option column (default 24).
*/
public Help optionWidth(int width) {
this.optionWidth = Math.max(8, width);
return this;
}
/**
* Sets output stream (default: stdout).
*/
public Help output(PrintStream out) {
this.out = out != null ? out : System.out;
return this;
}
/**
* Prints the help block to the given stream.
*/
public void print(PrintStream stream) {
if (stream == null) stream = System.out;
if (!title.isEmpty()) {
stream.println(title);
stream.println();
}
int ow = Math.max(optionWidth, 8);
for (int i = 0; i < options.size(); i++) {
String opt = options.get(i);
String desc = i < descriptions.size() ? descriptions.get(i) : "";
String padded = opt.length() <= ow ? opt + " ".repeat(ow - opt.length()) : opt;
stream.print(" " + padded + " ");
stream.println(desc);
}
}
/**
* Prints to stdout.
*/
public void print() {
print(out);
}
}

View File

@@ -22,7 +22,6 @@ public final class Log {
private final TerminalSupport support;
private final List<Entry> entries = new ArrayList<>();
private boolean withTimestamp;
private Level minLevel;
public Log(TerminalSupport support) {
this.support = support;
@@ -69,21 +68,11 @@ public final class Log {
}
/**
* Only print entries at this level or higher (e.g. WARN shows WARN and ERROR).
* Useful for "quiet" mode or production.
*/
public Log minLevel(Level level) {
this.minLevel = level;
return this;
}
/**
* Prints log entries to the given stream (respects minLevel if set).
* Prints all log entries to the given stream.
*/
public void print(PrintStream out) {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH:mm:ss").withZone(ZoneId.systemDefault());
for (Entry e : entries) {
if (minLevel != null && e.level.ordinal() < minLevel.ordinal()) continue;
String prefix = withTimestamp ? "[" + fmt.format(Instant.now()) + "] " : "";
String label = "[" + e.level.name() + "]";
while (label.length() < LABEL_WIDTH) label += " ";
@@ -115,14 +104,11 @@ public final class Log {
}
}
/**
* Log level for filtering. Order: DEBUG &lt; INFO &lt; WARN &lt; ERROR.
*/
public enum Level {
DEBUG(Ansi.FG_BRIGHT_BLACK),
private enum Level {
INFO(Ansi.FG_CYAN),
WARN(Ansi.FG_YELLOW),
ERROR(Ansi.FG_RED);
ERROR(Ansi.FG_RED),
DEBUG(Ansi.FG_BRIGHT_BLACK);
final String ansi;

View File

@@ -47,26 +47,6 @@ public final class Table {
return this;
}
/**
* Adds many rows from a list of arrays (e.g. from CSV or a list of records).
* Each array is one row; null cells are treated as empty string.
*/
public Table rows(Iterable<String[]> dataRows) {
if (dataRows == null) return this;
for (String[] cells : dataRows) {
if (cells != null) row(cells);
}
return this;
}
/**
* Builds a table from a header and a list of row arrays in one call.
*/
public Table fromRows(String[] headerRow, Iterable<String[]> dataRows) {
if (headerRow != null && headerRow.length > 0) header(headerRow);
return rows(dataRows);
}
/**
* Prints the table to the given stream.
*/

View File

@@ -61,15 +61,5 @@ public final class Ansi {
/** Clear line */
public static final String ERASE_LINE = "\u001B[2K";
/** Clear entire screen and move cursor to home (1,1) */
public static final String CLEAR_SCREEN = "\u001B[H\u001B[2J";
/**
* Cursor position (1-based row and column). ESC [ row ; col H
*/
public static String cursorTo(int row, int col) {
return "\u001B[" + Math.max(1, row) + ";" + Math.max(1, col) + "H";
}
private Ansi() {}
}

View File

@@ -1,244 +0,0 @@
package dev.jakub.terminal.example;
import dev.jakub.terminal.Terminal;
import dev.jakub.terminal.components.Log;
import dev.jakub.terminal.components.Notification;
import dev.jakub.terminal.components.Steps;
import dev.jakub.terminal.core.Color;
import java.util.List;
public final class Examples {
public static void main(String[] args) {
runAll();
}
public static void runAll() {
textAndRule();
keyValueAndBox();
table();
tableFromRows();
tree();
columns();
steps();
breadcrumb();
helpBlock();
log();
logWithMinLevel();
diff();
timeline();
heatmap();
chart();
badgeAndNotify();
codeBlock();
sysInfo();
markdown();
toc();
}
static void textAndRule() {
Terminal.print("=== Text & Rule ===").color(Color.CYAN).bold().println();
Terminal.rule().character('=').print();
Terminal.print("Hello").color(Color.GREEN).println();
Terminal.rule().doubles().prefix("-- ").suffix(" --").print();
System.out.println();
}
static void keyValueAndBox() {
Terminal.print("=== KeyValue & Box ===").color(Color.CYAN).bold().println();
Terminal.keyValue()
.row("Name", "terminal-ui")
.row("Version", "1.0")
.separator(" = ")
.print(System.out);
Terminal.box()
.title("Info")
.line("First line")
.lines("Second", "Third")
.print(System.out);
System.out.println();
}
static void table() {
Terminal.print("=== Table ===").color(Color.CYAN).bold().println();
Terminal.table()
.header("Col A", "Col B", "Col C")
.row("1", "2", "3")
.row("4", "5", "6")
.print(System.out);
System.out.println();
}
static void tableFromRows() {
Terminal.print("=== Table from rows ===").color(Color.CYAN).bold().println();
List<String[]> rows = List.of(
new String[]{"Alice", "10"},
new String[]{"Bob", "20"}
);
Terminal.table()
.header("Name", "Score")
.rows(rows)
.print(System.out);
System.out.println();
}
static void tree() {
Terminal.print("=== Tree ===").color(Color.CYAN).bold().println();
Terminal.tree()
.node("root")
.child("src")
.child("main").end()
.child("test").end()
.end()
.child("docs")
.end()
.print(System.out);
System.out.println();
}
static void columns() {
Terminal.print("=== Columns ===").color(Color.CYAN).bold().println();
Terminal.columns()
.column("Left\ncolumn\ntext")
.column("Middle")
.column("Right")
.print(System.out);
System.out.println();
}
static void steps() {
Terminal.print("=== Steps ===").color(Color.CYAN).bold().println();
Terminal.steps()
.step("Setup", Steps.Status.DONE)
.step("Build", Steps.Status.RUNNING)
.step("Deploy", Steps.Status.PENDING)
.print(System.out);
System.out.println();
}
static void breadcrumb() {
Terminal.print("=== Breadcrumb ===").color(Color.CYAN).bold().println();
Terminal.breadcrumb()
.crumb("Home")
.crumb("Projects")
.crumb("terminal-ui")
.separator(" / ")
.print(System.out);
System.out.println();
}
static void helpBlock() {
Terminal.print("=== Help (CLI usage) ===").color(Color.CYAN).bold().println();
Terminal.help()
.title("Options")
.option("-v, --verbose", "Enable verbose output")
.option("-h, --help", "Show this help")
.option("--file <path>", "Input file path")
.print(System.out);
System.out.println();
}
static void log() {
Terminal.print("=== Log ===").color(Color.CYAN).bold().println();
Terminal.log()
.info("Started")
.warn("Deprecation notice")
.error("Something failed")
.withTimestamp()
.print(System.out);
System.out.println();
}
static void logWithMinLevel() {
Terminal.print("=== Log (minLevel WARN) ===").color(Color.CYAN).bold().println();
Terminal.log()
.info("Hidden")
.warn("Visible")
.error("Visible")
.minLevel(Log.Level.WARN)
.print(System.out);
System.out.println();
}
static void diff() {
Terminal.print("=== Diff ===").color(Color.CYAN).bold().println();
Terminal.diff()
.before("old line 1\nold line 2")
.after("new line 1\nnew line 2\nnew line 3")
.print(System.out);
System.out.println();
}
static void timeline() {
Terminal.print("=== Timeline ===").color(Color.CYAN).bold().println();
Terminal.timeline()
.event("2025-01-01", "First release")
.event("2025-06-01", "Added SelectList")
.event("2026-01-01", "GitHub Packages")
.print(System.out);
System.out.println();
}
static void heatmap() {
Terminal.print("=== Heatmap ===").color(Color.CYAN).bold().println();
Terminal.heatmap()
.row("Mon", 10, 20, 5, 30)
.row("Tue", 15, 25, 10, 20)
.row("Wed", 5, 15, 25, 35)
.print(System.out);
System.out.println();
}
static void chart() {
Terminal.print("=== Chart ===").color(Color.CYAN).bold().println();
Terminal.chart()
.data(1.0, 2.0, 1.5, 3.0, 2.5)
.height(5)
.print(System.out);
System.out.println();
}
static void badgeAndNotify() {
Terminal.print("=== Badge & Notify ===").color(Color.CYAN).bold().println();
Terminal.badge("OK", Color.GREEN).println();
Terminal.badge("WARN", Color.YELLOW).print(System.out);
Terminal.notify("Done!", Notification.Type.SUCCESS);
Terminal.notify("Warning", Notification.Type.WARNING);
System.out.println();
}
static void codeBlock() {
Terminal.print("=== Code block ===").color(Color.CYAN).bold().println();
Terminal.code("java")
.line("public static void main(String[] args) {")
.line(" System.out.println(\"Hello\");")
.line("}")
.lineNumbers()
.print(System.out);
System.out.println();
}
static void sysInfo() {
Terminal.print("=== SysInfo ===").color(Color.CYAN).bold().println();
Terminal.sysinfo().print(System.out);
System.out.println();
}
static void markdown() {
Terminal.print("=== Markdown ===").color(Color.CYAN).bold().println();
Terminal.markdown("# Title\n\n**Bold** and *italic*.\n\n- Item 1\n- Item 2\n\n---")
.print(System.out);
System.out.println();
}
static void toc() {
Terminal.print("=== Table of contents ===").color(Color.CYAN).bold().println();
Terminal.toc()
.section("Introduction").sub("Overview").sub("Install")
.section("API").sub("Reference")
.section("Examples")
.print(System.out);
}
}

View File

@@ -6,7 +6,6 @@ import dev.jakub.terminal.core.TerminalSupport;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Scanner;
import java.util.function.Predicate;
/**
* Input prompt for reading user input. Use via {@link Terminal#prompt(String)}.
@@ -19,8 +18,6 @@ public final class Prompt {
private boolean masked;
private PrintStream out = System.out;
private InputStream in = System.in;
private Predicate<String> validator;
private String retryMessage = "Invalid, try again: ";
public Prompt(String message, TerminalSupport support) {
this.message = message != null ? message : "";
@@ -51,62 +48,33 @@ public final class Prompt {
return this;
}
/**
* Validates input: asks again until the predicate returns true. Null = no validation.
*/
public Prompt validate(Predicate<String> validator) {
this.validator = validator;
return this;
}
/**
* Message shown when validation fails (default: "Invalid, try again: ").
*/
public Prompt retryMessage(String retryMessage) {
this.retryMessage = retryMessage != null ? retryMessage : "Invalid, try again: ";
return this;
}
/**
* Prompts and returns the entered string. Blocks until a line is read.
* If {@link #validate(Predicate)} is set, repeats until the predicate accepts the input.
*/
public String ask() {
out.print(message);
out.flush();
if (masked) {
return readMasked();
}
try (Scanner scan = new Scanner(in)) {
return askLoop(scan);
return scan.hasNextLine() ? scan.nextLine() : "";
}
}
/**
* Prompts and returns the next line from the given scanner. Use this when
* sharing one scanner (e.g. in an interactive app) so input blocks correctly.
* If {@link #validate(Predicate)} is set, repeats until the predicate accepts the input.
*/
public String ask(Scanner sharedScanner) {
if (sharedScanner == null) return ask();
if (masked && System.console() != null) {
out.print(message);
out.flush();
if (masked && System.console() != null) {
char[] chars = System.console().readPassword();
return chars != null ? new String(chars) : "";
}
return askLoop(sharedScanner);
}
private String askLoop(Scanner scan) {
while (true) {
out.print(message);
out.flush();
if (!scan.hasNextLine()) return "";
String line = scan.nextLine();
if (line == null) line = "";
if (validator == null || validator.test(line)) return line;
out.print(retryMessage);
out.flush();
}
return sharedScanner.hasNextLine() ? sharedScanner.nextLine() : "";
}
private String readMasked() {

View File

@@ -7,7 +7,6 @@ import dev.jakub.terminal.interactive.Prompt;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Scanner;
import java.util.function.Predicate;
/**
* Fluent builder for {@link Prompt}. Use via {@link Terminal#prompt(String)}.
@@ -20,8 +19,6 @@ public final class PromptBuilder {
private boolean masked;
private PrintStream output = System.out;
private InputStream input = System.in;
private Predicate<String> validator;
private String retryMessage = "Invalid, try again: ";
public PromptBuilder(String message, TerminalSupport support) {
this.message = message != null ? message : "";
@@ -52,27 +49,14 @@ public final class PromptBuilder {
return this;
}
/**
* Validates input: prompts again until the predicate returns true. Null = no validation.
*/
public PromptBuilder validate(Predicate<String> validator) {
this.validator = validator;
return this;
}
/**
* Message shown when validation fails (default: "Invalid, try again: ").
*/
public PromptBuilder retryMessage(String retryMessage) {
this.retryMessage = retryMessage != null ? retryMessage : "Invalid, try again: ";
return this;
}
/**
* Prompts and returns the entered string.
*/
public String ask() {
Prompt p = buildPrompt();
Prompt p = new Prompt(message, support);
if (masked) p.masked();
p.output(output);
p.input(input);
return p.ask();
}
@@ -82,17 +66,10 @@ public final class PromptBuilder {
*/
public String ask(Scanner sharedScanner) {
if (sharedScanner == null) return ask();
Prompt p = buildPrompt();
return p.ask(sharedScanner);
}
private Prompt buildPrompt() {
Prompt p = new Prompt(message, support);
if (masked) p.masked();
p.output(output);
p.input(input);
if (validator != null) p.validate(validator);
p.retryMessage(retryMessage);
return p;
return p.ask(sharedScanner);
}
}