Shared helper functions used across Omni.UI components.
Provides utilities for formatting token usage, building CSS class lists, rendering markdown, and working with Omni data structures.
Summary
Functions
Returns a URL for an Omni.Content.Attachment.
Builds a CSS class string from various input formats.
Finds the label for a value in a flat or grouped options list.
Pretty-prints a value as JSON.
Formats a list of Omni.Model structs into grouped select options.
Formats a token cost as a dollar amount with 4 decimal places.
Formats a token count for compact display.
Extracts and pretty-prints the text content from a tool result.
Syntax-highlights a code string as HTML.
Returns the Tailwind utility classes that apply markdown typography styles.
Returns a string key for an Omni.Model in "provider:model" format.
Returns a human-readable position string like "2/5" for an element
among its siblings.
Formats a datetime as a short "time ago" label.
Converts a markdown string to HTML using MDEx with GFM and Mermaid support.
Functions
@spec attachment_url(Omni.Content.Attachment.t()) :: String.t()
Returns a URL for an Omni.Content.Attachment.
For base64-encoded attachments, returns a data URI. For URL-sourced attachments, returns the URL as-is.
Examples
iex> attachment = %Omni.Content.Attachment{media_type: "image/png", source: {:base64, "abc123"}}
iex> Omni.UI.Helpers.attachment_url(attachment)
"data:image/png;base64,abc123"
iex> attachment = %Omni.Content.Attachment{media_type: "image/png", source: {:url, "https://example.com/img.png"}}
iex> Omni.UI.Helpers.attachment_url(attachment)
"https://example.com/img.png"
Builds a CSS class string from various input formats.
Accepts a string, a list, or a map:
- String — returned as-is.
- List — falsy values (
nil,false) are filtered out, remaining entries are joined with spaces. - Map — keys whose values are truthy are included, joined with spaces.
Examples
iex> Omni.UI.Helpers.cls("foo bar")
"foo bar"
iex> Omni.UI.Helpers.cls(["foo", nil, "bar", false])
"foo bar"
iex> Omni.UI.Helpers.cls(%{"active" => true, "hidden" => false})
"active"
Finds the label for a value in a flat or grouped options list.
Searches through options of the form %{value: v, label: l} or grouped
options %{options: [%{value: v, label: l}]}. Returns the matching label
or nil if not found.
Examples
iex> options = [%{value: "a", label: "Alpha"}, %{value: "b", label: "Beta"}]
iex> Omni.UI.Helpers.find_option_label(options, "b")
"Beta"
iex> grouped = [%{label: "Group", options: [%{value: "x", label: "X-ray"}]}]
iex> Omni.UI.Helpers.find_option_label(grouped, "x")
"X-ray"
iex> Omni.UI.Helpers.find_option_label([%{value: "a", label: "A"}], "z")
nil
Pretty-prints a value as JSON.
When given a string, attempts to decode it as JSON first. If decoding
succeeds, re-encodes it with pretty formatting. If the input is already
a decoded data structure, encodes it directly. Falls back to the original
string or inspect/1 if encoding fails.
Examples
iex> Omni.UI.Helpers.format_json(~s|{"a":1}|)
~s|{\n "a": 1\n}|
iex> Omni.UI.Helpers.format_json("not json")
"not json"
iex> Omni.UI.Helpers.format_json(%{"key" => "value"})
~s|{\n "key": "value"\n}|
@spec format_model_options([Omni.Model.t()] | nil) :: [map()] | nil
Formats a list of Omni.Model structs into grouped select options.
Groups models by provider name, sorts both groups and models alphabetically,
and returns a list of %{label: provider, options: [%{value: key, label: name}]} maps
suitable for the select component. Returns nil for nil or empty input.
Formats a token cost as a dollar amount with 4 decimal places.
Returns "-" for nil.
Examples
iex> Omni.UI.Helpers.format_token_cost(0.0123)
"0.0123"
iex> Omni.UI.Helpers.format_token_cost(nil)
"-"
@spec format_token_count(non_neg_integer() | nil) :: String.t()
Formats a token count for compact display.
Counts under 1,000 are shown as-is. Counts from 1,000–9,999 are shown
with one decimal place (e.g. "1.5k"). Counts of 10,000+ are rounded
to the nearest thousand (e.g. "42k"). Returns "-" for nil.
Examples
iex> Omni.UI.Helpers.format_token_count(500)
"500"
iex> Omni.UI.Helpers.format_token_count(1_500)
"1.5k"
iex> Omni.UI.Helpers.format_token_count(42_000)
"42k"
iex> Omni.UI.Helpers.format_token_count(nil)
"-"
@spec format_tool_result(Omni.Content.ToolResult.t()) :: String.t()
Extracts and pretty-prints the text content from a tool result.
Filters for Omni.Content.Text entries in the result's content list,
joins their text, and formats the combined string as JSON.
Examples
iex> result = %Omni.Content.ToolResult{tool_use_id: "1", name: "search", content: [%Omni.Content.Text{text: ~s|{"ok":true}|}]}
iex> Omni.UI.Helpers.format_tool_result(result)
~s|{\n "ok": true\n}|
@spec highlight_code(String.t(), String.t() | nil) :: Phoenix.HTML.safe()
Syntax-highlights a code string as HTML.
Uses Lumis with inline styles (catppuccin_macchiato theme) so the output
is self-contained — no external CSS needed. When lang is nil, Lumis
auto-detects the language. The lang value can be a language name
("elixir", "json") or a filename ("report.html").
Returns a Phoenix.HTML.safe/0 tuple for direct use in HEEx templates.
@spec md_styles() :: [String.t()]
Returns the Tailwind utility classes that apply markdown typography styles.
Applied at the chat_interface/1 level via descendant selectors targeting
the .mdex class, so individual markdown components stay minimal.
@spec model_key(Omni.Model.t()) :: String.t()
Returns a string key for an Omni.Model in "provider:model" format.
Uses Omni.Model.to_ref/1 to resolve the provider atom and model ID,
then joins them with a colon.
Returns a human-readable position string like "2/5" for an element
among its siblings.
Examples
iex> Omni.UI.Helpers.sibling_pos(:b, [:a, :b, :c])
"2/3"
@spec time_ago(DateTime.t(), String.t()) :: String.t()
Formats a datetime as a short "time ago" label.
Returns one of:
"just now"for under a minute"Xm ago"/"Xh ago"/"Xd ago"for up to a week- a formatted absolute date beyond that (default:
"Apr 13")
The second argument is the Calendar.strftime/2 format string used for
the absolute-date fallback, so callers can control how old dates render
(e.g. the chat timestamp might want to include the year, the sessions
list might prefer month + day).
Examples
iex> Omni.UI.Helpers.time_ago(DateTime.utc_now())
"just now"
iex> dt = DateTime.add(DateTime.utc_now(), -300, :second)
iex> Omni.UI.Helpers.time_ago(dt)
"5m ago"
@spec to_md( String.t(), keyword() ) :: Phoenix.HTML.safe()
Converts a markdown string to HTML using MDEx with GFM and Mermaid support.
Returns a Phoenix.HTML.safe/0 tuple for direct use in HEEx templates.
Options
:streaming- whentrue, enables MDEx streaming mode for incremental rendering of in-progress content. Defaults tofalse.