Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ Caller-provided log excerpts are fine. Logging policy is not.
- keep focused runnable demos under `examples/`
- keep guided demos explicit about what primitive or package is being shown
- when output changes intentionally, update the relevant snapshots and GIFs in the same change
- any change that affects a focused runnable example must update that
example directory's `README.md` and the referenced GIF in `docs/vhs/`
in the same change
- any change that affects the default, non-customized examples or standard
walkthrough shown in the root `README.md` must update that README too

## Workflow

Expand Down
67 changes: 67 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ The package and module name stay `laslig`. The product branding is `Läslig`, fr

Every guided demo item now has its own runnable example under [`examples/`](./examples) and its own focused VHS capture under [`docs/vhs/`](./docs/vhs). `mage demo` now prints those focused examples one after another as one accumulating walkthrough, with the default braille spinner bridging each example transition. The hero GIF below is a direct capture of that real `mage demo` flow, while the smaller GIFs underneath stay focused one primitive at a time.

Every runnable example directory under [`examples/`](./examples) also carries
its own focused `README.md` with the matching GIF, run commands, and a real
library-usage snippet.

The hero demo is intentionally slowed down between sections for README display. Läslig itself does not add runtime delays to your commands by default.

[![Läslig full demo walkthrough](docs/vhs/demo.gif)](./examples)
Expand Down Expand Up @@ -168,6 +172,69 @@ printer.CodeBlock(laslig.CodeBlock{Title: "Example", Language: "go", Body: `fmt.
printer.LogBlock(laslig.LogBlock{Title: "stderr excerpt", Body: "INFO boot complete\nWARN retry scheduled"})
```

## Width and wrapping for framed blocks

Tables, panels, code blocks, and log blocks share a bordered width policy in styled human mode:

- `MaxWidth` — explicit cap for the rendered block (content + frame), useful for strict layouts.
- `WrapMode` — control how long values are compacted when constraints apply.
- If `MaxWidth` is omitted, styled blocks shrink toward content width and stay within the available terminal width, with an opinionated readable cap of `88` columns.

Wrap mode is intentionally small and opinionated:

- `TableWrapAuto` (default) — wrap words where possible and rebalance columns to stay within the budget.
- `TableWrapTruncate` — truncate long values with an ellipsis (`…`) to keep one logical line per cell.
- `TableWrapNever` — disable wrapping and truncate with an ellipsis when needed.

Today `TableWrapNever` and `TableWrapTruncate` render the same way. The library keeps both names because caller intent still matters: `never` means "do not wrap", while `truncate` means "compact this by truncating". The duplicate behavior is intentional for now, not an accident.

There is no separate `MinWidth` knob. The default behavior is deliberately opinionated: fit the content when possible, respect the available terminal width, and cap readable framed output before it becomes too wide.

The focused framed examples also accept `--content long`, `--max-width <n>`, and `--wrap-mode auto|truncate|never` so width behavior can be inspected directly:

```bash
go run ./examples/table --format human --style always --content long --max-width 48 --wrap-mode truncate
go run ./examples/panel --format human --style always --content long --max-width 48 --wrap-mode auto
go run ./examples/codeblock --format human --style always --content long --max-width 48 --wrap-mode never
go run ./examples/logblock --format human --style always --content long --max-width 48 --wrap-mode truncate
```

Terminal-width sweeps are useful when checking the adaptation strategy directly:

```bash
COLUMNS=96 go run ./examples/table --format human --style always --content long --wrap-mode auto
COLUMNS=72 go run ./examples/table --format human --style always --content long --wrap-mode auto
COLUMNS=56 go run ./examples/table --format human --style always --content long --wrap-mode auto
COLUMNS=48 go run ./examples/table --format human --style always --content long --wrap-mode truncate

COLUMNS=72 go run ./examples/panel --format human --style always --content long --max-width 48 --wrap-mode auto
COLUMNS=72 go run ./examples/codeblock --format human --style always --content long --max-width 48 --wrap-mode truncate
COLUMNS=72 go run ./examples/logblock --format human --style always --content long --max-width 48 --wrap-mode never
```

```go
printer.Table(laslig.Table{
Title: "Artifacts",
MaxWidth: 58,
WrapMode: laslig.TableWrapAuto,
Header: []string{"artifact_ref", "run_id", "created"},
Rows: [][]string{
{
"github.com/evanmschultz/hylla-fixture-go-2/pkg/very-long-artifact-reference/module",
"run_2026-04-01T00:00:00.123456789Z_very_long",
"2026-04-01T00:00:00Z",
},
},
})

printer.Panel(laslig.Panel{
Title: "Release note",
MaxWidth: 42,
WrapMode: laslig.TableWrapTruncate,
Body: "Artifacts with long refs and run ids are preserved while still fitting constrained terminals.",
})
```

`FormatAuto` resolves to human output on a terminal and plain text otherwise. `StyleAuto` enables ANSI styling only when the writer is attached to a TTY.

## Layout
Expand Down
Binary file modified docs/vhs/codeblock.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/vhs/codeblock.tape
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ Require go
Set Shell "zsh"
Set FontFamily "IosevkaTerm NFM"
Set FontSize 16
Set Width 1280
Set Height 920
Set Width 1480
Set Height 980
Set Padding 24
Set Theme "Catppuccin Mocha"
Set TypingSpeed 20ms
Expand Down
Binary file modified docs/vhs/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/vhs/demo.tape
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ Require go
Set Shell "zsh"
Set FontFamily "IosevkaTerm NFM"
Set FontSize 16
Set Width 1440
Set Height 1280
Set Width 1560
Set Height 1320
Set Padding 24
Set Theme "Catppuccin Mocha"
Set TypingSpeed 20ms
Expand Down
Binary file modified docs/vhs/gotestout.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/vhs/kv.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/vhs/list.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/vhs/logblock.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/vhs/logblock.tape
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ Require go
Set Shell "zsh"
Set FontFamily "IosevkaTerm NFM"
Set FontSize 16
Set Width 1280
Set Height 880
Set Width 1480
Set Height 940
Set Padding 24
Set Theme "Catppuccin Mocha"
Set TypingSpeed 20ms
Expand Down
Binary file modified docs/vhs/magecheck.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/vhs/markdown.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/vhs/notice.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/vhs/panel.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/vhs/panel.tape
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ Require go
Set Shell "zsh"
Set FontFamily "IosevkaTerm NFM"
Set FontSize 16
Set Width 1180
Set Height 660
Set Width 1400
Set Height 760
Set Padding 24
Set Theme "Catppuccin Mocha"
Set TypingSpeed 20ms
Expand Down
Binary file modified docs/vhs/paragraph.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/vhs/record.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/vhs/section.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/vhs/spinner.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/vhs/statusline.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/vhs/table.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/vhs/table.tape
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ Require go
Set Shell "zsh"
Set FontFamily "IosevkaTerm NFM"
Set FontSize 16
Set Width 1180
Set Height 760
Set Width 1400
Set Height 820
Set Padding 24
Set Theme "Catppuccin Mocha"
Set TypingSpeed 20ms
Expand Down
89 changes: 89 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Example Index

Each runnable example directory in this tree has:

- a focused `main.go`
- tests for the runnable entrypoint
- a local `README.md`
- a matching VHS GIF under `../docs/vhs/`

## Aggregate

- [`all`](./all): full guided walkthrough
GIF: [`demo.gif`](../docs/vhs/demo.gif)
Run: `go run ./examples/all --format human --style always`

## Structured Primitives

- [`section`](./section): document headings and section-owned indentation
GIF: [`section.gif`](../docs/vhs/section.gif)
Run: `go run ./examples/section --format human --style always`
- [`notice`](./notice): semantic user-facing notices
GIF: [`notice.gif`](../docs/vhs/notice.gif)
Run: `go run ./examples/notice --format human --style always`
- [`record`](./record): one object rendered as labeled facts
GIF: [`record.gif`](../docs/vhs/record.gif)
Run: `go run ./examples/record --format human --style always`
- [`kv`](./kv): compact aligned key-value output
GIF: [`kv.gif`](../docs/vhs/kv.gif)
Run: `go run ./examples/kv --format human --style always`
- [`list`](./list): grouped list items with optional badges and detail fields
GIF: [`list.gif`](../docs/vhs/list.gif)
Run: `go run ./examples/list --format human --style always`
- [`table`](./table): aligned comparison output plus width adaptation
GIF: [`table.gif`](../docs/vhs/table.gif)
Run: `go run ./examples/table --format human --style always`
- [`panel`](./panel): framed callouts and rationale blocks
GIF: [`panel.gif`](../docs/vhs/panel.gif)
Run: `go run ./examples/panel --format human --style always`
- [`paragraph`](./paragraph): long-form explanatory text
GIF: [`paragraph.gif`](../docs/vhs/paragraph.gif)
Run: `go run ./examples/paragraph --format human --style always`
- [`statusline`](./statusline): one compact semantic status row
GIF: [`statusline.gif`](../docs/vhs/statusline.gif)
Run: `go run ./examples/statusline --format human --style always`
- [`spinner`](./spinner): transient progress indicator with stable fallbacks
GIF: [`spinner.gif`](../docs/vhs/spinner.gif)
Run: `go run ./examples/spinner --format human --style always`

## Rich Text And Progress

- [`markdown`](./markdown): terminal-rendered Markdown via Glamour
GIF: [`markdown.gif`](../docs/vhs/markdown.gif)
Run: `go run ./examples/markdown --format human --style always`
- [`codeblock`](./codeblock): framed code snippets with width-aware rendering
GIF: [`codeblock.gif`](../docs/vhs/codeblock.gif)
Run: `go run ./examples/codeblock --format human --style always`
- [`logblock`](./logblock): framed caller-provided log excerpts
GIF: [`logblock.gif`](../docs/vhs/logblock.gif)
Run: `go run ./examples/logblock --format human --style always`

## Specialized Packages

- [`gotestout`](./gotestout): focused `go test -json` rendering example
GIF: [`gotestout.gif`](../docs/vhs/gotestout.gif)
Run: `go run ./examples/gotestout --format human --style always`
- [`magecheck`](./magecheck): Mage-style task-runner integration with `gotestout`
GIF: [`magecheck.gif`](../docs/vhs/magecheck.gif)
Run: `go run ./examples/magecheck --format human --style always`

## Width Inspection

These are the best commands for checking the framed primitives before
regenerating VHS assets. The things to verify are:

- default framed blocks shrink toward content width instead of forcing full width
- the rendered block still respects the current terminal width
- an explicit `MaxWidth` wins when it is set
- `never` and `truncate` currently render the same way on purpose

```bash
COLUMNS=96 go run ./examples/table --format human --style always --content long --wrap-mode auto
COLUMNS=72 go run ./examples/table --format human --style always --content long --wrap-mode auto
COLUMNS=56 go run ./examples/table --format human --style always --content long --wrap-mode auto
COLUMNS=48 go run ./examples/table --format human --style always --content long --max-width 48 --wrap-mode truncate

COLUMNS=72 go run ./examples/panel --format human --style always --content long --max-width 48 --wrap-mode auto
COLUMNS=72 go run ./examples/codeblock --format human --style always --content long --max-width 48 --wrap-mode truncate
COLUMNS=72 go run ./examples/logblock --format human --style always --content long --max-width 48 --wrap-mode never
```
50 changes: 50 additions & 0 deletions examples/all/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Läslig Demo Walkthrough

This directory is the aggregate example. It renders the same focused examples
that appear individually under `examples/`, but as one accumulating document.

![Aggregate demo walkthrough](../../docs/vhs/demo.gif)

## Run

```bash
mage demo
go run ./examples/all --format human --style always
```

`mage demo` is the paced walkthrough used for the aggregate VHS/README
presentation. `go run ./examples/all` is the direct aggregate example path
without the demo pacing layer.

## What It Shows

- the document rhythm across sections
- the default primitive ordering used in the guided walkthrough
- the same shared renderers that back the focused examples and README GIFs

## Real Library Shape

```go
printer := laslig.New(os.Stdout, laslig.Policy{
Format: laslig.FormatAuto,
Style: laslig.StyleAuto,
})

_ = printer.Section("Deploy")
_ = printer.Notice(laslig.Notice{
Level: laslig.NoticeSuccessLevel,
Title: "Checks passed",
Body: "The release walkthrough can continue.",
})
_ = printer.Table(laslig.Table{
Title: "Artifacts",
Header: []string{"name", "status"},
Rows: [][]string{
{"darwin-arm64", "ready"},
{"linux-amd64", "ready"},
},
})
```

The small wrapper in [main.go](./main.go) delegates to the shared aggregate
renderer in `internal/examples`.
35 changes: 35 additions & 0 deletions examples/codeblock/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# CodeBlock Example

This example shows framed code rendering with Glamour and width-aware wrapping
for long snippets.

![CodeBlock example](../../docs/vhs/codeblock.gif)

## Run

```bash
go run ./examples/codeblock --format human --style always
COLUMNS=72 go run ./examples/codeblock --format human --style always --content long --max-width 48 --wrap-mode truncate
COLUMNS=72 go run ./examples/codeblock --format human --style always --content long --max-width 48 --wrap-mode never
```

## Real Library Shape

```go
_ = printer.CodeBlock(laslig.CodeBlock{
Title: "Go snippet",
Language: "go",
Body: "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"hello from laslig\")\n}",
Footer: "Use CodeBlock when code should stay visibly distinct from prose.",
MaxWidth: 48,
WrapMode: laslig.TableWrapTruncate,
})
```

The code renderer now receives the frame-aware width budget before Glamour
renders, so the right border closes cleanly even on narrow terminals.

Code blocks reuse the same `TableWrapMode` enum as tables, panels, and log
blocks. `truncate` and `never` currently render the same way on purpose; both
keep one logical line per rendered segment and compact by truncating when
needed.
35 changes: 35 additions & 0 deletions examples/gotestout/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# gotestout Example

This example shows the focused `gotestout` package rendering a mixed
`go test -json` stream.

![gotestout example](../../docs/vhs/gotestout.gif)

## Run

```bash
go run ./examples/gotestout --format human --style always
go run ./examples/gotestout --format plain --style never
```

## Real Library Shape

```go
summary, err := gotestout.Render(os.Stdout, stdout, gotestout.Options{
Policy: laslig.Policy{
Format: laslig.FormatAuto,
Style: laslig.StyleAuto,
},
View: gotestout.ViewDetailed,
Activity: gotestout.ActivityOptions{
Mode: gotestout.ActivityAuto,
Text: "Streaming mixed go test -json fixture",
},
})
if err != nil {
return err
}
if summary.HasFailures() {
return errors.New("tests failed")
}
```
23 changes: 23 additions & 0 deletions examples/kv/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# KV Example

This example shows compact aligned key-value output with `KV`.

![KV example](../../docs/vhs/kv.gif)

## Run

```bash
go run ./examples/kv --format human --style always
```

## Real Library Shape

```go
_ = printer.KV(laslig.KV{
Title: "Config",
Pairs: []laslig.Field{
{Label: "format", Value: "human"},
{Label: "styled", Value: "true", Muted: true},
},
})
```
Loading