Guppy.IR (guppy v0.1.0)

Copy Markdown View Source

Helpers and validation for Guppy's Elixir-owned UI IR.

Elixir processes build full-tree IR values, Guppy validates the supported schema, and the native runtime renders those trees through GPUI while routing events back to the owning process.

Style tokens are represented as ordered lists. That order is preserved across the bridge so later tokens can override earlier ones.

Summary

Types

action_bindings()

@type action_bindings() :: %{optional(action_name()) => callback_id()}

action_name()

@type action_name() :: String.t()

animation()

@type animation() :: %{
  :id => String.t(),
  optional(:duration_ms) => pos_integer(),
  optional(:repeat) => boolean(),
  optional(:from) => number(),
  optional(:to) => number()
}

background_pattern_options()

@type background_pattern_options() :: [
  color: gradient_color(),
  width: number(),
  interval: number()
]

border_radius_axis()

@type border_radius_axis() ::
  :all
  | :top
  | :right
  | :bottom
  | :left
  | :top_left
  | :top_right
  | :bottom_left
  | :bottom_right

box_shadow_options()

@type box_shadow_options() :: [
  color: gradient_color(),
  x: number(),
  y: number(),
  blur: number(),
  spread: number()
]

button_events()

@type button_events() :: %{
  optional(:click) => String.t(),
  optional(:hover) => String.t(),
  optional(:focus) => String.t(),
  optional(:blur) => String.t(),
  optional(:key_down) => String.t(),
  optional(:key_up) => String.t(),
  optional(:context_menu) => String.t(),
  optional(:mouse_down) => String.t(),
  optional(:mouse_up) => String.t(),
  optional(:mouse_move) => String.t()
}

button_node()

@type button_node() :: %{
  :kind => :button,
  :label => String.t(),
  optional(:id) => node_id(),
  optional(:style) => style(),
  optional(:hover_style) => style(),
  optional(:focus_style) => style(),
  optional(:focus_visible_style) => style(),
  optional(:in_focus_style) => style(),
  optional(:active_style) => style(),
  optional(:disabled_style) => style(),
  optional(:animation) => animation(),
  optional(:disabled) => boolean(),
  optional(:tab_index) => integer(),
  optional(:actions) => action_bindings(),
  optional(:shortcuts) => [shortcut_binding()],
  optional(:events) => button_events()
}

callback_id()

@type callback_id() :: String.t()

canvas_color()

@type canvas_color() :: gradient_color()

canvas_command()

canvas_events()

@type canvas_events() :: %{
  optional(:click) => String.t(),
  optional(:context_menu) => String.t()
}

canvas_node()

@type canvas_node() :: %{
  :kind => :canvas,
  :commands => [canvas_command()],
  optional(:id) => node_id(),
  optional(:style) => style(),
  optional(:events) => canvas_events()
}

canvas_pattern_rect_command()

@type canvas_pattern_rect_command() :: %{
  :op => :pattern_rect,
  :x => number(),
  :y => number(),
  :width => number(),
  :height => number(),
  :color => canvas_color(),
  :line_width => number(),
  :interval => number(),
  optional(:radius) => number()
}

canvas_rect_command()

@type canvas_rect_command() :: %{
  :op => :rect,
  :x => number(),
  :y => number(),
  :width => number(),
  :height => number(),
  :fill => canvas_color(),
  optional(:radius) => number()
}

canvas_rounded_rect_command()

@type canvas_rounded_rect_command() :: %{
  op: :rounded_rect,
  x: number(),
  y: number(),
  width: number(),
  height: number(),
  fill: canvas_color(),
  radius: number()
}

checkbox_events()

@type checkbox_events() :: %{
  optional(:change) => String.t(),
  optional(:focus) => String.t(),
  optional(:blur) => String.t()
}

checkbox_node()

@type checkbox_node() :: %{
  :kind => :checkbox,
  :label => String.t(),
  :checked => boolean(),
  optional(:id) => node_id(),
  optional(:style) => style(),
  optional(:hover_style) => style(),
  optional(:focus_style) => style(),
  optional(:focus_visible_style) => style(),
  optional(:in_focus_style) => style(),
  optional(:active_style) => style(),
  optional(:disabled_style) => style(),
  optional(:disabled) => boolean(),
  optional(:tab_index) => integer(),
  optional(:events) => checkbox_events()
}

color_token()

@type color_token() :: :red | :green | :blue | :yellow | :black | :white | :gray

data_table_cell()

@type data_table_cell() :: %{
  :column_id => node_id(),
  :children => [data_table_cell_node()],
  optional(:style) => style()
}

data_table_cell_div_node()

@type data_table_cell_div_node() :: %{
  :kind => :div,
  :children => [data_table_cell_node()],
  optional(:id) => node_id(),
  optional(:style) => style(),
  optional(:disabled) => boolean(),
  optional(:events) => list_row_click_events()
}

data_table_cell_node()

@type data_table_cell_node() ::
  text_node() | spacer_node() | data_table_cell_div_node()

data_table_column()

@type data_table_column() :: %{
  :id => node_id(),
  :label => String.t(),
  optional(:width) => data_table_column_width(),
  optional(:sortable) => boolean(),
  optional(:pinned) => boolean(),
  optional(:style) => style()
}

data_table_column_width()

@type data_table_column_width() :: :auto | {:px, number()} | {:fr, pos_integer()}

data_table_events()

@type data_table_events() :: %{
  optional(:row_click) => String.t(),
  optional(:cell_click) => String.t(),
  optional(:sort) => String.t(),
  optional(:column_reorder) => String.t(),
  optional(:column_resize) => String.t(),
  optional(:row_context_menu) => String.t(),
  optional(:cell_context_menu) => String.t()
}

data_table_node()

@type data_table_node() :: %{
  :kind => :data_table,
  :columns => [data_table_column()],
  :rows => [data_table_row()],
  optional(:id) => node_id(),
  optional(:style) => style(),
  optional(:header_style) => style(),
  optional(:row_style) => style(),
  optional(:cell_style) => style(),
  optional(:selected_row_id) => node_id(),
  optional(:selected_cell) => {node_id(), node_id()},
  optional(:sort) => data_table_sort(),
  optional(:events) => data_table_events()
}

data_table_row()

@type data_table_row() :: %{
  :id => node_id(),
  :cells => [data_table_cell()],
  optional(:style) => style()
}

data_table_sort()

@type data_table_sort() :: %{column_id: node_id(), direction: :asc | :desc}

div_events()

@type div_events() :: %{
  optional(:click) => String.t(),
  optional(:hover) => String.t(),
  optional(:focus) => String.t(),
  optional(:blur) => String.t(),
  optional(:key_down) => String.t(),
  optional(:key_up) => String.t(),
  optional(:context_menu) => String.t(),
  optional(:drag_start) => String.t(),
  optional(:drag_move) => String.t(),
  optional(:drop) => String.t(),
  optional(:mouse_down) => String.t(),
  optional(:mouse_up) => String.t(),
  optional(:mouse_move) => String.t(),
  optional(:scroll_wheel) => String.t()
}

div_node()

@type div_node() :: %{
  :kind => :div,
  :children => [ir_node()],
  optional(:id) => node_id(),
  optional(:style) => style(),
  optional(:hover_style) => style(),
  optional(:focus_style) => style(),
  optional(:focus_visible_style) => style(),
  optional(:in_focus_style) => style(),
  optional(:active_style) => style(),
  optional(:disabled_style) => style(),
  optional(:animation) => animation(),
  optional(:disabled) => boolean(),
  optional(:stack_priority) => non_neg_integer(),
  optional(:occlude) => boolean(),
  optional(:focusable) => boolean(),
  optional(:tab_stop) => boolean(),
  optional(:tab_index) => integer(),
  optional(:track_scroll) => boolean(),
  optional(:anchor_scroll) => boolean(),
  optional(:scroll_to) => boolean(),
  optional(:tooltip) => String.t(),
  optional(:actions) => action_bindings(),
  optional(:shortcuts) => [shortcut_binding()],
  optional(:events) => div_events()
}

gradient_color()

@type gradient_color() :: color_token() | String.t()

icon_node()

@type icon_node() :: %{
  :kind => :icon,
  :source => image_source(),
  optional(:id) => node_id(),
  optional(:style) => style()
}

image_node()

@type image_node() :: %{
  :kind => :image,
  :source => image_source(),
  optional(:id) => node_id(),
  optional(:style) => style(),
  optional(:object_fit) => image_object_fit(),
  optional(:grayscale) => boolean()
}

image_object_fit()

@type image_object_fit() :: :fill | :contain | :cover | :scale_down | :none

image_source()

@type image_source() ::
  String.t()
  | {:uri, String.t()}
  | {:path, String.t()}
  | {:embedded, String.t()}

ir_node()

linear_gradient_stop()

@type linear_gradient_stop() :: {gradient_color(), number()}

list_events()

@type list_events() :: %{
  optional(:click) => String.t(),
  optional(:context_menu) => String.t()
}

list_item()

@type list_item() :: %{id: node_id(), children: [list_row_node()]}

list_node()

@type list_node() :: %{
  :kind => :list,
  :items => [list_item()],
  optional(:id) => node_id(),
  optional(:style) => style(),
  optional(:item_style) => style(),
  optional(:events) => list_events()
}

list_row_button_node()

@type list_row_button_node() :: %{
  :kind => :button,
  :label => String.t(),
  :id => node_id(),
  optional(:style) => style(),
  optional(:hover_style) => style(),
  optional(:focus_style) => style(),
  optional(:focus_visible_style) => style(),
  optional(:in_focus_style) => style(),
  optional(:active_style) => style(),
  optional(:disabled_style) => style(),
  optional(:disabled) => boolean(),
  optional(:tab_index) => integer(),
  optional(:events) => list_row_click_events()
}

list_row_change_events()

@type list_row_change_events() :: %{optional(:change) => String.t()}

list_row_checkbox_node()

@type list_row_checkbox_node() :: %{
  :kind => :checkbox,
  :label => String.t(),
  :checked => boolean(),
  :id => node_id(),
  optional(:style) => style(),
  optional(:hover_style) => style(),
  optional(:focus_style) => style(),
  optional(:focus_visible_style) => style(),
  optional(:in_focus_style) => style(),
  optional(:active_style) => style(),
  optional(:disabled_style) => style(),
  optional(:disabled) => boolean(),
  optional(:tab_index) => integer(),
  optional(:events) => list_row_change_events()
}

list_row_click_events()

@type list_row_click_events() :: %{optional(:click) => String.t()}

list_row_div_node()

@type list_row_div_node() :: %{
  :kind => :div,
  :children => [list_row_node()],
  optional(:id) => node_id(),
  optional(:style) => style(),
  optional(:disabled) => boolean(),
  optional(:events) => list_row_click_events()
}

list_row_node()

list_row_radio_node()

@type list_row_radio_node() :: %{
  :kind => :radio,
  :label => String.t(),
  :value => String.t(),
  :checked => boolean(),
  :id => node_id(),
  optional(:style) => style(),
  optional(:hover_style) => style(),
  optional(:focus_style) => style(),
  optional(:focus_visible_style) => style(),
  optional(:in_focus_style) => style(),
  optional(:active_style) => style(),
  optional(:disabled_style) => style(),
  optional(:disabled) => boolean(),
  optional(:tab_index) => integer(),
  optional(:events) => list_row_change_events()
}

node_id()

@type node_id() :: String.t()

point()

@type point() :: {number(), number()}

popover_anchor()

@type popover_anchor() :: :top_left | :top_right | :bottom_left | :bottom_right

popover_anchor_fit()

@type popover_anchor_fit() ::
  :switch_anchor | :snap_to_window | :snap_to_window_with_margin

popover_anchor_position_mode()

@type popover_anchor_position_mode() :: :window | :local

popover_events()

@type popover_events() :: %{
  optional(:click) => String.t(),
  optional(:close) => String.t()
}

popover_node()

@type popover_node() :: %{
  :kind => :popover,
  :label => String.t(),
  :open => boolean(),
  :children => [ir_node()],
  optional(:id) => node_id(),
  optional(:style) => style(),
  optional(:popover_style) => style(),
  optional(:anchor) => popover_anchor(),
  optional(:anchor_position) => point(),
  optional(:anchor_offset) => point(),
  optional(:anchor_position_mode) => popover_anchor_position_mode(),
  optional(:anchor_fit) => popover_anchor_fit(),
  optional(:snap_margin) => number(),
  optional(:close_on_click_outside) => boolean(),
  optional(:stack_priority) => non_neg_integer(),
  optional(:disabled) => boolean(),
  optional(:events) => popover_events()
}

radio_node()

@type radio_node() :: %{
  :kind => :radio,
  :label => String.t(),
  :value => String.t(),
  :checked => boolean(),
  optional(:id) => node_id(),
  optional(:style) => style(),
  optional(:hover_style) => style(),
  optional(:focus_style) => style(),
  optional(:focus_visible_style) => style(),
  optional(:in_focus_style) => style(),
  optional(:active_style) => style(),
  optional(:disabled_style) => style(),
  optional(:disabled) => boolean(),
  optional(:tab_index) => integer(),
  optional(:events) => checkbox_events()
}

scroll_axis()

@type scroll_axis() :: :x | :y | :both

scroll_node()

@type scroll_node() :: %{
  :kind => :scroll,
  :children => [ir_node()],
  optional(:id) => node_id(),
  optional(:axis) => scroll_axis(),
  optional(:style) => style()
}

select_events()

@type select_events() :: %{
  optional(:click) => String.t(),
  optional(:change) => String.t(),
  optional(:close) => String.t(),
  optional(:focus) => String.t(),
  optional(:blur) => String.t()
}

select_node()

@type select_node() :: %{
  :kind => :select,
  :options => [select_option()],
  optional(:id) => node_id(),
  optional(:value) => String.t(),
  optional(:open) => boolean(),
  optional(:placeholder) => String.t(),
  optional(:style) => style(),
  optional(:list_style) => style(),
  optional(:option_style) => style(),
  optional(:anchor) => popover_anchor(),
  optional(:anchor_offset) => point(),
  optional(:anchor_fit) => popover_anchor_fit(),
  optional(:snap_margin) => number(),
  optional(:disabled) => boolean(),
  optional(:tab_index) => integer(),
  optional(:events) => select_events()
}

select_option()

@type select_option() :: %{
  :value => String.t(),
  :label => String.t(),
  optional(:disabled) => boolean()
}

shortcut_binding()

@type shortcut_binding() :: {String.t(), action_name()}

spacer_node()

@type spacer_node() :: %{
  :kind => :spacer,
  optional(:id) => node_id(),
  optional(:style) => style()
}

style()

@type style() :: [style_op()]

style_axis()

@type style_axis() :: :all | :x | :y | :top | :right | :bottom | :left

style_flag()

@type style_flag() ::
  :grid
  | :flex
  | :flex_col
  | :flex_row
  | :flex_wrap
  | :flex_nowrap
  | :flex_none
  | :flex_auto
  | :flex_grow
  | :flex_shrink
  | :flex_shrink_0
  | :flex_1
  | :col_span_full
  | :row_span_full
  | :size_full
  | :w_full
  | :h_full
  | :w_32
  | :w_64
  | :w_96
  | :h_32
  | :min_w_32
  | :min_h_0
  | :min_h_full
  | :max_w_64
  | :max_w_96
  | :max_w_full
  | :max_h_32
  | :max_h_96
  | :max_h_full
  | :gap_1
  | :gap_2
  | :gap_4
  | :p_1
  | :p_2
  | :p_4
  | :p_6
  | :p_8
  | :px_2
  | :py_2
  | :pt_2
  | :pr_2
  | :pb_2
  | :pl_2
  | :m_2
  | :mx_2
  | :my_2
  | :mt_2
  | :mr_2
  | :mb_2
  | :ml_2
  | :relative
  | :absolute
  | :top_0
  | :right_0
  | :bottom_0
  | :left_0
  | :inset_0
  | :top_1
  | :right_1
  | :top_2
  | :right_2
  | :bottom_2
  | :left_2
  | :text_left
  | :text_center
  | :text_right
  | :whitespace_normal
  | :whitespace_nowrap
  | :truncate
  | :text_ellipsis
  | :line_clamp_2
  | :line_clamp_3
  | :text_xs
  | :text_sm
  | :text_base
  | :text_lg
  | :text_xl
  | :text_2xl
  | :text_3xl
  | :leading_none
  | :leading_tight
  | :leading_snug
  | :leading_normal
  | :leading_relaxed
  | :leading_loose
  | :font_thin
  | :font_extralight
  | :font_light
  | :font_normal
  | :font_medium
  | :font_semibold
  | :font_bold
  | :font_extrabold
  | :font_black
  | :italic
  | :not_italic
  | :underline
  | :line_through
  | :items_start
  | :items_center
  | :items_end
  | :justify_start
  | :justify_center
  | :justify_end
  | :justify_between
  | :justify_around
  | :cursor_pointer
  | :rounded_sm
  | :rounded_md
  | :rounded_lg
  | :rounded_xl
  | :rounded_2xl
  | :rounded_full
  | :border_1
  | :border_2
  | :border_dashed
  | :border_t_1
  | :border_r_1
  | :border_b_1
  | :border_l_1
  | :shadow_sm
  | :shadow_md
  | :shadow_lg
  | :overflow_scroll
  | :overflow_x_scroll
  | :overflow_y_scroll
  | :overflow_hidden
  | :overflow_x_hidden
  | :overflow_y_hidden

style_length()

@type style_length() :: {:px, number()} | {:rem, number()} | {:fraction, number()}

style_op()

@type style_op() :: style_flag() | style_value()

style_value()

@type style_value() ::
  {:padding, style_axis(), style_length()}
  | {:margin, style_axis(), style_length() | :auto}
  | {:gap, :all | :x | :y, style_length()}
  | {:width, style_length() | :auto}
  | {:height, style_length() | :auto}
  | {:size, style_length() | :auto}
  | {:min_width, style_length() | :auto}
  | {:min_height, style_length() | :auto}
  | {:max_width, style_length() | :auto}
  | {:max_height, style_length() | :auto}
  | {:aspect_ratio, number()}
  | {:position, :relative | :absolute}
  | {:inset, :all | :top | :right | :bottom | :left, style_length() | :auto}
  | {:display, :block | :flex | :grid | :none}
  | {:visibility, :visible | :hidden}
  | {:overflow, :all | :x | :y, :visible | :clip | :hidden | :scroll}
  | {:allow_concurrent_scroll, boolean()}
  | {:restrict_scroll_to_axis, boolean()}
  | {:debug, boolean()}
  | {:debug_below, boolean()}
  | {:cursor, atom()}
  | {:border_width, style_axis(), {:px | :rem, number()}}
  | {:border_radius, border_radius_axis(), {:px | :rem, number()}}
  | {:border_style, :solid | :dashed}
  | {:shadow, :none | :"2xs" | :xs | :sm | :md | :lg | :xl | :"2xl"}
  | {:flex_direction, :column | :column_reverse | :row | :row_reverse}
  | {:flex_wrap, :wrap | :wrap_reverse | :nowrap}
  | {:flex_item, :one | :auto | :initial | :none | :grow | :shrink | :shrink_0}
  | {:flex_basis, style_length() | :auto}
  | {:flex_grow, number()}
  | {:flex_shrink, number()}
  | {:align_items, :start | :end | :center | :baseline | :stretch}
  | {:align_self, :start | :end | :center | :baseline | :stretch}
  | {:justify_content,
     :start | :end | :center | :between | :around | :evenly | :stretch}
  | {:align_content,
     :normal | :start | :end | :center | :between | :around | :evenly | :stretch}
  | {:bg, color_token()}
  | {:text_color, color_token()}
  | {:text_bg, color_token()}
  | {:font_size, :xs | :sm | :base | :lg | :xl | :"2xl" | :"3xl"}
  | {:text_size, {:px | :rem, number()}}
  | {:line_height, :none | :tight | :snug | :normal | :relaxed | :loose}
  | {:line_height_length, {:px | :rem | :fraction, number()}}
  | {:font_weight_value, number()}
  | {:font_family, String.t()}
  | {:font_fallbacks, [String.t()]}
  | {:font_features, [{String.t(), non_neg_integer()}]}
  | {:border_color, color_token()}
  | {:bg_hex, String.t()}
  | {:text_color_hex, String.t()}
  | {:text_bg_hex, String.t()}
  | {:border_color_hex, String.t()}
  | {:text_decoration_color, color_token()}
  | {:text_decoration_color_hex, String.t()}
  | {:text_decoration_style, :solid | :wavy}
  | {:text_decoration_thickness, number()}
  | {:strikethrough_color, color_token()}
  | {:strikethrough_color_hex, String.t()}
  | {:strikethrough_thickness, number()}
  | {:bg_linear_gradient,
     angle: number(), from: linear_gradient_stop(), to: linear_gradient_stop()}
  | {:bg_pattern_slash, background_pattern_options()}
  | {:box_shadow, [box_shadow_options()]}
  | {:opacity, number()}
  | {:line_clamp, pos_integer()}
  | {:grid_cols, pos_integer()}
  | {:grid_rows, pos_integer()}
  | {:col_start, integer() | :auto}
  | {:col_end, integer() | :auto}
  | {:row_start, integer() | :auto}
  | {:row_end, integer() | :auto}
  | {:col_span, pos_integer() | :full}
  | {:row_span, pos_integer() | :full}
  | {:w_px, number()}
  | {:w_rem, number()}
  | {:w_frac, number()}
  | {:h_px, number()}
  | {:h_rem, number()}
  | {:h_frac, number()}
  | {:scrollbar_width_px, number()}
  | {:scrollbar_width_rem, number()}

text_events()

@type text_events() :: %{optional(:click) => String.t()}

text_input_events()

@type text_input_events() :: %{
  optional(:change) => String.t(),
  optional(:focus) => String.t(),
  optional(:blur) => String.t(),
  optional(:context_menu) => String.t()
}

text_input_node()

@type text_input_node() :: %{
  :kind => :text_input,
  :value => String.t(),
  optional(:id) => node_id(),
  optional(:placeholder) => String.t(),
  optional(:style) => style(),
  optional(:disabled) => boolean(),
  optional(:tab_index) => integer(),
  optional(:actions) => action_bindings(),
  optional(:shortcuts) => [shortcut_binding()],
  optional(:events) => text_input_events()
}

text_node()

@type text_node() :: %{
  :kind => :text,
  :content => String.t(),
  optional(:id) => node_id(),
  optional(:style) => style(),
  optional(:runs) => [text_run()],
  optional(:events) => text_events()
}

text_run()

@type text_run() :: %{:text => String.t(), optional(:style) => style()}

textarea_node()

@type textarea_node() :: %{
  :kind => :textarea,
  :value => String.t(),
  optional(:id) => node_id(),
  optional(:placeholder) => String.t(),
  optional(:style) => style(),
  optional(:disabled) => boolean(),
  optional(:tab_index) => integer(),
  optional(:actions) => action_bindings(),
  optional(:shortcuts) => [shortcut_binding()],
  optional(:events) => text_input_events()
}

tree_events()

@type tree_events() :: %{
  optional(:select) => String.t(),
  optional(:toggle) => String.t(),
  optional(:context_menu) => String.t()
}

tree_item()

@type tree_item() :: %{
  :id => node_id(),
  :label => String.t(),
  optional(:expanded) => boolean(),
  optional(:children) => [tree_item()],
  optional(:style) => style()
}

tree_node()

@type tree_node() :: %{
  :kind => :tree,
  :nodes => [tree_item()],
  optional(:id) => node_id(),
  optional(:style) => style(),
  optional(:row_style) => style(),
  optional(:selected_id) => node_id(),
  optional(:events) => tree_events()
}

uniform_list_item()

@type uniform_list_item() :: %{id: node_id(), label: String.t()}

uniform_list_node()

@type uniform_list_node() :: %{
  :kind => :uniform_list,
  :items => [uniform_list_item()],
  optional(:id) => node_id(),
  optional(:style) => style(),
  optional(:item_style) => style(),
  optional(:events) => list_events()
}

Functions

button(label, opts \\ [])

@spec button(
  String.t(),
  keyword()
) :: button_node()

canvas(commands, opts \\ [])

@spec canvas(
  [canvas_command()],
  keyword()
) :: canvas_node()

checkbox(label, checked, opts \\ [])

@spec checkbox(String.t(), boolean(), keyword()) :: checkbox_node()

data_table(columns, rows, opts \\ [])

@spec data_table([data_table_column()], [data_table_row()], keyword()) ::
  data_table_node()

div(children, opts \\ [])

@spec div(
  [ir_node()],
  keyword()
) :: div_node()

icon(source, opts \\ [])

@spec icon(
  image_source(),
  keyword()
) :: icon_node()

image(source, opts \\ [])

@spec image(
  image_source(),
  keyword()
) :: image_node()

list(items, opts \\ [])

@spec list(
  [list_item()],
  keyword()
) :: list_node()

popover(label, open, children, opts \\ [])

@spec popover(String.t(), boolean(), [ir_node()], keyword()) :: popover_node()

radio(label, value, checked, opts \\ [])

@spec radio(String.t(), String.t(), boolean(), keyword()) :: radio_node()

rich_text(runs, opts \\ [])

@spec rich_text(
  [text_run() | String.t() | {String.t(), style()}],
  keyword()
) :: text_node()

scroll(children, opts \\ [])

@spec scroll(
  [ir_node()],
  keyword()
) :: scroll_node()

select(options, opts \\ [])

@spec select(
  [select_option()],
  keyword()
) :: select_node()

spacer(opts \\ [])

@spec spacer(keyword()) :: spacer_node()

text(content, opts \\ [])

@spec text(
  String.t(),
  keyword()
) :: text_node()

text_input(value, opts \\ [])

@spec text_input(
  String.t(),
  keyword()
) :: text_input_node()

textarea(value, opts \\ [])

@spec textarea(
  String.t(),
  keyword()
) :: textarea_node()

tree(nodes, opts \\ [])

@spec tree(
  [tree_item()],
  keyword()
) :: tree_node()

uniform_list(items, opts \\ [])

@spec uniform_list(
  [uniform_list_item()],
  keyword()
) :: uniform_list_node()

unwrap(ir)

validate(ir)

@spec validate(ir_node() | Guppy.IR.Validated.t()) :: :ok | {:error, term()}

validated(validated)

@spec validated(ir_node()) :: {:ok, Guppy.IR.Validated.t()} | {:error, term()}

validated!(ir)

@spec validated!(ir_node()) :: Guppy.IR.Validated.t()