Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions packages/unified-ui/lib/unified_ui/reference.ex
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,6 @@ defmodule UnifiedUi.Reference do
WidgetComponents.catalog()
end

@spec widget_component_source_mapping() :: %{pos_integer() => map()}
def widget_component_source_mapping do
WidgetComponents.source_mapping()
end

@spec widget_component_aliases() :: %{atom() => atom()}
def widget_component_aliases do
WidgetComponents.aliases()
Expand Down
3 changes: 1 addition & 2 deletions packages/unified-ui/lib/unified_ui/tooling.ex
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,7 @@ defmodule UnifiedUi.Tooling do
%{
families: WidgetComponents.component_families(),
components: WidgetComponents.catalog(),
aliases: WidgetComponents.aliases(),
source_mapping: WidgetComponents.source_mapping()
aliases: WidgetComponents.aliases()
}
end

Expand Down
51 changes: 6 additions & 45 deletions packages/unified-ui/lib/unified_ui/widget_components.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ defmodule UnifiedUi.WidgetComponents do
@moduledoc """
Reference catalog for the canonical widget-component expansion.

The catalog records the AshUi PR 79-98 source mapping, portable canonical
names, semantic families, and compatibility aliases before the DSL entities
are implemented.
The catalog records portable canonical names, semantic families, and
compatibility aliases for widget kinds available in UnifiedUi.

Schema is intentionally loose: a catalog entry needs only kind, family,
summary, and aliases. Provenance and PR tracking live in git history,
not in the runtime catalog (per Pascal Discord 2026-05-16).
"""

@type family ::
Expand All @@ -19,17 +22,10 @@ defmodule UnifiedUi.WidgetComponents do
@type component :: %{
required(:kind) => atom(),
required(:family) => family(),
required(:source) => source(),
required(:summary) => String.t(),
required(:aliases) => [atom()]
}

@type source :: %{
required(:system) => :ash_ui,
required(:pr) => pos_integer(),
required(:name) => atom()
}

@type name_diagnostic :: %{
required(:status) => :canonical | :alias | :unknown,
required(:name) => atom() | String.t(),
Expand All @@ -52,141 +48,121 @@ defmodule UnifiedUi.WidgetComponents do
%{
kind: :inline_rich_text_heading,
family: :content_identity_and_disclosure,
source: %{system: :ash_ui, pr: 79, name: :inline_rich_text_heading},
summary: "Semantic heading with inline text and emphasis segments.",
aliases: []
},
%{
kind: :disclosure,
family: :content_identity_and_disclosure,
source: %{system: :ash_ui, pr: 80, name: :disclosure},
summary: "Progressive disclosure region with summary, open state, and body children.",
aliases: []
},
%{
kind: :runtime_form_shell,
family: :form_control_and_composer,
source: %{system: :ash_ui, pr: 81, name: :phoenix_form},
summary: "Portable form shell whose runtime-owned form state is supplied by the host.",
aliases: [:phoenix_form]
},
%{
kind: :kicker,
family: :content_identity_and_disclosure,
source: %{system: :ash_ui, pr: 82, name: :kicker},
summary: "Eyebrow label cluster with ordered items and separator behavior.",
aliases: []
},
%{
kind: :avatar,
family: :content_identity_and_disclosure,
source: %{system: :ash_ui, pr: 83, name: :avatar},
summary: "Identity display using initials or image source with size, shape, and variant.",
aliases: []
},
%{
kind: :presence_dot,
family: :content_identity_and_disclosure,
source: %{system: :ash_ui, pr: 84, name: :presence_dot},
summary: "Small state-driven status indicator with size and accessibility metadata.",
aliases: []
},
%{
kind: :segmented_button_group,
family: :form_control_and_composer,
source: %{system: :ash_ui, pr: 85, name: :segmented_button_group},
summary: "Single-selection segmented control with option values and labels.",
aliases: []
},
%{
kind: :list_item_multi_column,
family: :row_and_artifact,
source: %{system: :ash_ui, pr: 86, name: :list_item_multi_column},
summary: "Selectable or linkable multi-column row with child cell content.",
aliases: []
},
%{
kind: :artifact_row,
family: :row_and_artifact,
source: %{system: :ash_ui, pr: 87, name: :artifact_row},
summary: "Artifact row with title, meta text, row identity, and trailing content.",
aliases: []
},
%{
kind: :sticky_frosted_header,
family: :layer_shell_and_callout,
source: %{system: :ash_ui, pr: 88, name: :sticky_frosted_header},
summary: "Sticky shell header with leading, title, and trailing child positions.",
aliases: []
},
%{
kind: :pipeline_stepper_horizontal,
family: :workflow_progress_and_status,
source: %{system: :ash_ui, pr: 89, name: :pipeline_stepper_horizontal},
summary: "Horizontal workflow stepper with done, active, and pending step state.",
aliases: []
},
%{
kind: :segmented_progress_bar,
family: :workflow_progress_and_status,
source: %{system: :ash_ui, pr: 90, name: :segmented_progress_bar},
summary: "Weighted multi-segment progress bar with per-segment state.",
aliases: []
},
%{
kind: :workflow_stage_list_vertical,
family: :workflow_progress_and_status,
source: %{system: :ash_ui, pr: 91, name: :workflow_stage_list_vertical},
summary: "Vertical numbered workflow stage list with connector state.",
aliases: []
},
%{
kind: :meter_thin,
family: :workflow_progress_and_status,
source: %{system: :ash_ui, pr: 92, name: :meter_thin},
summary: "Compact progress meter with normalized value and optional label.",
aliases: []
},
%{
kind: :slide_over_panel,
family: :layer_shell_and_callout,
source: %{system: :ash_ui, pr: 93, name: :slide_over_panel},
summary: "Non-modal contextual side panel with open state, size, and children.",
aliases: []
},
%{
kind: :event_callout,
family: :layer_shell_and_callout,
source: %{system: :ash_ui, pr: 94, name: :event_callout},
summary: "Inline stream or status callout with tone, eyebrow, body, and action content.",
aliases: []
},
%{
kind: :redline_inline,
family: :redline_and_code,
source: %{system: :ash_ui, pr: 95, name: :redline_inline},
summary:
"Inline redline text display with keep, insert, delete, accepted, and rejected state.",
aliases: []
},
%{
kind: :code_block_syntax_highlighted,
family: :redline_and_code,
source: %{system: :ash_ui, pr: 96, name: :code_block_syntax_highlighted},
summary: "Pre-tokenized code block display with language and semantic token types.",
aliases: []
},
%{
kind: :chat_composer,
family: :form_control_and_composer,
source: %{system: :ash_ui, pr: 97, name: :chat_composer},
summary: "Multi-line composer with tool area, send intent, and change intent.",
aliases: []
},
%{
kind: :list_repeat,
family: :composition_behavior,
source: %{system: :ash_ui, pr: 98, name: :ui_relationship_repeat},
summary:
"Composition behavior that repeats a child template over rows from a list binding.",
aliases: [:ui_relationship_repeat, :repeat]
Expand Down Expand Up @@ -329,21 +305,6 @@ defmodule UnifiedUi.WidgetComponents do
Enum.filter(@components, &(&1.family == family))
end

@spec source_mapping() :: %{pos_integer() => map()}
def source_mapping do
Map.new(@components, fn component ->
source = component.source

{source.pr,
%{
source_name: source.name,
canonical_kind: component.kind,
family: component.family,
aliases: component.aliases
}}
end)
end

defp normalize_name(name) when is_atom(name), do: name

defp normalize_name(name) when is_binary(name) do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,6 @@ defmodule UnifiedUi.WidgetComponentsCatalogTest do
}
end

test "source mapping records every AshUi PR in order" do
source_mapping = WidgetComponents.source_mapping()

assert Map.keys(source_mapping) == Enum.to_list(79..98)
assert source_mapping[79].canonical_kind == :inline_rich_text_heading
assert source_mapping[81].source_name == :phoenix_form
assert source_mapping[81].canonical_kind == :runtime_form_shell
assert source_mapping[98].source_name == :ui_relationship_repeat
assert source_mapping[98].canonical_kind == :list_repeat
end

test "canonical name lookup accepts AshUi aliases with diagnostics" do
assert WidgetComponents.canonical_kind(:runtime_form_shell) == {:ok, :runtime_form_shell}
assert WidgetComponents.canonical_kind("runtime_form_shell") == {:ok, :runtime_form_shell}
Expand Down Expand Up @@ -94,13 +83,9 @@ defmodule UnifiedUi.WidgetComponentsCatalogTest do
assert UnifiedUi.Reference.widget_component_families() ==
WidgetComponents.component_families()

assert UnifiedUi.Reference.widget_component_source_mapping()[98].canonical_kind ==
:list_repeat

tooling_catalog = UnifiedUi.Tooling.widget_component_catalog()

assert tooling_catalog.families == WidgetComponents.component_families()
assert tooling_catalog.aliases == WidgetComponents.aliases()
assert tooling_catalog.source_mapping[81].canonical_kind == :runtime_form_shell
end
end
Loading