UI (fnord v0.8.82)

View Source

User interface functions for output, logging, and user interaction.

Context Warnings for Interactive UI

Interactive UI functions (confirm/1, choose/2, prompt/1) can cause deadlocks when called from certain contexts and must be wrapped appropriately.

GenServer Callbacks

Use UI.Queue.run_from_genserver/1 to prevent deadlocks:

def handle_call(:delete_item, _from, state) do
  confirmed = UI.Queue.run_from_genserver(fn ->
    UI.confirm("Delete this item?")
  end)
  # ...
end

Services.Globals.Spawn.async and Spawned Processes

Use UI.Queue.run_from_task/1 when tasks need to participate in an existing UI interaction:

task = Services.Globals.Spawn.async(fn ->
  UI.Queue.run_from_task(fn ->
    UI.confirm("Process this item?")
  end)
end)

Creating UI Components

Use UI.interact/1 to group multiple UI operations into a single atomic component:

def confirm_with_details(item) do
  UI.interact(fn ->
    UI.info("Item details: #{item.name}")
    UI.puts("Size: #{item.size}, Modified: #{item.date}")
    UI.confirm("Delete this item?")
  end)
end

Non-interactive functions (info/2, warn/2, error/2, puts/1, say/1) are safe to call directly from any context.

Interactive vs Non-Interactive Functions

Interactive (require context wrappers in GenServer/Task contexts):

Non-interactive (safe to call directly from any context):

Summary

Functions

async_stream(enumerable, fun, label \\ "Working", options \\ [])

begin_step(msg)

begin_step(msg, detail)

box(contents, opts)

choose(label, options)

choose(label, options, timeout_ms, default)

clean_detail(detail)

colorize?()

confirm(msg)

@spec confirm(binary()) :: boolean()

confirm(msg, default)

@spec confirm(binary(), boolean()) :: boolean()

debug(msg)

debug(msg, detail)

end_step(msg)

end_step(msg, detail)

error(msg)

error(msg, detail)

fatal(msg)

@spec fatal(binary()) :: no_return()

fatal(msg, detail)

@spec fatal(binary(), binary()) :: no_return()

feedback(atom, name, msg)

flush()

info(msg)

info(msg, detail)

interact(fun)

Execute a function as a single interaction unit. All UI calls within the function (puts, log, choose, prompt, etc.) will be treated as part of this interaction and execute immediately without queuing.

This is useful for composite TUI components that combine multiple UI elements.

iodata?(term)

is_tty?()

italicize(text)

@spec italicize(binary()) :: iodata()

log_usage(model, usage)

@spec log_usage(AI.Model.t(), non_neg_integer() | map()) :: :ok

newline()

printf_debug(item)

progress_bar_start(name, label, total)

progress_bar_update(name)

prompt(prompt, owl_opts \\ [])

puts(msg)

quiet?()

report_from(name, msg)

report_from(name, msg, detail)

report_step(msg)

report_step(msg, detail)

say(msg)

spin(processing, func)

warn(msg)

warn(msg, detail)

warning_banner(msg)

@spec warning_banner(binary()) :: :ok