# Add Gui

> Add a clickable inventory GUI to a Minecraft plugin — a menu built from an Inventory of ItemStacks with an InventoryClickEvent handler that cancels menu clicks. Use this whenever the user wants a chest menu, GUI, clickable interface, item-based menu, shop screen, settings panel, paginated selector, or 'open an inventory the player can click buttons in' for their Bukkit/Paper plugin. Reads the target stack from .mcplugin/config.yml.

- Skill: `itamarb2010-jpg/add-gui` (Agent Skill)
- Install (CLI): `npx skillmds@latest add itamarb2010-jpg/add-gui`
- Raw SKILL.md: https://api.skillmd.com/api/skills/itamarb2010-jpg/add-gui/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: itamarb2010-jpg (https://skillmd.com/u/itamarb2010-jpg)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/itamarb2010-jpg/add-gui

---


# Add a clickable inventory GUI

Wire up a chest-style menu with clickable buttons, matching the project's stack and conventions.
Follow the collaborative rule: propose the design, show the code, then write after the user
approves.

## Phase 1: Load context

- Read `.mcplugin/config.yml` for `platform`, `mc_version`, `package`, `main_class`,
  `plugin_name`. If it's missing, tell the user to run `/setup-platform` (and `/scaffold`) first.
- Read the reference and pitfalls (relative to this skill; `Glob` as fallback):
  - `../../references/api/inventory-gui.md` — `Inventory`, `InventoryHolder`, click handling
  - `../../references/pitfalls.md` — the "Events", "Threading", and "Memory leaks" sections
- Look at existing source to match style: `Glob` `src/main/java/**/*.java`, read the main class
  and any existing menu/listener classes.

## Phase 2: Clarify the menu

Ask (AskUserQuestion or plain text) only what you can't infer:
- **Size and layout** — rows (a chest is 9 per row, max 6 rows / 54 slots) and the menu title.
- **Items** — which slots hold which `Material`, their display names/lore, and whether any are
  filler/decoration vs. real buttons.
- **What each click does** — per button: run a command, give an item, toggle a setting, open a
  sub-menu, close. Note whether left/right/shift-click should differ.
- **Who opens it and how** — a command, an event, an NPC interaction.
- **Dynamic content** — is the menu static, or does it change per player / need pagination
  (next/prev buttons over a list too big for one screen)? Pagination state lives on the holder.

## Phase 3: Choose the identification strategy

Decide from the stack and explain the pick briefly — **how the click handler knows a click is in
*this* menu** matters for safety:
- **Custom `InventoryHolder`** (recommended) — create the inventory with
  `Bukkit.createInventory(new YourMenuHolder(...), size, title)` and identify clicks by
  `event.getInventory().getHolder() instanceof YourMenuHolder`. Robust and lets the holder carry
  per-menu state (e.g. page number, target player).
- **Title-string match** — fragile: titles can collide, and on Paper titles are `Component`s
  (compare via the Adventure API, not `equals` on a legacy string). Only for the simplest cases;
  never rely on it alone to gate the anti-theft cancel.

## Phase 4: Implement

Generate, in the project's package:
1. **A `YourMenuHolder implements InventoryHolder`** carrying any per-open state and building/
   returning the `Inventory`.
2. **A GUI/menu class** that builds items via `ItemStack` + `ItemMeta` — set display name and
   lore (on Paper use Adventure `Component` via `displayName(...)`; on Spigot
   `setDisplayName(String)`), place them in slots, and expose an `open(Player)` method.
3. **An `InventoryClickEvent` handler** (a registered `Listener`) that:
   - Confirms the inventory is this menu (holder check), then **always
     `event.setCancelled(true)` for menu-slot clicks** so players can't take the items
     (anti-theft) — do this *before* running the button action.
   - Dispatches on the clicked slot (and click type if relevant) to the button behaviour.
   - Guards against clicks in the player's own inventory / outside slots as intended, and blocks
     shift-click and number-key hotbar swaps that could pull items out of the menu.
   - Reads `event.getRawSlot()` to tell top-inventory (menu) slots from the player's inventory,
     and `event.getWhoClicked()` (cast to `Player`) for the actor — do any world side effects on
     the main thread (the click event already runs there; only *added* async work needs a hop).
4. **Register the listener** in `onEnable` via `getServer().getPluginManager().registerEvents`.
   Optionally handle `InventoryCloseEvent` to clean up per-menu state (avoid leaking `Player`
   references — key state by `UUID`).

Show the new/edited files (holder, menu class, click listener, `onEnable` diff) and get approval
before writing.

## Phase 5: Verify + hand off

- Re-check the pitfalls: menu-slot clicks are **cancelled** (items can't be stolen), the menu is
  **identified by `InventoryHolder`** (not by title string alone), the click listener is
  **registered**, no `Player` objects retained long-term (use `UUID`), and any async work in a
  button hops back to the main thread before touching the world/inventory.
- Suggest next steps: "`/build` to compile, then `/run-server`; open the menu and click each
  button to confirm actions fire and items stay put."

Do not fabricate APIs for a Minecraft version newer than `../../references/api/VERSION.md`
documents — if unsure a method exists in the target version, say so and verify.

