Skip to content
Merged
34 changes: 33 additions & 1 deletion docs/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -580,14 +580,19 @@ Human output is designed for readable terminal display:
- `TableColumn::no_truncate` opts a column out of shrinking entirely (still
bounded by a large pathological-value safety cap) — use it for values that
are useless when cut short, such as URLs.
- `TableColumn::field` supports a dotted path (`"parameters.items"`) to reach a value nested under intermediate objects — useful when a response wraps a list in a pagination/summary envelope. A literal field name containing a `.` is not addressable this way (the `.` is always read as a path separator), matching the same convention `crate::output::fields` already uses for `--fields` projection.
- `TableColumn::nested(columns)` opts a column into rendering its value as an indented child table (when the value is a list of objects) or an indented child property bag (when it's a single object), instead of the raw-JSON fallback every other column gets. It's a strict opt-in: a column with no `.nested(...)` renders exactly as before even if its runtime value happens to be list/object shaped. Nesting only applies inside an object's property bag — a row cell inside an array-of-objects table always renders as a single flat value, since a table row is one monospace line and can't itself contain a rendered sub-block. A nested child's own columns may set `.nested(...)` again for a grandchild table or property bag; the width budget and hide-before-truncate behavior below apply to every nesting level, narrowed by two spaces of indent per level.
- When the terminal is too narrow for every column, hiding a column is
preferred over truncating a cell: the lowest-priority (trailing) columns —
see "Column order is priority" below — are hidden one at a time until the
survivors fit in full, or only one column remains. A footer names whatever
got hidden and suggests `--fields`/`--json`. A similar footer appears if a
cell still had to be shortened (only possible once hiding can't help
further — e.g. a single remaining column whose value alone exceeds the
display width).
display width). When the narrowing happened inside a `TableColumn::nested`
column's own child table or property bag, the footer suggests only
`--json` — `--fields` selects among top-level declared columns and can
drop a nested column entirely, but can't narrow what's shown inside one.

Views can be assigned to commands. There are two ways to do it.

Expand Down Expand Up @@ -623,6 +628,33 @@ let shared = HumanViewDef::new(
let spec = CommandSpec::new("get", "Get a project").with_view_id("projects-table");
```

A column can nest a child table under an object field. Given a response shaped like `{ "name": "getPets", "parameters": { "items": [...], "total": 2 } }`:

```rust
use cli_engine::{CommandSpec, TableColumn};

let spec = CommandSpec::new("get", "Get an operation").with_view(vec![
TableColumn::new("name", "Name"),
TableColumn::new("parameters.items", "Parameters").nested(vec![
TableColumn::new("name", "Name"),
TableColumn::new("in", "In"),
]),
]);
```

which renders as:

```
Name: getPets
Parameters:
NAME IN
----- -----
limit query
id path

(2 rows)
```

### Column order is priority

Column order is a priority order, most important first — put the column a reader most needs (usually an id or name) first. This drives two things: display order, and which columns survive when the terminal is too narrow to show all of them (lowest-priority, trailing columns are hidden first).
Expand Down
Loading