use elicitation::elicit_tool;
use rmcp::ErrorData;
use rmcp::model::{CallToolResult, Content};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use tracing::instrument;
use crate::Vec2Json;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "type")]
pub enum MenuActionJson {
ContextMenu {
region_id: String,
},
ContextMenuItem {
label: String,
shortcut: Option<String>,
},
ContextMenuSeparator,
Popup {
id: String,
position: Vec2Json,
content: String,
},
PopupBelowWidget {
anchor_id: String,
content: String,
},
ClosePopup,
Tooltip {
widget_id: String,
text: String,
},
TooltipRich {
widget_id: String,
content: String,
},
TooltipAtPointer {
text: String,
},
Modal {
title: String,
content: String,
buttons: Vec<String>,
},
ConfirmDialog {
title: String,
message: String,
},
AlertDialog {
title: String,
message: String,
},
Notification {
text: String,
duration_secs: f32,
position: Option<Vec2Json>,
},
}
fn menu_result(action: &MenuActionJson) -> CallToolResult {
match serde_json::to_string(action) {
Ok(s) => CallToolResult::success(vec![Content::text(s)]),
Err(e) => CallToolResult::error(vec![Content::text(format!("serialize error: {e}"))]),
}
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct ContextMenuParams {
pub region_id: String,
}
#[elicit_tool(
plugin = "egui_menus",
name = "egui_context_menu",
description = "Add a right-click context menu to a region. Returns MenuActionJson::ContextMenu.",
emit = Auto
)]
#[instrument(skip_all)]
async fn egui_context_menu(p: ContextMenuParams) -> Result<CallToolResult, ErrorData> {
let a = MenuActionJson::ContextMenu {
region_id: p.region_id,
};
Ok(menu_result(&a))
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct ContextMenuItemParams {
pub label: String,
pub shortcut: Option<String>,
}
#[elicit_tool(
plugin = "egui_menus",
name = "egui_context_menu_item",
description = "Add an item to a context menu. Returns MenuActionJson::ContextMenuItem.",
emit = Auto
)]
#[instrument(skip_all)]
async fn egui_context_menu_item(p: ContextMenuItemParams) -> Result<CallToolResult, ErrorData> {
let a = MenuActionJson::ContextMenuItem {
label: p.label,
shortcut: p.shortcut,
};
Ok(menu_result(&a))
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct EmptyMenuParams {}
#[elicit_tool(
plugin = "egui_menus",
name = "egui_context_menu_separator",
description = "Add a separator line in a context menu. Returns MenuActionJson::ContextMenuSeparator.",
emit = Auto
)]
#[instrument(skip_all)]
async fn egui_context_menu_separator(p: EmptyMenuParams) -> Result<CallToolResult, ErrorData> {
let _ = p;
Ok(menu_result(&MenuActionJson::ContextMenuSeparator))
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct MenuPopupParams {
pub id: String,
pub position: Vec2Json,
pub content: String,
}
#[elicit_tool(
plugin = "egui_menus",
name = "egui_popup",
description = "Show a popup at a position. Returns MenuActionJson::Popup.",
emit = Auto
)]
#[instrument(skip_all)]
async fn egui_popup(p: MenuPopupParams) -> Result<CallToolResult, ErrorData> {
let a = MenuActionJson::Popup {
id: p.id,
position: p.position,
content: p.content,
};
Ok(menu_result(&a))
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct PopupBelowWidgetParams {
pub anchor_id: String,
pub content: String,
}
#[elicit_tool(
plugin = "egui_menus",
name = "egui_popup_below_widget",
description = "Show popup below a specific widget. Returns MenuActionJson::PopupBelowWidget.",
emit = Auto
)]
#[instrument(skip_all)]
async fn egui_popup_below_widget(p: PopupBelowWidgetParams) -> Result<CallToolResult, ErrorData> {
let a = MenuActionJson::PopupBelowWidget {
anchor_id: p.anchor_id,
content: p.content,
};
Ok(menu_result(&a))
}
#[elicit_tool(
plugin = "egui_menus",
name = "egui_close_popup",
description = "Close the current popup. Returns MenuActionJson::ClosePopup.",
emit = Auto,
emit = None
)]
#[instrument(skip_all)]
async fn egui_close_popup(p: EmptyMenuParams) -> Result<CallToolResult, ErrorData> {
let _ = p;
Ok(menu_result(&MenuActionJson::ClosePopup))
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct MenuTooltipParams {
pub widget_id: String,
pub text: String,
}
#[elicit_tool(
plugin = "egui_menus",
name = "egui_tooltip",
description = "Show a tooltip on hover for a widget. Returns MenuActionJson::Tooltip.",
emit = Auto
)]
#[instrument(skip_all)]
async fn egui_tooltip(p: MenuTooltipParams) -> Result<CallToolResult, ErrorData> {
let a = MenuActionJson::Tooltip {
widget_id: p.widget_id,
text: p.text,
};
Ok(menu_result(&a))
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct TooltipRichParams {
pub widget_id: String,
pub content: String,
}
#[elicit_tool(
plugin = "egui_menus",
name = "egui_tooltip_rich",
description = "Show a rich tooltip with custom UI content. Returns MenuActionJson::TooltipRich.",
emit = Auto
)]
#[instrument(skip_all)]
async fn egui_tooltip_rich(p: TooltipRichParams) -> Result<CallToolResult, ErrorData> {
let a = MenuActionJson::TooltipRich {
widget_id: p.widget_id,
content: p.content,
};
Ok(menu_result(&a))
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct TooltipAtPointerParams {
pub text: String,
}
#[elicit_tool(
plugin = "egui_menus",
name = "egui_tooltip_at_pointer",
description = "Show tooltip at the mouse pointer position. Returns MenuActionJson::TooltipAtPointer.",
emit = Auto
)]
#[instrument(skip_all)]
async fn egui_tooltip_at_pointer(p: TooltipAtPointerParams) -> Result<CallToolResult, ErrorData> {
let a = MenuActionJson::TooltipAtPointer { text: p.text };
Ok(menu_result(&a))
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct ModalParams {
pub title: String,
pub content: String,
pub buttons: Vec<String>,
}
#[elicit_tool(
plugin = "egui_menus",
name = "egui_modal",
description = "Show a modal dialog with title, content, and buttons. Returns MenuActionJson::Modal.",
emit = Auto
)]
#[instrument(skip_all)]
async fn egui_modal(p: ModalParams) -> Result<CallToolResult, ErrorData> {
let a = MenuActionJson::Modal {
title: p.title,
content: p.content,
buttons: p.buttons,
};
Ok(menu_result(&a))
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct ConfirmDialogParams {
pub title: String,
pub message: String,
}
#[elicit_tool(
plugin = "egui_menus",
name = "egui_confirm_dialog",
description = "Show a yes/no confirmation dialog. Returns MenuActionJson::ConfirmDialog.",
emit = Auto
)]
#[instrument(skip_all)]
async fn egui_confirm_dialog(p: ConfirmDialogParams) -> Result<CallToolResult, ErrorData> {
let a = MenuActionJson::ConfirmDialog {
title: p.title,
message: p.message,
};
Ok(menu_result(&a))
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct AlertDialogParams {
pub title: String,
pub message: String,
}
#[elicit_tool(
plugin = "egui_menus",
name = "egui_alert_dialog",
description = "Show an alert/info dialog with single OK button. Returns MenuActionJson::AlertDialog.",
emit = Auto
)]
#[instrument(skip_all)]
async fn egui_alert_dialog(p: AlertDialogParams) -> Result<CallToolResult, ErrorData> {
let a = MenuActionJson::AlertDialog {
title: p.title,
message: p.message,
};
Ok(menu_result(&a))
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct NotificationParams {
pub text: String,
pub duration_secs: f32,
pub position: Option<Vec2Json>,
}
#[elicit_tool(
plugin = "egui_menus",
name = "egui_notification",
description = "Show a temporary notification/toast message. Returns MenuActionJson::Notification.",
emit = Auto
)]
#[instrument(skip_all)]
async fn egui_notification(p: NotificationParams) -> Result<CallToolResult, ErrorData> {
let a = MenuActionJson::Notification {
text: p.text,
duration_secs: p.duration_secs,
position: p.position,
};
Ok(menu_result(&a))
}