# Give Any Command a Beautiful Terminal UI ![banner](https://articles.aux4.blog/data/posts/aux4/give-any-command-a-terminal-ui/banner.png) ## Introduction A command that dumps raw JSON is fine for a machine and painful for a human. This post uses `aux4/render` to turn a JSON array into a readable terminal view — a formatted table, then a list with status icons and currency badges — and finally wires it into a custom command's `render` field so it shows a polished UI in a terminal but still emits plain JSON when piped into another tool. ## Install the package ```bash aux4 aux4 pkger install aux4/render ``` ## Some data to display `aux4 render` reads a JSON array on stdin. We'll use a small task board — save it as `tasks.json`: ```json [ { "title": "Design landing page", "owner": "Sally", "status": "DONE", "priority": "high", "budget": 4200.5 }, { "title": "Ship billing API", "owner": "Marco", "status": "IN_PROGRESS", "priority": "high", "budget": 15750 }, { "title": "Write onboarding docs", "owner": "Priya", "status": "TODO", "priority": "medium", "budget": 900 }, { "title": "Investigate outage", "owner": "Chen", "status": "BLOCKED", "priority": "urgent", "budget": 0 }, { "title": "Refactor auth module", "owner": "Aisha", "status": "IN_PROGRESS", "priority": "low", "budget": 3300 } ] ``` ## Step 1: a formatted table Pass the columns you want and `render table` lays them out — aligned, with colored headers in a real terminal: ```bash cat tasks.json | aux4 render table title,owner,status,priority,budget ``` ```text title owner status priority budget Design landing page Sally DONE high 4200.5 Ship billing API Marco IN_PROGRESS high 15750 Write onboarding docs Priya TODO medium 900 Investigate outage Chen BLOCKED urgent 0 Refactor auth module Aisha IN_PROGRESS low 3300 ``` A raw `4200.5` isn't money, though. Attach a format to a column with `{format:...}`: ```bash cat tasks.json | aux4 render table 'title,owner,status,budget{format:currency,currency:USD,locale:en-US}' ``` ```text title owner status budget Design landing page Sally DONE $4,200.50 Ship billing API Marco IN_PROGRESS $15,750.00 Write onboarding docs Priya TODO $900.00 Investigate outage Chen BLOCKED $0.00 Refactor auth module Aisha IN_PROGRESS $3,300.00 ``` ## Step 2: a list with icons and badges `render list` gives a card-style view. `--primary` is the headline, `--secondary` the subtitle, `--badge` the right-aligned tag. The real power is two mini-syntaxes: - **Value maps** — `field[VALUE:label,...]` — swap a raw value for something friendlier (an emoji, a proper label). - **Value formats** — `field{format:currency,...}` — same formatting as the table. Put them together: a status icon, a human-readable status subtitle, and the budget as a currency badge: ```bash cat tasks.json | aux4 render list \ --primary title \ --icon 'status[TODO:📋,IN_PROGRESS:🔧,DONE:✅,BLOCKED:🚫]' \ --secondary 'status[TODO:To Do,IN_PROGRESS:In Progress,DONE:Done,BLOCKED:Blocked]' \ --badge 'budget{format:currency,currency:USD,locale:en-US}' ``` ```text ✅ Design landing page $4,200.50 Done 🔧 Ship billing API $15,750.00 In Progress 📋 Write onboarding docs $900.00 To Do 🚫 Investigate outage $0.00 Blocked 🔧 Refactor auth module $3,300.00 In Progress ``` The same data, now scannable. `--primary` also interpolates fields with `$` — `--primary '$owner: $title'` — and unmapped values fall through unchanged. ## Step 3: other views A single record as key/value (`--index` selects the row): ```bash cat tasks.json | aux4 render kv --index 0 ``` ```text title=Design landing page owner=Sally status=DONE priority=high budget=4200.5 ``` Or as YAML — selected fields across all records: ```bash cat tasks.json | aux4 render yaml 'title,status' ``` ```text - title: Design landing page status: DONE - title: Ship billing API status: IN_PROGRESS - title: Write onboarding docs status: TODO - title: Investigate outage status: BLOCKED - title: Refactor auth module status: IN_PROGRESS ``` `render csv` rounds out the set for spreadsheet exports. ## Step 4: wire it into a command Here's the payoff. Instead of typing that long `render list` every time, attach it to a command in an `.aux4` file. The `render` field maps output modes to render commands — `tty` runs automatically when output is a terminal. Create `.aux4`: ```json { "profiles": [ { "name": "main", "commands": [ { "name": "tasks", "execute": ["json:cat tasks.json"], "render": { "tty": "aux4 render list --primary title --icon 'status[TODO:📋,IN_PROGRESS:🔧,DONE:✅,BLOCKED:🚫]' --secondary 'status[TODO:To Do,IN_PROGRESS:In Progress,DONE:Done,BLOCKED:Blocked]' --badge 'budget{format:currency,currency:USD,locale:en-US}'", "table": "aux4 render table title,owner,status,priority,budget" }, "help": { "text": "List the project tasks." } } ] } ] } ``` The `json:` executor captures the JSON into the command's response. Now run it in a terminal and `render.tty` fires automatically: ```bash aux4 tasks ``` ```text ✅ Design landing page $4,200.50 Done 🔧 Ship billing API $15,750.00 In Progress 📋 Write onboarding docs $900.00 To Do 🚫 Investigate outage $0.00 Blocked 🔧 Refactor auth module $3,300.00 In Progress ``` But pipe it — no terminal, no rendering — and you get the raw JSON back, untouched, ready for `jq` or another command: ```bash aux4 tasks | jq '.[0]' ``` ```json { "budget": 4200.5, "owner": "Sally", "priority": "high", "status": "DONE", "title": "Design landing page" } ``` That's the whole trick: one command, a human-friendly UI for a person and clean data for a pipeline. Any other named mode is on demand — `render.table` here is `aux4 tasks --render table`: ```bash aux4 tasks --render table ``` ```text title owner status priority budget Design landing page Sally DONE high 4200.5 Ship billing API Marco IN_PROGRESS high 15750 Write onboarding docs Priya TODO medium 900 Investigate outage Chen BLOCKED urgent 0 Refactor auth module Aisha IN_PROGRESS low 3300 ``` ## Conclusion `aux4/render` turns a JSON array into a table, a key/value block, YAML, CSV, or a MUI-style list with icons and currency badges — and its `render` field lets any command detect a terminal and show a polished UI there while staying pipe-friendly everywhere else. Give your commands a face without giving up their data. ## See Also - [aux4/render](https://hub.aux4.io/r/public/packages/aux4/render)