Home›Java›Mods›Vista Dialogs
Vista Dialogs
ModsJava

Vista Dialogs

Java mod listed for Minecraft 1.21–26.x. Downloads from the MC Java Mods app on Android.

by Pizzalopa · on Modrinth

Menus built on Minecraft's native dialog screens instead of chest GUIs, you can also create a dialog that shows on the escape menu, easy to configure.

⬇ Download on Modrinth
Vista Dialogs — screenshot 1Vista Dialogs — screenshot 2Vista Dialogs — screenshot 3Vista Dialogs — screenshot 4Vista Dialogs — screenshot 5

Vista

Server menus built on Minecraft's own dialog screens

No chest inventories. No filler panes. No item names pretending to be labels. One YAML file per screen, rendered for each player who opens it.

Random Teleport menu

Why this one?

With other dialog plugins a button runs one command, and that is the whole feature. Vista adds everything a real menu needs, put price: 1000 on a button and it is a shop that checks their balance, confirms, takes the money and if they're short, it tells them how short they are; buttons can be hidden from, the wrong ranks, locked behind a level, or marked Owned once bought; players get on/off settings, and player lookup; and the G key and the Escape-menu button work from the first start, with no datapack and no second restart.


Things to build with it

A server hub. The G key opens it, every feature is one press away, and staff-only entries appear with a show-if on a permission.

Server Selector Dialog


A profile. One file, lookup: true, and a search box for anyone else's numbers.

Profile Dialog


Leaderboards. Body lines of placeholders with <head:%top_1_name%> drawn beside each one.

Leaderboard Dialog


Kits, warps, crates. A list screen, one button each, click-if for the requirement and deny for the reason.

Kits Dialog


Rules and guides. Body text, a button per section, no plugin dependency at all.

Rules Dialog


Settings. type: toggle buttons that flip on and off in place, remember themselves per player, and run a command on each side.

Settings Dialog

The possibilities are nearly endless. If you can imagine a menu, Vista gives you the tools to build it.

The G keybind and the pause button, from first start

Vista automatically adds its G key Quick Actions and Escape menu integration. Just drop the plugin in, start your server, and it works. No datapacks, reloads, or extra setup required.

quick-actions:
  enabled: true
  dialog: menu
  label: 'Menu'

pause-screen:
  enabled: true
  dialog: menu
  label: 'Menu'

Point both at the same dialog and editing that one file changes both.

Escape Menu button


Wiki

The dialog format

Every key is optional except title and buttons.

Screen

title: 'Crate Keys'          # window title. Placeholders work.
external-title: 'Keys'       # the label when opened from the pause menu
command: 'keys'              # registers /keys
aliases: ['k', 'crates']     # extra names for it
overwrite: true              # take /keys even if another plugin has it
permission: 'server.keys'    # required to open at all
back: 'shop'                 # where Back goes when there is no history
columns: 2                   # buttons per row
escape: true                 # can Esc close it
close-button: 'Close'        # bottom-right button that closes; omit for none
back-button: 'Back'          # or one that goes back; omit for none
currency: 'coins'            # what price: is counted in
prefix: '<#3A90E8>SHOP ▶ '   # chat prefix for messages this screen causes
lookup: false                # resolve placeholders for a searched player
open-if:                     # conditions checked before it opens
  - '%player_level% >= 5'
Key Meaning
title Window title. Placeholders resolve per viewer
external-title Label used when this screen is the pause-menu button
command Registers /thatname, live — no restart
aliases Extra names for that command
overwrite Whether command and aliases take a name another plugin already uses. Default true
permission Node required to open the screen
back Dialog the Back button falls back to
columns Buttons per row. 1 is a tall list, 2 suits a hub
escape Whether Esc closes it. Default true
close-button Text of a bottom-right button that closes. Omit it for none
back-button Text of a bottom-right button that goes back. Omit it for none
currency Which currency price: means here
prefix Chat prefix for messages this screen causes
lookup Resolve placeholders for a searched player instead of the viewer
open-if Conditions, all of which must pass

Body

Lines of text above the buttons. An empty string is a blank line.

body:
  - '<white>Keys go straight into your inventory.'
  - '<gray>Choose one below</gray>'
  - ''
  - '<gray>Balance:</gray> <white>%economy_balance%</white>'

Buttons

Each key under buttons: names one button. The name is yours and shows up in /vista list.

buttons:
  vote-key:
    label: 'Vote Key'
    tooltip:
      - '<#3A90E8>Vote Key'
      - ''
      - '<white>Opens the Vote Crate.'
    width: 150
    slot: 1
    priority: 0
    permission: 'server.keys.vote'
    show-if:
      - '%player_level% >= 10'
    click-if:
      - '%player_world% == survival'
    deny:
      - '[message] <red>Only in the survival world.'
    price: 100
    owned: 'server.perk.fly'
    url: 'https://example.com'
    run:
      - '[console] crates give %player_name% vote %amount%'
Key Meaning
label The button text
tooltip A list of lines, or one string using <br>
width Pixels, 1–1024. Default 150
slot Draw order. Buttons sharing one are resolved by priority
priority Lower wins the slot. Default 0
permission Node required to see and press it
show-if Conditions; failing hides the button
click-if Conditions; failing blocks the press and runs deny
deny Actions run when click-if fails
price Cost in the screen's currency
amounts Quantity-to-price map, for anything sold in sizes
owned Permission meaning "already bought"
url Opens a link instead of running anything
with-permission Permissions the player holds only while this button's actions run
type: toggle Makes it a switch over a setting named after the button
for-each: items Repeats the button once per item in the game — see Item lists
search With for-each, the input whose text narrows the list
run The actions, in order

Inputs

Text fields, drawn above the buttons and written like buttons: a name, then what it is. A button reads one back as $(name).

inputs:
  player:
    label: 'Look up a player'
    max-length: 16
    initial: '%player_name%'
    width: 200
  search:
    label: 'Search'
    run:
      - '[player] find $(player)'

$(name) works in conditions too, so a click-if can check what was typed before anything runs, and in an input's own initial: — initial: '$(search)' keeps the text in the box when the screen redraws. Opening a screen starts its inputs empty.

What was typed can be tidied on the way in:

Written " My Home " becomes
$(name|trim) My Home — outer and doubled spaces dropped
$(name|lower) / $(name|upper) my home / MY HOME
$(name|slug) my_home — one safe word, for ids and commands

They chain, left to right: $(name|trim|upper).


Actions

run: is a list, carried out in order. A chain stops at the first step that refuses.

Action Does
[player] <command> Runs the command as the player
[console] <command> Runs it as the console
[message] <text> Sends the player a message
[broadcast] <text> Sends it to everyone
[log] <text> Writes a console line
[sound] <name> [volume] [pitch] Plays a sound
[open] <dialog> Opens another dialog
[server] <name> Sends the player to that server through the proxy
[back] Goes back one screen
[close] Closes the screen
[refresh] Redraws it in place
[lookup] <player> Makes the next screen describe that player
[set] <name> <value> Remembers text for this player — see Variables
[unset] <name> Forgets it
[givepermission] <node> Grants a permission
[takepermission] <node> Removes one

An untagged line is treated as [message].

A step can also be a choice, made when the chain reaches it:

    run:
      - if: '$(name|slug) != %vista_var_home%'
        run:
          - '[player] essentials:renamehome %vista_var_home% $(name|slug)'
        else:
          - '[message] Same home, new spelling.'
      - '[open] homes-manage'

if: takes any condition line, or a list of them. else: is optional.

[server] is how a server selector works. /server belongs to the proxy, not to the server the player is standing on, so running it as them does nothing; [server] survival asks the proxy directly over the BungeeCord plugin channel, which Velocity answers as well. Nothing to install or configure on either side.

There is no click-sound setting, on purpose: the client plays its own click when a dialog button is pressed, so a second from the server doubles it up. A button that wants a particular noise asks with [sound].


Conditions

show-if hides a button. click-if lets it be seen but blocks the press. Both are lists, and every line must pass. A leading ! inverts one.

    show-if:
      - '%player_level% >= 30'
      - '%player_world% == survival'
      - 'has permission server.vip'
      - '!has permission server.banned'
      - 'has any permission server.vip server.mvp'
      - '%player_prefix% contains Gold'
      - '$(name) matches [A-Za-z0-9_]{1,16}'
Operator Compares
> >= < <= Numbers
== != Numbers when both sides are numeric, text otherwise
equals equalsignorecase Text
contains Text
matches A regular expression
has permission <node> One permission
has any permission <a> <b> Any of several
has setting <name> One of the player's own on/off settings

|| joins lines of which any one is enough:

      - '%player_level% >= 30 || has permission server.vip'

A click-if line can carry its own message. Only the first line that fails is reported, so the player is told exactly what is wrong; lines without one fall back to the button's deny:.

    click-if:
      - if: '$(name|trim) != '
        deny: '[message] <red>Type a name first.'
      - if: '$(name|trim) matches [A-Za-z0-9_ ]{1,16}'
        deny: '[message] <red>Letters, numbers, spaces and _ only.'
      - '%player_level% >= 5'
    deny:
      - '[message] <red>You need level 5.'

A line that cannot be read is dropped and logged, so one typo cannot break a screen.

One slot, several buttons

Buttons sharing a slot: are checked in ascending priority:, and the first whose show-if passes is the one drawn. That is how a single screen shows an entry as claimed, current or available without three separate files.

  rank-claimed:
    label: 'Rank 2'
    slot: 1
    priority: 1
    show-if: ['%player_rank% > 2']
    run: ['[message] <gray>Already claimed.']

  rank-current:
    label: 'Rank 2'
    slot: 1
    priority: 2
    show-if: ['%player_rank% == 2']
    run: ['[message] <gray>This is your rank.']

  rank-available:
    label: 'Rank 2'
    slot: 1
    priority: 3
    run: ['[console] rank set %player_name% 2']

Settings and switches

A button with type: toggle is a switch. Pressing it flips the setting, runs the matching side at once, and the screen redraws where it stands: a screen holding a switch is sent as one that stays open, so the press closes nothing and nothing is reopened.

buttons:
  pvp:
    type: 'toggle'
    label: 'PvP'
    default: true
    when-on:
      - '[console] pvp enable %player_name%'
    when-off:
      - '[console] pvp disable %player_name%'
Key Meaning
type: toggle Makes the button a switch
label What it is called. Drawn as PvP: On
default What the setting is before the player has ever pressed it
setting The setting it switches. Defaults to the button's own name
state-if Reads the state from a condition instead of remembering it
when-on / when-off Actions run the moment it lands on that side

The button's name is the setting, remembered per player in players.yml. Naming it creates it; there is nothing to declare anywhere else.

PvP: On is toggle-label in lang.yml, and the two words are state-on and state-off, so every switch on the server is reworded in one place. A label that writes <state> itself is used as written.

Reading a setting elsewhere

Where How
Any text %vista_setting_pvp% — prints On or Off
Any condition has setting pvp, or !has setting pvp

A setting another plugin owns

state-if: reads the state from a condition rather than remembering it, so the switch and the plugin behind it can never disagree.

  fly:
    type: 'toggle'
    label: 'Flight'
    state-if:
      - '%player_is_flying% == true'
    when-on:
      - '[player] fly on'
    when-off:
      - '[player] fly off'

Nothing is stored for one of these; the condition answers every time the screen is drawn.


Variables

On/off is not always enough. [set] remembers any text for the player who pressed the button, and %vista_var_<name>% reads it back anywhere — a label, a body line, a condition, another command.

  pick-base:
    label: 'Base'
    run:
      - '[set] home base'
      - '[open] home-screen'
# home-screen.yml
title: '%vista_var_home%'
buttons:
  teleport:
    label: 'Teleport'
    run:
      - '[player] home %vista_var_home%'

That is how one screen serves many things: the button that opens it says which one. Variables are kept per player in players.yml, survive restarts, and read as nothing until they are set. [unset] home forgets one.

Defaults

A dialog can say what a variable reads before anything has been set. A * covers every name that starts with the rest:

variables:
  homeicon_*: '<sprite:block/red_wool>'
  homename_*: '*'

Every home now shows red wool until the player picks something else. A * in the value is whatever the name's * matched, so %vista_var_homename_base% reads base until a nicer name is set. Defaults apply everywhere, whichever dialog declares them, so a set of screens that belong together can carry their own.

A placeholder inside a placeholder

Write the inner one in braces and it is filled in first:

  - '%vista_var_icon_{vista_var_home}%'

With home set to base, that reads %vista_var_icon_base%. Any placeholder works in braces, including other plugins' — ordinary braces in text are left alone.


Commands players can't type

with-permission: gives the player a permission for as long as that one button's actions run, then takes it straight back. A menu can use a command your players have no access to — so /sethome can live behind a screen that checks the name first, instead of in chat.

  create:
    label: 'Create'
    click-if:
      - '$(name|trim) matches [A-Za-z0-9_ ]{1,16}'
    with-permission:
      - 'essentials.sethome'
    run:
      - '[player] essentials:sethome $(name|slug)'
      - '[set] homename_$(name|slug) $(name|trim)'

Only [player] commands in that button's chain benefit, and only while they run.


Item lists

for-each: items turns one button into one per item in the game, drawn where the button is written. Three tokens fill in for each item:

Token Is
<icon> The item's sprite, drawn in the text
<name> Its name, in each player's own language
<id> Its id, e.g. diamond_sword
columns: 4

inputs:
  search:
    label: 'Search items'
    initial: '$(search)'

buttons:
  search:
    label: 'Search'
    run:
      - '[refresh]'

  icon:
    for-each: 'items'
    search: 'search'
    label: '<icon> <name>'
    run:
      - '[set] icon_%vista_var_home% <icon>'
      - '[open] home-screen'

search: names the input that narrows the list: type "sword", press Search, and only swords are left. It matches English names and ids, so "bottle o" finds the Bottle o' Enchanting.

A stored <icon> is a sprite wherever it is shown afterwards, so %vista_var_icon_base% in a label draws the item. Each item's button is built once and reused, so a screen of fifteen hundred opens as fast as a screen of ten. Beds are drawn whole, both halves side by side. Chests, shields and mob heads have no flat sprite in the game's textures and show only their name.


Shops

The whole of it

Define a currency once:

currencies:
  coins:
    name: 'Coins'
    balance: '%economy_balance%'
    take: 'eco take %player_name% %amount%'
    format: '<price> <currency>'

Name it at the top of a screen, and each entry states only its price:

currency: coins

buttons:
  diamond:
    label: 'Diamond'
    price: 100
    run:
      - '[console] give %player_name% diamond %amount%'

That one price: gives you the price line in the tooltip, a balance check before anything is taken, a confirmation screen, the deduction, the entry's own command, a receipt, a sound, the screens closing, and a refusal naming the cost and the balance when there isn't enough.

Nothing about money appears in the dialog file.

Sizes

Give each size its own price. Bulk does not have to be a multiple. %amount% becomes the quantity, and pressing the entry opens a quantity screen first.

  crate-key:
    label: 'Crate Key'
    amounts:
      1: 100
      3: 250
    run:
      - '[console] crates give %player_name% vote %amount%'

Already owned

    owned: 'server.perk.fly'

Once the player has that permission the entry recolours, reads Owned and stops responding. It stays on the screen so the catalogue is still readable instead of shrinking.

Recolouring means exactly that and nothing else. Affordability never changes how an entry looks: one the player cannot pay for is pressable, and the refusal arrives in chat.

How money reads

format: decides how an amount is written everywhere at once — tooltips, the quantity screen, the confirmation, the receipt, the refusal.

format: '<price> <currency>'   # 500 Coins
format: '$<price>'             # $500
format: '<raw> Cash'           # 500 Cash, no thousands separator
format: 'You need <price>'     # You need 500
format: '<price>⛃'             # 500⛃, a glyph from your resource pack

Add as many currencies as your server has. A currency is a balance placeholder and a take command, so anything that exposes those works.


Everything the player reads

Both screens Vista builds for you — the quantity picker and the confirmation — have every line in lang.yml, including their titles and both buttons. Nothing is compiled in.

quantity-title: "How many?"

quantity-body:
  - "<white>Choose how many <accent><name><white> to buy."
  - ""
  - "<gray>Your balance:</gray> <white><money>"

quantity-option: "<white><amount> <dark_gray>|</dark_gray> <accent><money>"
quantity-tooltip: "<accent><amount>x <name><br><br><white>Costs <accent><money><white>."
confirm-title: "Confirm Purchase"

confirm-body:
  - "<white>Buy <accent><amount>x <name><white> for <accent><money><white>?"
  - ""
  - "<gray>Your balance:</gray> <white><balance>"
  - "<gray>After this purchase:</gray> <white><after>"

button-confirm: "<green>Confirm"
button-confirm-tooltip: "<green>Confirm<br><br><white>Complete the purchase."
button-cancel: "<gray>Cancel"

The body lists take as many or as few lines as you like. Tokens available on them:

Token Is
<name> What is being bought
<amount> How many
<money> The cost, written the way that currency's format: says
<price> <currency> The same, split apart
<balance> What the player has
<after> What they will have

Column counts are buying.quantity-columns and buying.confirm-columns.

The wording every switch uses lives here too, so On and Off are said your way everywhere at once:

toggle-label: "<white><label>: <state>"
state-on: "<green>On"
state-off: "<gray>Off"

Prefixes

Three, all separate and all yours:

prefix: "<#3A90E8><b>VISTA </b><gray>▶</gray> "
error-prefix: "<#F43F5E><b>ERROR </b><gray>▶</gray> "
accent: "<#3A90E8>"
owned-color: "<#3A90E8>"

prefix is Vista talking about itself. error-prefix is every refusal, and a dialog can never override it. A dialog's own prefix: covers the messages it causes, so a shop's receipts say SHOP instead. accent recolours every screen Vista builds; owned-color is what an owned entry is drawn in.

Every refusal works the same way: a red ERROR line, and the screen closes. Nobody is left looking at a button that will not work.

Colours and text

MiniMessage throughout, with legacy &7 and &#RRGGBB codes accepted in the same string, so a line lifted from an older config renders without being rewritten.

  - '<gradient:#3A90E8:#8C4DCC>Gradient text</gradient>'
  - '<hover:show_text:"A tip"><u>Hover me</u></hover>'
  - '<head:Notch> drawn inline with the text'
  - '<sprite:items:item/diamond> any item sprite, <sprite:block/stone> any block'
  - '&7Legacy grey and &#3A90E8hex, in the same line as <white>MiniMessage'

Player lookup

A screen with lookup: true resolves its placeholders for whoever the viewer searched for instead of for the viewer, so one file serves as everyone's profile.

lookup: true
title: '%player_name%'

body:
  - '<head:%player_name%>'
  - '<gray>Kills:</gray> <white>%statistic_player_kills%</white>'

inputs:
  player:
    label: 'Look up a player'

buttons:
  search:
    label: 'Search'
    run:
      - '[lookup] $(player)'
      - '[refresh]'

Part of a name is enough. An exact name wins outright, a single match opens straight away, and several matches put up a Did you mean? screen — one button per player, each carrying their head, closest match first. Nothing resolves until one is pressed.

%vista_looking_at% is empty on your own profile and holds the searched name otherwise, which is enough to swap a Back button between two destinations with show-if. %vista_dialog% is the current dialog id. Both are answered by Vista itself.


Links

  discord:
    label: 'Join the Discord'
    tooltip:
      - '<#5865F2>Join the Discord'
      - ''
      - '<white>Opens in your browser.'
    url: 'https://discord.gg/ripplemc'

The client shows its own confirmation and opens the browser. No chat message with a link in it, no command in between. Placeholders work in the address.


Commands

Command Does
/vista open <dialog> [player] Opens a dialog
/vista list Every loaded dialog, with its file and button count
/vista reload Reloads the config, the language file and every dialog
/vista instantbuy Toggles your own purchase confirmations
/dialog open <id> [player] The same as /vista open
/dialog list The same as /vista list

vista.admin covers listing, reloading and opening a dialog for someone else.

Any screen with a command: gets its own command, registered live. Add a file, run /vista reload, and /yourdialog works — no restart.

If another plugin already has that name, the dialog takes it — command: playtime replaces their /playtime — and gives it back if the dialog is removed or reloaded with overwrite: false. With overwrite: false the dialog leaves their command alone and stays reachable as /vista:playtime.

A reload that finds something wrong in a file says so in chat, and the console says what and where.

Minecraft has had its own /dialog since 1.21.6. Vista takes the plain label and leaves the vanilla one reachable as /minecraft:dialog; commands.dialog: false leaves it alone.


Getting started

  1. Drop the jar in plugins/ and start the server.
  2. Vista writes config.yml, lang.yml and four example dialogs.
  3. Open plugins/Vista/dialogs/menu.yml — it is commented as a tutorial.
  4. Press G in game.

menu.yml covers the basics, shop.yml covers selling, profile.yml covers player lookup. Copy whichever is closest to what you want.

Updating: replace the jar. Settings added in a newer version are written into your config.yml and lang.yml on the next start, in the section they belong to and with the comment that explains them. Your values, ordering and your own notes are left alone, and example currencies you removed are not put back.


Requirements

Server: Paper or Folia 1.21.7 or newer, Java 21. Minecraft added dialogs in 1.21.6, but the server-side API for building them arrived in 1.21.7, and that is what Vista uses. On Folia every click, command and redraw runs on the region that owns the player.

Players: a 1.21.6 or newer client. Dialogs render client-side, so an older client cannot see one at all. Player heads and sprites in text additionally need 1.21.9 — on 1.21.6 to 1.21.8 the tag is skipped and the name still shows — and item sprites need 1.21.11, when the game gave items their own texture atlas.

Dependencies: none. PlaceholderAPI is used when it is there and ignored when it is not, and nothing else is required at all. The jar is around 100 KB and bundles nothing.


Open source all rights reserved.

Quick facts

Install steps are the general flow for this file type — How to install Minecraft Java mods & modpacks walks through it step by step.

File checked by MCModsHub

These come from our own check of the pack file, not from the source page.

Explore more