142 lines
5.1 KiB
Markdown
142 lines
5.1 KiB
Markdown
# inline
|
||
|
||
Styled text and in-place progress in the normal terminal scrollback. No raw
|
||
mode, no alternate screen, no input handling, no cursor hiding — the shell
|
||
keeps owning the terminal. Intended for CLI tools (package managers, service
|
||
tooling, build scripts) that want color and live status without a full-screen
|
||
TUI.
|
||
|
||
Unix only (`//go:build unix`). Depends on the parent `terminal` package for
|
||
color types, capability detection, and RGB → 256 mapping.
|
||
|
||
## Model
|
||
|
||
Output is split into two zones:
|
||
|
||
installed openssl ← permanent lines (Log) — scroll normally
|
||
installed zlib
|
||
⠹ installing curl ← live block (Update) — rewritten in place
|
||
[██████░░░░░░░░] 2/5
|
||
|
||
`Log` prints permanent lines above the live block; `Update` replaces the live
|
||
block by cursor-up + clear + rewrite; `Done` erases the block and optionally
|
||
prints final lines. Interleaving is handled internally — `Log` during an
|
||
active live block erases, prints, and redraws in one flush.
|
||
|
||
## API
|
||
|
||
### Printer
|
||
|
||
| Method | Description |
|
||
|---|---|
|
||
| `New(w io.Writer) *Printer` | Creates a printer. Terminal detection via size probe; color defaults on for terminals with `NO_COLOR` unset. Safe for concurrent use. |
|
||
| `Log(format string, a ...any)` | Prints one permanent line above the live block (`Printf` semantics, newline appended). |
|
||
| `Update(lines ...string)` | Replaces the live block, rewriting in place. No-op on non-terminal output. |
|
||
| `Done(final ...string)` | Erases the live block and prints final permanent lines. Call before exit. |
|
||
| `Paint(s string, st Style) string` | Returns `s` wrapped in SGR codes for the detected color mode, or unchanged when color is off. |
|
||
| `SetColor(on bool)` | Overrides color detection (e.g. force styling into a pipe for `less -R`). Affects `Paint` only; `Update` remains terminal-gated. |
|
||
| `Size() (w, h int)` | Current terminal dimensions, 80×24 when unknown. |
|
||
|
||
### Style
|
||
|
||
Value type, zero value is unstyled, builder-composable:
|
||
|
||
| Function | Description |
|
||
|---|---|
|
||
| `Fg(c terminal.RGB) Style` | Starts a style with foreground color. |
|
||
| `(s Style) Bg(c terminal.RGB) Style` | Adds background color. |
|
||
| `(s Style) Attr(a terminal.Attr) Style` | Adds attribute bits (`AttrBold`, `AttrDim`, ...). |
|
||
|
||
```go
|
||
warn := inline.Fg(terminal.Amber).Attr(terminal.AttrBold)
|
||
p.Log("%s low disk space", p.Paint("warning:", warn))
|
||
```
|
||
|
||
True color terminals get `38;2;R;G;B`; 256-color terminals get `38;5;N` via
|
||
Redmean mapping — same degradation path as the parent package.
|
||
|
||
### Progress helpers
|
||
|
||
Pure string builders, no Printer required:
|
||
|
||
| Function | Description |
|
||
|---|---|
|
||
| `Bar(width int, pct float64, chars [3]rune) string` | Progress bar of `width` cells, `pct` clamped to [0,1], half-cell resolution via the partial rune. |
|
||
| `BarBlock` | Default character set `[3]rune{'█', '▌', '░'}`. |
|
||
| `Spinner(frame int) string` | Braille spinner frame for a monotonic counter. |
|
||
|
||
Compose with `Paint` for colored bars:
|
||
|
||
```go
|
||
line := "[" + p.Paint(inline.Bar(30, pct, inline.BarBlock), barStyle) + "]"
|
||
```
|
||
|
||
## Non-terminal output
|
||
|
||
When output is a pipe or file (CI, redirection): `Update` is a no-op, `Paint`
|
||
returns input unchanged, `Log` and `Done` print plain sequential text. A tool
|
||
using inline degrades to ordinary log output with no code changes.
|
||
|
||
## Example
|
||
|
||
Simulated package installation — spinner, overall progress bar, permanent
|
||
completion lines:
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"time"
|
||
|
||
"github.com/lixenwraith/terminal"
|
||
"github.com/lixenwraith/terminal/inline"
|
||
)
|
||
|
||
func main() {
|
||
p := inline.New(os.Stdout)
|
||
|
||
name := inline.Fg(terminal.LightSkyBlue).Attr(terminal.AttrBold)
|
||
okSt := inline.Fg(terminal.LimeGreen).Attr(terminal.AttrBold)
|
||
dim := inline.Fg(terminal.IronGray)
|
||
|
||
pkgs := []string{"openssl", "zlib", "curl", "git", "go"}
|
||
frame := 0
|
||
|
||
for i, pkg := range pkgs {
|
||
const steps = 25
|
||
for s := range steps {
|
||
pct := (float64(i) + float64(s)/steps) / float64(len(pkgs))
|
||
p.Update(
|
||
inline.Spinner(frame)+" installing "+p.Paint(pkg, name),
|
||
"["+inline.Bar(32, pct, inline.BarBlock)+"] "+
|
||
p.Paint(fmt.Sprintf("%d/%d", i+1, len(pkgs)), dim),
|
||
)
|
||
frame++
|
||
time.Sleep(40 * time.Millisecond)
|
||
}
|
||
p.Log("%s %s", p.Paint("✓", okSt), pkg)
|
||
}
|
||
|
||
p.Done(p.Paint("✓ 5 packages installed", okSt))
|
||
}
|
||
```
|
||
|
||
Run in a terminal: the two-line status block animates in place while
|
||
completion lines accumulate above it. Piped (`go run . | cat`): only the
|
||
completion lines and the final summary appear, unstyled.
|
||
|
||
## Notes
|
||
|
||
- Width is measured in runes (`unicode/utf8`); East Asian wide characters and
|
||
combining marks are not width-aware — same limitation as `tui`.
|
||
- Live block lines must occupy one visual row each: no `\n`, tabs, or control
|
||
characters. Lines are truncated to terminal width automatically; embedded
|
||
SGR from `Paint` is preserved through truncation.
|
||
- The live block is clamped to terminal height − 1 rows (newest lines kept).
|
||
- Pass external strings (package names, paths) as `Log` arguments, never as
|
||
the format string.
|
||
- Ctrl-C mid-update leaves the live block on screen but the terminal in a
|
||
normal state — no raw mode or screen buffer to restore.
|