use elicitation::elicit_tool;
use rmcp::ErrorData;
use rmcp::model::{CallToolResult, Content};
use schemars::JsonSchema;
use serde::Deserialize;
use tracing::instrument;
use crate::serde_types::{ColorJson, RangeJson, StrokeJson, WidgetJson};
#[derive(Debug, Deserialize, JsonSchema)]
pub struct EmptyParams {}
fn widget_result(widget: &WidgetJson) -> CallToolResult {
match serde_json::to_string(widget) {
Ok(s) => CallToolResult::success(vec![Content::text(s)]),
Err(e) => CallToolResult::error(vec![Content::text(format!("serialize error: {e}"))]),
}
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct LabelParams {
pub text: String,
#[serde(default)]
pub wrap: bool,
pub color: Option<ColorJson>,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_label",
description = "Create a plain text label. Returns WidgetJson::Label."
)]
#[instrument(skip_all)]
async fn widget_label(p: LabelParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::Label {
text: p.text,
wrap: p.wrap,
color: p.color,
};
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct HeadingParams {
pub text: String,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_heading",
description = "Create a heading (large bold text). Returns WidgetJson::Heading."
)]
#[instrument(skip_all)]
async fn widget_heading(p: HeadingParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::Heading { text: p.text };
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct MonospaceParams {
pub text: String,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_monospace",
description = "Create monospace (fixed-width) text. Returns WidgetJson::Monospace."
)]
#[instrument(skip_all)]
async fn widget_monospace(p: MonospaceParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::Monospace { text: p.text };
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct CodeParams {
pub text: String,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_code",
description = "Create code text (monospace with background). Returns WidgetJson::Code."
)]
#[instrument(skip_all)]
async fn widget_code(p: CodeParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::Code { text: p.text };
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct SimpleTextParams {
pub text: String,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_small",
description = "Create small text. Returns WidgetJson::Small."
)]
#[instrument(skip_all)]
async fn widget_small(p: SimpleTextParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::Small { text: p.text };
Ok(widget_result(&w))
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_strong",
description = "Create strong (bold) text. Returns WidgetJson::Strong.",
emit = None
)]
#[instrument(skip_all)]
async fn widget_strong(p: SimpleTextParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::Strong { text: p.text };
Ok(widget_result(&w))
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_weak",
description = "Create weak (faint) text. Returns WidgetJson::Weak.",
emit = None
)]
#[instrument(skip_all)]
async fn widget_weak(p: SimpleTextParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::Weak { text: p.text };
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ColoredLabelParams {
pub text: String,
pub color: ColorJson,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_colored_label",
description = "Create a coloured text label. Returns WidgetJson::ColoredLabel."
)]
#[instrument(skip_all)]
async fn widget_colored_label(p: ColoredLabelParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::ColoredLabel {
text: p.text,
color: p.color,
};
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ButtonParams {
pub text: String,
#[serde(default)]
pub wrap: bool,
pub fill: Option<ColorJson>,
pub stroke: Option<StrokeJson>,
#[serde(default)]
pub selected: bool,
#[serde(default = "default_true")]
pub frame: bool,
pub min_size: Option<crate::serde_types::Vec2Json>,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_button",
description = "Create a clickable button. Returns WidgetJson::Button."
)]
#[instrument(skip_all)]
async fn widget_button(p: ButtonParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::Button {
text: p.text,
wrap: p.wrap,
fill: p.fill,
stroke: p.stroke,
selected: p.selected,
frame: p.frame,
min_size: p.min_size,
};
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct SmallButtonParams {
pub text: String,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_small_button",
description = "Create a small button with less padding. Returns WidgetJson::SmallButton."
)]
#[instrument(skip_all)]
async fn widget_small_button(p: SmallButtonParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::SmallButton { text: p.text };
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct CheckboxParams {
pub text: String,
#[serde(default)]
pub checked: bool,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_checkbox",
description = "Create a boolean checkbox. Returns WidgetJson::Checkbox."
)]
#[instrument(skip_all)]
async fn widget_checkbox(p: CheckboxParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::Checkbox {
text: p.text,
checked: p.checked,
};
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct RadioValueParams {
pub text: String,
#[serde(default)]
pub selected: bool,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_radio_value",
description = "Create a radio button (one of a group). Returns WidgetJson::RadioValue."
)]
#[instrument(skip_all)]
async fn widget_radio_value(p: RadioValueParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::RadioValue {
text: p.text,
selected: p.selected,
};
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct SelectableLabelParams {
pub text: String,
#[serde(default)]
pub selected: bool,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_selectable_label",
description = "Create a selectable label (click to toggle). Returns WidgetJson::SelectableLabel."
)]
#[instrument(skip_all)]
async fn widget_selectable_label(p: SelectableLabelParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::SelectableLabel {
text: p.text,
selected: p.selected,
};
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct HyperlinkParams {
pub text: String,
pub url: String,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_hyperlink",
description = "Create a hyperlink that opens a URL. Returns WidgetJson::Hyperlink."
)]
#[instrument(skip_all)]
async fn widget_hyperlink(p: HyperlinkParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::Hyperlink {
text: p.text,
url: p.url,
};
Ok(widget_result(&w))
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_separator",
description = "Create a separator line. Returns WidgetJson::Separator."
)]
#[instrument(skip_all)]
async fn widget_separator(p: EmptyParams) -> Result<CallToolResult, ErrorData> {
let _ = p;
Ok(widget_result(&WidgetJson::Separator))
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_spinner",
description = "Create a loading spinner animation. Returns WidgetJson::Spinner.",
emit = None
)]
#[instrument(skip_all)]
async fn widget_spinner(p: EmptyParams) -> Result<CallToolResult, ErrorData> {
let _ = p;
Ok(widget_result(&WidgetJson::Spinner))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct TextEditSinglelineParams {
pub text: String,
pub hint: Option<String>,
#[serde(default = "default_true")]
pub interactive: bool,
}
fn default_true() -> bool {
true
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_text_edit_singleline",
description = "Create a single-line text input. Returns WidgetJson::TextEditSingleline."
)]
#[instrument(skip_all)]
async fn widget_text_edit_singleline(
p: TextEditSinglelineParams,
) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::TextEditSingleline {
text: p.text,
hint: p.hint,
interactive: p.interactive,
};
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct TextEditMultilineParams {
pub text: String,
pub hint: Option<String>,
#[serde(default = "default_true")]
pub interactive: bool,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_text_edit_multiline",
description = "Create a multi-line text input. Returns WidgetJson::TextEditMultiline."
)]
#[instrument(skip_all)]
async fn widget_text_edit_multiline(
p: TextEditMultilineParams,
) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::TextEditMultiline {
text: p.text,
hint: p.hint,
interactive: p.interactive,
};
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct CodeEditorParams {
pub text: String,
pub language: Option<String>,
#[serde(default = "default_true")]
pub interactive: bool,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_code_editor",
description = "Create a code editor (monospace, tab support). Returns WidgetJson::CodeEditor."
)]
#[instrument(skip_all)]
async fn widget_code_editor(p: CodeEditorParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::CodeEditor {
text: p.text,
language: p.language,
interactive: p.interactive,
};
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct SliderParams {
pub value: f64,
pub min: f64,
pub max: f64,
pub step: Option<f64>,
pub text: Option<String>,
pub prefix: Option<String>,
pub suffix: Option<String>,
#[serde(default)]
pub logarithmic: bool,
#[serde(default = "default_true")]
pub clamping: bool,
#[serde(default = "default_true")]
pub show_value: bool,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_slider",
description = "Create a numeric slider. Returns WidgetJson::Slider."
)]
#[instrument(skip_all)]
async fn widget_slider(p: SliderParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::Slider {
value: p.value,
range: RangeJson {
min: p.min,
max: p.max,
},
step: p.step,
text: p.text,
prefix: p.prefix,
suffix: p.suffix,
logarithmic: p.logarithmic,
clamping: p.clamping,
show_value: p.show_value,
};
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct DragValueParams {
pub value: f64,
pub range: Option<RangeJson>,
pub speed: Option<f64>,
pub prefix: Option<String>,
pub suffix: Option<String>,
pub min_decimals: Option<usize>,
pub max_decimals: Option<usize>,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_drag_value",
description = "Create a drag-to-edit numeric value. Returns WidgetJson::DragValue."
)]
#[instrument(skip_all)]
async fn widget_drag_value(p: DragValueParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::DragValue {
value: p.value,
range: p.range,
speed: p.speed,
prefix: p.prefix,
suffix: p.suffix,
min_decimals: p.min_decimals,
max_decimals: p.max_decimals,
};
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ProgressBarParams {
pub progress: f32,
pub text: Option<String>,
#[serde(default)]
pub animate: bool,
pub fill: Option<ColorJson>,
pub desired_width: Option<f32>,
pub corner_radius: Option<crate::serde_types::CornerRadiusJson>,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_progress_bar",
description = "Create a progress bar (0.0–1.0). Returns WidgetJson::ProgressBar."
)]
#[instrument(skip_all)]
async fn widget_progress_bar(p: ProgressBarParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::ProgressBar {
progress: p.progress,
text: p.text,
animate: p.animate,
fill: p.fill,
desired_width: p.desired_width,
corner_radius: p.corner_radius,
};
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ImageParams {
pub uri: String,
pub size: Option<crate::serde_types::Vec2Json>,
#[serde(default = "default_true")]
pub maintain_aspect_ratio: bool,
pub tint: Option<ColorJson>,
pub corner_radius: Option<crate::serde_types::CornerRadiusJson>,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_image",
description = "Display an image by URI. Returns WidgetJson::Image."
)]
#[instrument(skip_all)]
async fn widget_image(p: ImageParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::Image {
uri: p.uri,
size: p.size,
maintain_aspect_ratio: p.maintain_aspect_ratio,
tint: p.tint,
corner_radius: p.corner_radius,
};
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct LinkParams {
pub text: String,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_link",
description = "Create a clickable text link (no auto-navigation). Returns WidgetJson::Link."
)]
#[instrument(skip_all)]
async fn widget_link(p: LinkParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::Link { text: p.text };
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ToggleValueParams {
pub text: String,
#[serde(default)]
pub selected: bool,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_toggle_value",
description = "Create a boolean toggle (simpler than checkbox). Returns WidgetJson::ToggleValue."
)]
#[instrument(skip_all)]
async fn widget_toggle_value(p: ToggleValueParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::ToggleValue {
text: p.text,
selected: p.selected,
};
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct RadioParams {
pub text: String,
#[serde(default)]
pub selected: bool,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_radio",
description = "Create a simple radio button (display state, no auto-update). Returns WidgetJson::Radio."
)]
#[instrument(skip_all)]
async fn widget_radio(p: RadioParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::Radio {
text: p.text,
selected: p.selected,
};
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct DragAngleParams {
pub radians: f64,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_drag_angle",
description = "Create a drag-to-edit angle (displayed in degrees, stored as radians). Returns WidgetJson::DragAngle."
)]
#[instrument(skip_all)]
async fn widget_drag_angle(p: DragAngleParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::DragAngle { radians: p.radians };
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct DragAngleTauParams {
pub radians: f64,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_drag_angle_tau",
description = "Create a drag-to-edit angle (displayed as fraction of tau). Returns WidgetJson::DragAngleTau."
)]
#[instrument(skip_all)]
async fn widget_drag_angle_tau(p: DragAngleTauParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::DragAngleTau { radians: p.radians };
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ColorEditButtonSrgbaParams {
pub color: ColorJson,
#[serde(default = "default_true")]
pub alpha: bool,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_color_edit_button_srgba",
description = "Create an sRGBA colour picker button. Returns WidgetJson::ColorEditButtonSrgba."
)]
#[instrument(skip_all)]
async fn widget_color_edit_button_srgba(
p: ColorEditButtonSrgbaParams,
) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::ColorEditButtonSrgba {
color: p.color,
alpha: p.alpha,
};
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ColorEditButtonHsvaParams {
pub color: ColorJson,
#[serde(default = "default_true")]
pub alpha: bool,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_color_edit_button_hsva",
description = "Create an HSVA colour picker button. Returns WidgetJson::ColorEditButtonHsva."
)]
#[instrument(skip_all)]
async fn widget_color_edit_button_hsva(
p: ColorEditButtonHsvaParams,
) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::ColorEditButtonHsva {
color: p.color,
alpha: p.alpha,
};
Ok(widget_result(&w))
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct SliderVerticalParams {
pub value: f64,
pub min: f64,
pub max: f64,
pub step: Option<f64>,
pub text: Option<String>,
pub suffix: Option<String>,
#[serde(default)]
pub logarithmic: bool,
}
#[elicit_tool(
plugin = "egui_widgets",
name = "widget_slider_vertical",
description = "Create a vertical numeric slider. Returns WidgetJson::SliderVertical."
)]
#[instrument(skip_all)]
async fn widget_slider_vertical(p: SliderVerticalParams) -> Result<CallToolResult, ErrorData> {
let w = WidgetJson::SliderVertical {
value: p.value,
range: RangeJson {
min: p.min,
max: p.max,
},
step: p.step,
text: p.text,
suffix: p.suffix,
logarithmic: p.logarithmic,
};
Ok(widget_result(&w))
}