GUI
Overview
GUI turns a single TypeWriter entry, Open GUI, into a complete inventory menu. You choose the container type (a chest, an anvil, a merchant window, a written book...), write a title in MiniMessage, then describe the content as a tree of layouts stored in the entry's layoutPool. Each layout places items: a material, a name, some lore, and what happens when a player clicks it.
Everything is edited in the web panel: there are no menu files to write by hand. A menu is opened like any other action, from a quest step, an NPC dialogue, a command event or another menu. Clicks run console commands, trigger other entries and modify facts, so a menu is just another way to drive your TypeWriter pages.
GUI is also the foundation of the other BTC menu extensions. Shops and Quest Codex do not ship their own window: they place marker items in an open_gui layout and the extension fills them with products or quests.
Features
- Ten layouts —
simple,flex,paginated,scrollable,frame,composite,book,merchant,storageandleaderboard. - 24 container types — chest sizes from 9 to 54 slots, plus anvil, enchanting table, smithing table, stonecutter, grindstone, loom, cartography table, merchant, book, hopper, furnaces and more.
- MiniMessage everywhere — colours, gradients and PlaceholderAPI placeholders in titles, item names and lore.
- Click actions per click type — left, right, shift, middle, double click, number keys, drop and offhand swap, each with its own commands and triggers.
- Slot permissions — hide a slot with
viewPermissionor leave it visible but inert withclickPermission. - Slot repetition — fill a row or a block with
count,direction,gapandrepeatYinstead of copying items. - Views and tabs — one menu shell, several screens, with a breadcrumb title and tabs that light up.
- Persistent storage — slots that keep their items per player or per group, with quotas, placeholders and optional drop on close.
- Leaderboards — rank players, worlds or groups on one or more facts, including offline players, or show the rows another extension supplies through the
sourcefield (the statistics of Dungeon, for example). - Extended inventory — turn the player's own inventory into four extra menu rows for a ten-row canvas.
Installation
- Install TypeWriter on a Paper or Folia server. PacketEvents, which TypeWriter already requires, must be present.
- Download the JAR from the GUI GitHub releases (the button in the summary panel above).
- Put it in
plugins/Typewriter/extensions/and restart the server. - Optional: install CraftEngine if your titles use
<image>or<shift>tags to draw custom backgrounds.
Install GUI before the extensions that rely on it, such as Shops and Quest Codex.
The public GUI release does not include a standalone menu editor or its web server: menus are edited in the TypeWriter web panel. The release only ships a startup check that reads your published open_gui entries and reports broken menus in the console (see Troubleshooting).
Getting started
Build a small three-button menu:
- In the web panel, open a sequence page and add an Open GUI entry. Name it
main_menu. - Set
guiTypetoCUSTOM,sizetoSIZE_27andtitleto<gold>Main menu. - In
layoutPool, add a layout of typesimple, give it theidmain, then typemaininmainLayoutId. - In the layout
items, add an item atx4,y1. Pick aDIAMONDinitem, setdisplayNameto<aqua>Rewardsand add aloreline<gray>Click to claim. - In
interactionList, add an interaction withtypeLEFT_CLICK. Add a console command such asgive %player% diamond 1tocommands, or link an entry intriggers. EnablecloseMenuto close the menu after the click. - Add a second
simpleitem withcount9,directionrightand a gray stained glass pane to draw a border row. Set itsdisplayNameto a single space. - Link the Open GUI entry to something that fires it, for example the trigger of a dialogue option or a command event, then Publish.
Get a single simple layout working in game before you add frame, scrollable or paginated layers. Every layout is addressed by its id, so a mistyped mainLayoutId is the first thing to check when a menu opens empty.
How it works
When the action fires, the extension builds the layout tree for that player (criteria and permissions are evaluated at this moment), registers a menu session and shows the inventory. Each click is resolved against the slot at that position, then runs the matching interaction. The session remembers previous menus, so gui:back returns to the menu the player came from.
Menus
Container types
guiType selects the window. CUSTOM is a plain chest-like inventory whose height you choose with size. size only applies to CUSTOM and defaults to six rows; the other types use the vanilla size of their window.
guiType | Window |
|---|---|
CUSTOM, CHEST, ENDER_CHEST, BARREL, SHULKER_BOX | Chest-style grids. For CUSTOM, size is SIZE_9, SIZE_18, SIZE_27, SIZE_36, SIZE_45 or SIZE_54. |
HOPPER, DISPENSER, DROPPER | Small fixed-size containers. |
FURNACE, BLAST_FURNACE, SMOKER, BREWING_STAND, BEACON | Machine windows. |
CRAFTING_TABLE, ENCHANTING_TABLE, ANVIL, SMITHING_TABLE, GRINDSTONE, LOOM, CARTOGRAPHY_TABLE, STONECUTTER | Vanilla workstations. With no layout they behave like the vanilla screen; GUI only manages opening and closing. |
MERCHANT, VILLAGER_TRADE | The villager trade window, filled by a merchant layout. |
BOOK | A written book, filled by a book layout. |
Layouts
Each layout is a named entry of layoutPool. The one named in mainLayoutId is the root; the others are reached through references (composite children, scrollable innerId, frame layoutId).
| Layout | Use it for | Key fields |
|---|---|---|
simple | Fixed buttons and decoration at x/y positions. | items |
flex | Items laid out automatically in rows. | justifyContent (START, CENTER, END, SPACE_BETWEEN), alignItems, wrap, virtualHeight |
paginated | A long list split into pages. | itemsPerPage, slots, items, navigationButtons (NEXT, PREVIOUS, BACK, INDICATOR) |
scrollable | Content taller or wider than the window. | innerId, virtualWidth, virtualHeight, buttons (direction, step), showDefaultButtons, defaultStep |
frame | Several independent rectangles, for example tabs, content and a footer. | frames (id, x, y, width, height, layoutId) |
composite | Stacking layers: background, content, buttons. | children (layout ids, later children draw on top) |
book | The pages of a written book. | pages (MiniMessage) |
merchant | Villager trades with custom items. | trades (result, costOne, costTwo, maxUses, criteria) |
storage | Persistent slots backed by a GUI Storage artifact. | entry, groupKey, slots |
leaderboard | Ranking rows with pages, from facts or from the source of another extension. | leaderboard, x, y, width, height, previousButton, nextButton |
A book or merchant layout only works as the main layout, and guiType is overridden to the matching window. A scrollable layout gets default arrows while showDefaultButtons is on (the default): one for every direction you gave no buttons entry, see Layouts, frames and slots.
See Layouts, frames and slots for a detailed walk through each layout.
Items and slots
Every item of a layout is a slot description with the same fields:
| Field | Role |
|---|---|
item | The item to display. Left empty, the slot shows the item the player holds in their main hand. |
displayName, lore | MiniMessage text with PlaceholderAPI support. Names are upright by default; add <italic> if you want italics. A blank displayName keeps the name of the item instead of replacing it with an empty one. |
x, y | Column 0 to 8 and row from 0 at the top. |
rollPerSlot | For an item repeated over several slots whose item is random: off (default) rolls once and every slot shows that result, on rolls each slot on its own when the menu opens. The result is kept while the menu stays open, unless liveItem is on. |
liveItem | Off (default): the item is drawn when the menu opens and kept for the whole session; on refresh only its name and lore follow. On: the item is evaluated again at every refresh, so a random item is drawn again each time. With rollPerSlot on, each repeated slot draws on its own at each refresh; with it off, all repeated slots share one draw per refresh. A blank name is still ignored. |
count, direction, gap, repeatY | Slot repetition, see below. |
criteria | The slot is not drawn for players who do not match. |
viewPermission | Permission required to see the slot. |
clickPermission | Permission required to use the slot; without it the slot is drawn but inert. |
allowPickup, isGhost | Whether the item can be taken out of the menu. Both are off by default; ghost items can be clicked but never moved or copied. |
cooldownTicks | Ticks before the same slot can be clicked again. |
interactionList, triggers, modifiers | What a click does. |
input | Opens a text input dialog on click. |
storageId | Turns the slot into a persistent storage slot from storagePool. |
buttonType, buttonPrefix | Marks the slot as a button provided by another extension. |
animation | Moves the slot to a target position over a duration in milliseconds when the menu opens. |
Slot repetition. count repeats an item along direction (right, left, down, up). gap is a step, not a spacing: 1 is adjacent, 2 leaves one empty slot between copies, and 0 behaves as 1. repeatY repeats the whole line on the perpendicular axis. Without a direction, count, gap and repeatY are ignored and a single slot is placed. These fields count slots, never the stack size of the item.
Overlaps. When two slots share a cell, the one that does something (a click action, a tag, a storage slot) wins over a purely decorative one, whatever the order. Between two of the same kind the last declared one wins. A warning naming the cell is logged once.
Click actions
interactionList maps a click type to commands and triggers. The available types are LEFT_CLICK, RIGHT_CLICK, SHIFT_LEFT_CLICK, SHIFT_RIGHT_CLICK, MIDDLE_CLICK, DOUBLE_CLICK, NUMBER_KEY_1 to NUMBER_KEY_9, DROP, DROP_ALL and SWAP_OFFHAND.
commandsrun from the console.%player%is replaced by the player name, and PlaceholderAPI placeholders are resolved first.triggersfire TypeWriter entries for the player. The slot's owntriggersandmodifiersfire on every left or right click, or whenever a specific interaction matches.closeMenucloses the window after the action.executeReturngoes back to the previous menu.
Commands beginning with gui: are handled by GUI itself:
| Command | Effect |
|---|---|
gui:close | Close the menu. |
gui:back | Reopen the previous menu of the session. |
gui:open <entry_id> or gui:action <entry_id> | Trigger another entry, usually another Open GUI. |
gui:view <view_id> | Switch to another view without closing the inventory. |
gui:page <delta> [layout_id] | Move a paginated or leaderboard layout by that many pages. |
gui:scroll <dx> <dy> [layout_id] | Scroll a scrollable layout. |
gui:refresh | Redraw the menu now. |
gui:sound <sound_key> | Play a sound to the player. |
Open, back and close
An Open GUI opened from another menu is pushed onto the session history, so gui:back or executeReturn returns to it. Closing with Escape ends the session. Opening a book layout ends the current session first.
Set audio to play sounds on open, close, scroll and click. Set autoRefreshTicks to redraw the menu every N ticks while it is open, which is what makes placeholders in names and lore tick like a counter or a timer.
Views, tabs and inheritance
views turn one open_gui into several screens that share the same shell. A frame whose layoutId is @view is filled with the layout of the active view: first the view's explicit frames mapping, then the layout named <view id>_<frame id>, then the same lookups on the view's parentId. Mark a tab item with buttonPrefix view: and buttonType set to the view id and it switches view when clicked and lights up while active. The view title accepts {breadcrumb}, {view} and {root}.
baseMenuId points at another Open GUI and merges its layoutPool, views and storagePool under yours: a layout with the same id replaces the inherited one. Details in Views, tabs and inheritance.
Storage slots
A GUI Storage artifact holds the items, per player or per group. Declare reusable configurations in the entry's storagePool, then give an item a storageId, or use a storage layout. Item names and lore of a storage slot accept {stored_name}, {stored_amount} and {stored_max}.
Default click bindings, which you can change in GUI Global Settings:
| Action | Default click |
|---|---|
| Place one item from the cursor | Left click |
| Place the whole cursor stack | Shift + left click |
| Take one item | Right click |
| Take everything | Left click |
| Take a stack | Shift + right click |
| Fill from the inventory | Swap offhand (F) |
| Drop everything on the ground | Drop (Q) |
dropOnClose drops what is still stored at the player's feet when the menu really closes, on Escape or on disconnect, but not when the player moves to another menu.
Extended inventory
extendToPlayerInventory shows the player's own 36 slots as four extra rows, for a canvas of ten rows (y 0 to 9). It is client-side only: the real inventory is never modified and is restored when the menu closes, and items cannot be picked up from the bottom rows. It requires guiType CUSTOM with size SIZE_54. A root layout whose id ends with _extended enables it on its own.
Configuration
GUI has no configuration file. Two optional manifest entries adjust it:
- GUI Extension Configuration enables
debuglogging and thehideVanillaItemStatsoption, on by default, which hides attack damage, durability and enchantment lines on menu buttons. - GUI Global Settings sets the storage click bindings and the skill tree connector items.
Placeholders
| Placeholder | Returns |
|---|---|
%typewriter_gui_storage_amount_<entry_id>_<slot_index>% | Number of items stored for the player in a GUI Storage entry, 0 if empty. |
%typewriter_gui_storage_has_<entry_id>_<slot_index>% | true when the slot holds an item, otherwise false. |
<slot_index> is y * 9 + x of the storage slot. These placeholders always read the player's own storage, never a shared group storage.
Inside menus, the following tokens are replaced by the extension: {stored_name}, {stored_amount}, {stored_max} (storage slots), {page}, {next_page}, {prev_page}, {total_pages} and {page_indicator} (the INDICATOR button of a paginated layout), and {rank}, {name}, {score}, {group}, {world} and {score_<fact_id>} (leaderboard rows), plus {score_raw} and {value_<key>} for the rows of a leaderboard source.
Entries
6 entries, generated from the extension source.
Actions1
- Open GUIOpen a GUI for the player
Artifacts2
- GUI Leaderboard PopulationOffline population data for GUI leaderboards
- GUI StoragePersistent storage artifact for GUI storage slots
Manifests3
- GUI ConfigGUI Extension Configuration
- GUI LeaderboardGUI leaderboard backed by Typewriter facts
- GUI SettingsGUI Global Settings
Troubleshooting
The menu opens empty. mainLayoutId is blank, or does not match any layout id in layoutPool (ids are case-sensitive). When using baseMenuId, a blank mainLayoutId falls back to the parent's. The startup check reports an unresolved main layout in the console.
A slot is missing and the console says "falls outside the grid". The item landed at a negative x or y, or past column 8. This usually comes from a too large count, which counts slots. The log names the layout and the rejected coordinates.
The console says "Slot overlap ... gap". Two slots share a cell. gap is a step: 1 places adjacent slots and 2 leaves one empty slot between each, so authors who read gap: 1 as "one gap" get copies on top of each other's neighbours. Change gap or move one slot.
A slot is visible to admins but not to players. viewPermission names a permission nobody but operators has. Blank values are ignored, but a typo is a real, operator-only node. The same rule applies to a view's viewPermission.
A button shows but does nothing. Check clickPermission, then cooldownTicks, then criteria. If the slot has a buttonType, its buttonPrefix must match the extension providing it; an unclaimed tag keeps your own interactionList but nothing else. Commands run from the console, so a command that needs a player sender fails silently.
The scrollable content does not move. Check that the content is larger than the window (virtualWidth and virtualHeight): an arrow only shows while there is something to scroll to in its direction. With showDefaultButtons off and no buttons, there is no arrow at all. A default arrow also gives way when its cell already holds a content item, a button or a decoration: nothing is logged, so move it with scrollArrows in GUI Settings or add your own buttons with a direction and a step, outside the scrolled area.
The leaderboard only lists online players. Link a GUI Leaderboard Population artifact in the leaderboard's population field to keep the last known values of offline players.
The extended menu does not show ten rows. extendToPlayerInventory needs guiType CUSTOM and size SIZE_54.
Related extensions
- Shops — shop menus built on GUI layouts and the
shop_button:marker. - Quest Codex — the quest log menu, built on the same
open_guichassis.