Dialogs, Forms & Virtual Books

PotatoMC introduces first-class, Paper-grade support for interactive client dialogs, Bedrock crossplay forms, and virtual book interfaces—enabling rich, interactive user experiences without client mods or resource packs.

1. Native Java 26.2+ Dialog Boxes

Minecraft Java 26.2 introduced native modal dialog boxes directly inside the client engine. PotatoMC provides a fluent builder to create and display these dialogs effortlessly:

use potato_api::dialog::{Dialog, DialogButton};
use potato_api::player::Player;

let dialog = Dialog::new("quest_dragon_slayer")
    .title("§6§lThe Ancient Dragon Quest")
    .body_text("A ferocious dragon threatens the realm! Will you embark on the journey to slay the beast?")
    .add_action_button("§a§lAccept Quest", "quest_accept")
    .add_action_button("§cDecline", "quest_decline")
    .add_url_button("§eWiki & Guides", "https://potatomc.flaxa.in/")
    .can_close_with_escape(true);

// Show dialog to player
player.show_dialog(&dialog);

// Clear dialog when done
player.clear_dialog();

Interactive Input Fields

Dialogs can collect input data directly from the user with form fields, toggles, text boxes, and sliders:

let config_dialog = Dialog::new("player_settings")
    .title("Player Preferences")
    .add_bool_input("pvp_enabled", "Enable PvP Combat", true)
    .add_text_input("nickname", "Custom Nickname", "Enter nick...", "Player")
    .add_number_range("particle_density", "Particle Multiplier", 0.5, 3.0, 1.0, 0.5)
    .add_single_option("title_color", "Preferred Color", vec!["Gold".into(), "Aqua".into(), "Red".into()], 0)
    .add_action_button("Save Changes", "save_settings");

player.show_dialog(&config_dialog);

2. Bedrock & Crossplay Forms

For cross-play networks running Bedrock Edition players (e.g., via Geyser or native bridge), PotatoMC includes native Bedrock JSON forms:

Simple Form (Buttons & Icons)

use potato_api::dialog::{SimpleForm, FormImage};

let form = SimpleForm::new("§2§lServer Selector")
    .content("Choose a server realm to join:")
    .button_with_image("Survival Realm", FormImage::url("https://example.com/survival.png"))
    .button_with_image("Creative Realm", FormImage::url("https://example.com/creative.png"))
    .button("Minigames");

player.send_simple_form(101, &form);

Modal Form (Two-Button Confirmation)

use potato_api::dialog::ModalForm;

let modal = ModalForm::new(
    "Purchase Confirmation",
    "Do you want to buy Diamond Rank for 1000 Coins?",
    "Confirm Purchase",
    "Cancel"
);

player.send_modal_form(102, &modal);

Custom Form (Complex Inputs & Dropdowns)

use potato_api::dialog::CustomForm;

let custom = CustomForm::new("Guild Creation")
    .input("Guild Tag", "3-5 characters", "POT")
    .toggle("Public Recruitment", true)
    .slider("Tax Rate (%)", 0.0, 20.0, 1.0, 5.0)
    .dropdown("Primary Language", vec!["English".into(), "Spanish".into(), "German".into()], 0);

player.send_custom_form(103, &custom);

3. Virtual Book GUI & Sign Editor

Open a written book screen directly for a player without requiring them to hold a book in their hands (equivalent to Paper's Player#openBook):

use potato_api::dialog::Book;

let rules = Book::new("Server Rules", "Admin")
    .add_page("§1§lServer Rules\n\n§01. Be respectful to others.\n2. No hacking or exploits.\n3. Keep chat family-friendly.")
    .add_page("§1§lSection 2\n\n§04. Report bugs on Discord.\n5. Have fun exploring PotatoMC!");

player.open_book(&rules);

// Open the interactive sign typing interface at any world location:
player.open_sign_editor(&location);

4. Handling Dialog & Form Events

use potato_api::event::{DialogClickActionEvent, PlayerFormResponseEvent, Cancellable};

// Java Dialog button clicks
context.register_event(|event: &mut DialogClickActionEvent| {
    if event.action_id == "quest_accept" {
        event.player.send_message("§aYou have accepted the quest!");
        event.player.clear_dialog();
    }
});

// Bedrock form responses
context.register_event(|event: &mut PlayerFormResponseEvent| {
    if event.form_id == 101 {
        event.player.send_message(&format!("Selected option: {}", event.response_json));
    }
});