Skip to content

Commit a2d48f6

Browse files
Fixed column ordering in Owl tables
1 parent 51bb462 commit a2d48f6

File tree

10 files changed

+363
-5
lines changed

10 files changed

+363
-5
lines changed
File renamed without changes.
File renamed without changes.
File renamed without changes.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
---
22
verblock: "22 Sep 2025:v0.1: Matthew Sinclair - Initial version"
33
intent_version: 2.2.0
4-
status: WIP
4+
status: Completed
55
created: 20250922
6-
completed:
6+
completed: 20251006
77
---
88
# ST0008: Orthogonalised formatting and outputting
99

File renamed without changes.

intent/st/steel_threads.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,6 @@ This document provides an overview of all steel threads in the project. It helps
3939
4. Use this document to quickly locate specific steel thread documents
4040

4141
The detailed information for each steel thread is contained in its individual document (e.g., ST0001.md).
42-
| ST0008 | Orthogonalised formatting and outputting | WIP | 2025-09-22 | |
42+
| ST0008 | Orthogonalised formatting and outputting | Completed | | 2025-10-06 |
4343
| ST0009 | 8 | Not Started | 2025-09-22 | |
4444
| ST0003 | REPL Output Callback System | Completed | | 2025-09-22 |

intent/usr/deployment_guide.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,56 @@ arca_cli --help
9191

9292
Configure Arca.Cli behavior using these environment variables:
9393

94+
#### Configuration Variables
95+
9496
| Variable | Purpose | Default |
9597
|------------------------------|-----------------------------------|-----------------------------------|
9698
| APP_NAME_CONFIG_PATH | Configuration directory path | .app_name/ (e.g., .arca_cli/) |
9799
| APP_NAME_CONFIG_FILE | Configuration filename | config.json |
98100

99101
Note: The actual environment variable names are derived from the application name (in UPPERCASE). For example, for the `arca_cli` application, the environment variables would be `ARCA_CLI_CONFIG_PATH` and `ARCA_CLI_CONFIG_FILE`.
100102

103+
#### Output Style Variables
104+
105+
| Variable | Purpose | Values | Default |
106+
|------------------------------|-----------------------------------|-----------------------------------|------------|
107+
| ARCA_STYLE | Force specific output style | ansi, plain, json, dump | auto |
108+
| NO_COLOR | Disable colored output | 1 (to disable), unset (to enable) | unset |
109+
| MIX_ENV | Elixir environment | test, dev, prod | dev |
110+
111+
**Output Style Behavior:**
112+
113+
- `ARCA_STYLE=ansi`: Force colored ANSI output with symbols and formatting
114+
- `ARCA_STYLE=plain`: Force plain text output without ANSI codes
115+
- `ARCA_STYLE=json`: Output structured JSON for programmatic consumption
116+
- `ARCA_STYLE=dump`: Output raw data structures for debugging
117+
- `NO_COLOR=1`: Disable colors (equivalent to `ARCA_STYLE=plain`)
118+
- `MIX_ENV=test`: Automatically uses plain style for test output
119+
120+
**Style Detection Priority:**
121+
122+
1. Test environment (`MIX_ENV=test`) → Plain style
123+
2. `NO_COLOR=1` → Plain style
124+
3. `ARCA_STYLE` value → Specified style
125+
4. Non-TTY environment → Plain style
126+
5. Interactive TTY → ANSI style (default)
127+
128+
**Usage Examples:**
129+
130+
```bash
131+
# Force plain output for scripting
132+
ARCA_STYLE=plain arca_cli command > output.txt
133+
134+
# Disable colors for CI/CD pipelines
135+
NO_COLOR=1 arca_cli command
136+
137+
# Get JSON for parsing
138+
ARCA_STYLE=json arca_cli command | jq '.output'
139+
140+
# Debug with raw data structures
141+
ARCA_STYLE=dump arca_cli command
142+
```
143+
101144
Example configuration in `.bashrc` or `.zshrc` for an application named `arca_cli`:
102145

103146
```bash

intent/usr/reference_guide.md

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,191 @@ arca_cli dev.deps
189189

190190
## API Reference
191191

192+
### Context Module (`Arca.Cli.Ctx`)
193+
194+
The Context module provides a structured way to build command output.
195+
196+
#### Context Structure
197+
198+
```elixir
199+
%Arca.Cli.Ctx{
200+
command: atom(), # Command being executed
201+
args: map(), # Parsed arguments
202+
options: map(), # Command options
203+
output: list(), # Structured output items
204+
errors: list(), # Error messages
205+
status: atom(), # :ok | :error | :warning | :pending
206+
cargo: map(), # Command-specific data
207+
meta: map() # Style, format, and other metadata
208+
}
209+
```
210+
211+
#### Core Functions
212+
213+
**`Ctx.new(command, settings)`**
214+
215+
Creates a new context for a command.
216+
217+
```elixir
218+
ctx = Ctx.new(:my_command, settings)
219+
```
220+
221+
**`Ctx.add_output(ctx, output_item)`**
222+
223+
Adds an output item to the context.
224+
225+
```elixir
226+
ctx = Ctx.add_output(ctx, {:success, "Operation completed"})
227+
```
228+
229+
**`Ctx.add_error(ctx, error_message)`**
230+
231+
Adds an error message to the context.
232+
233+
```elixir
234+
ctx = Ctx.add_error(ctx, "File not found")
235+
```
236+
237+
**`Ctx.complete(ctx, status)`**
238+
239+
Marks the context as complete with a final status.
240+
241+
```elixir
242+
ctx = Ctx.complete(ctx, :ok) # or :error, :warning
243+
```
244+
245+
**`Ctx.put_cargo(ctx, key, value)`** / **`Ctx.get_cargo(ctx, key)`**
246+
247+
Stores and retrieves command-specific data.
248+
249+
```elixir
250+
ctx = Ctx.put_cargo(ctx, :user_count, 42)
251+
count = Ctx.get_cargo(ctx, :user_count)
252+
```
253+
254+
#### Output Item Types
255+
256+
**Messages**
257+
258+
```elixir
259+
{:success, message} # Green checkmark in ANSI mode
260+
{:error, message} # Red X in ANSI mode
261+
{:warning, message} # Yellow warning symbol in ANSI mode
262+
{:info, message} # Cyan info symbol in ANSI mode
263+
{:text, content} # Plain text
264+
```
265+
266+
**Tables**
267+
268+
```elixir
269+
# With explicit headers (columns in header order)
270+
{:table, rows, headers: ["Name", "Age", "City"]}
271+
272+
# Override column order
273+
{:table, rows,
274+
headers: ["Name", "Age", "City"],
275+
column_order: ["City", "Age", "Name"]
276+
}
277+
278+
# First row as headers (preserves row order)
279+
{:table, [headers_row | data_rows], has_headers: true}
280+
281+
# Alphabetical column order (default when no headers/column_order)
282+
{:table, rows, []}
283+
284+
# Descending alphabetical
285+
{:table, rows, column_order: :desc}
286+
```
287+
288+
**Table Options:**
289+
290+
- `headers`: List of column header names (also sets column order)
291+
- `column_order`: Explicit column ordering (list, `:asc`, `:desc`, or custom function)
292+
- `has_headers`: Boolean, treats first row as headers
293+
- `border_style`: `:solid`, `:solid_rounded`, etc.
294+
- `divide_body_rows`: Boolean, add lines between rows
295+
- `padding_x`: Integer, horizontal padding in cells
296+
297+
**Lists**
298+
299+
```elixir
300+
# Simple list
301+
{:list, ["Item 1", "Item 2", "Item 3"]}
302+
303+
# List with title
304+
{:list, items, title: "Available Options"}
305+
306+
# List with custom bullet color (ANSI mode)
307+
{:list, items, bullet_color: :green}
308+
```
309+
310+
**Interactive Elements (ANSI mode only)**
311+
312+
```elixir
313+
# Spinner
314+
{:spinner, "Loading data", fn ->
315+
result = perform_operation()
316+
{:ok, result}
317+
end}
318+
319+
# Progress indicator
320+
{:progress, "Processing files", fn ->
321+
result = process_files()
322+
{:ok, result}
323+
end}
324+
```
325+
326+
### Output Module (`Arca.Cli.Output`)
327+
328+
The Output module handles rendering of contexts to different formats.
329+
330+
**`Output.render(ctx)`**
331+
332+
Renders a context to a string using the appropriate style.
333+
334+
```elixir
335+
output = Arca.Cli.Output.render(ctx)
336+
IO.puts(output)
337+
```
338+
339+
**Style Detection**
340+
341+
The output style is automatically determined:
342+
343+
1. Plain style if `MIX_ENV=test`
344+
2. Plain style if `NO_COLOR=1`
345+
3. Style from `ARCA_STYLE` environment variable (ansi, plain, json, dump)
346+
4. Plain style if not a TTY
347+
5. ANSI style otherwise (default for interactive terminals)
348+
349+
### Renderers
350+
351+
**AnsiRenderer** - Colored output with formatting
352+
353+
- Green checkmarks for success
354+
- Red X for errors
355+
- Yellow warnings
356+
- Cyan info messages
357+
- Rounded borders for tables
358+
- Colored bullet points for lists
359+
360+
**PlainRenderer** - No ANSI codes
361+
362+
- Simple symbols (✓, ✗, ⚠)
363+
- Box-drawing characters for tables
364+
- Plain bullet points for lists
365+
- Suitable for tests and non-TTY environments
366+
367+
**JsonRenderer** - JSON structured output
368+
369+
- Outputs context as JSON
370+
- Useful for programmatic consumption
371+
372+
**DumpRenderer** - Raw data inspection
373+
374+
- Shows internal structure
375+
- Useful for debugging
376+
192377
### Command Definition
193378

194379
Commands are defined using the `Arca.Cli.Command.BaseCommand` module:
@@ -216,6 +401,58 @@ defmodule YourApp.Cli.Commands.GreetCommand do
216401
end
217402
```
218403

404+
### Context-Based Command Definition
405+
406+
Commands can return structured output using the Context system:
407+
408+
```elixir
409+
defmodule YourApp.Cli.Commands.UserCommand do
410+
use Arca.Cli.Command.BaseCommand
411+
alias Arca.Cli.Ctx
412+
413+
config :user,
414+
name: "user",
415+
about: "Display user information",
416+
args: [
417+
username: [
418+
value_name: "USERNAME",
419+
help: "Username to lookup",
420+
required: true,
421+
parser: :string
422+
]
423+
]
424+
425+
@impl true
426+
def handle(args, settings, _optimus) do
427+
username = args.args.username
428+
429+
Ctx.new(:user, settings)
430+
|> fetch_user_data(username)
431+
|> format_output()
432+
|> Ctx.complete(:ok)
433+
end
434+
435+
defp fetch_user_data(ctx, username) do
436+
case Database.get_user(username) do
437+
{:ok, user} ->
438+
Ctx.put_cargo(ctx, :user, user)
439+
440+
{:error, reason} ->
441+
ctx
442+
|> Ctx.add_error("User not found: #{reason}")
443+
|> Ctx.complete(:error)
444+
end
445+
end
446+
447+
defp format_output(%Ctx{status: :error} = ctx), do: ctx
448+
defp format_output(%Ctx{cargo: %{user: user}} = ctx) do
449+
ctx
450+
|> Ctx.add_output({:success, "User found: #{user.name}"})
451+
|> Ctx.add_output({:table, [user], headers: ["id", "name", "email", "created_at"]})
452+
end
453+
end
454+
```
455+
219456
### Namespaced Command Definition
220457

221458
Using the NamespaceCommandHelper for multiple related commands:

0 commit comments

Comments
 (0)