> ## Documentation Index
> Fetch the complete documentation index at: https://www.kitemc.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Interfaces, themes, and third-party development

<span id="interfaces-themes-and-third-party-development" />

KiteMarket provides **the complete vanilla GUI plus ItemsAdder v4 compatibility**. Third-party developers may freely create, modify, use, distribute or independently sell their own configuration or Java interfaces **without an additional KiteMC theme license**. Public SDKs and examples have independent MIT licenses; IA theme resources are not bundled.

<Info>
  **Version and downloads**

  This page covers the `1.0.0` public UI SDK. Interface source and examples are available through the [public repository](https://github.com/KiteMC/KiteMarket); see [downloads](/en/kitemarket/download) for runtime, SDK and example filenames. The pinned Paper 1.21.11 / Java 21 / ItemsAdder 4.0.16 combination has real openings of community configuration and Java examples; the [recorded scope](/en/kitemarket/compatibility) does not certify other versions or Folia.
</Info>

<h2 id="quick-start">
  Quick start
</h2>

For vanilla appearance changes, use [file configuration](/en/kitemarket/guide#customize-the-vanilla-gui) without writing a plugin. A built-in IA renderer only needs your resources and theme declaration; use Java only for custom rendering logic.

1. Get the `examples/ui/themes/example-ia` configuration example from the public repository, or the Java IA example in `KiteMarket-Examples-1.0.0.zip` once released.
2. Follow [IA integration](/en/kitemarket/dlc) to install your namespace and theme, rebuild the pack and register its actual UUID and SHA-1.
3. Java providers also require their JAR in `plugins/` and a normal restart. Configuration-only themes use `/km reload`.
4. After the player successfully applies the pack, use `/km ui itemsadder example-ia`; the Java example theme is `example-ia-java`. Confirmation buttons act on the real market, so develop with isolated characters and orders.

<h3 id="java-example-github-packages">
  Java example: GitHub Packages
</h3>

Reference `com.kitemc:kitemarket-ui-api:1.0.0` with `compileOnly`, never bundling or relocating the SDK. Runnable source is in the public `examples/ui-java` project, without proprietary core dependencies.

First merge the [market API's Packages registry and authentication configuration](/en/kitemarket/api#github-packages), then add these dependencies. Public packages also need a classic PAT with `read:packages`, provided through user-level `gpr.user`/`gpr.key` or `GITHUB_ACTOR`/`GITHUB_TOKEN`. Never store tokens in the project. Confirm that the repository's Packages list contains the version before using it; this guide does not claim the package is already uploaded.

```kotlin theme={null}
dependencies {
    compileOnly("com.kitemc:kitemarket-ui-api:1.0.0")
    compileOnly("com.destroystokyo.paper:paper-api:1.16.5-R0.1-SNAPSHOT")
    compileOnly("beer.devs:itemsadder-api:4.0.18-beta-10")
}
tasks.withType<JavaCompile>().configureEach {
    options.release.set(21) // IA adapter code; the public SDK stays on Java 11.
}
```

For Maven, use the same `github-kitemarket` registry and `settings.xml` credentials, then add:

```xml theme={null}
<dependency>
  <groupId>com.kitemc</groupId>
  <artifactId>kitemarket-ui-api</artifactId>
  <version>1.0.0</version>
  <scope>provided</scope>
</dependency>
```

Retain the Paper/Bukkit and IA API compile dependencies and their own registries. Packages do not contain vendor runtime plugins. Both SDKs remain Java 11; the real IA adapter example remains Java 21.

For offline development or when Packages authentication is unavailable, download `KiteMarket-UI-API-1.0.0.jar` directly from [GitHub Releases](https://github.com/KiteMC/KiteMarket/releases) as a fallback compile dependency without a Packages Token. No reference method allows bundling, shading or relocating the SDK.

<h2 id="player-selection-and-server-defaults">
  Player selection and server defaults
</h2>

`/km ui` displays the requested preference, actual interface, theme, and fallback reason. Select a backend and optional theme with:

```text theme={null}
/km ui auto
/km ui vanilla
/km ui itemsadder example-ia
```

Selection stores a preference. Without an actual provider, ready client, or resources, vanilla remains available with an explanation. “Choose a theme” in the settings lists registered themes with their backend and availability reason and can restore the server default. Preferences persist across the market network. Switching retains drafts and cannot submit a trade twice.

For example:

```yaml theme={null}
gui:
  renderer: auto
  auto-order: [itemsadder, vanilla]
  default-themes:
    itemsadder: example-ia
```

Player buttons, command help and completion show only auto/vanilla/itemsadder. Old `germ`/`dragoncore` preferences, settings and SDK enums remain readable as existing extension positions. Developers may register, maintain and validate their own actual providers. Without a matching registered provider, the interface reports `UI_BACKEND_RETIRED`, falls back and retains the saved preference.

<h2 id="own-theme-declarations">
  Own theme declarations
</h2>

Place declarations in `plugins/KiteMarket/themes/*.yml` and validate/load them with `/km reload`; an invalid candidate retains the active catalog. IDs use lowercase letters, digits, dots, underscores and hyphens, up to 96 characters, beginning with a letter or digit. `official.*` and the `km_market_stall` namespace are reserved identifiers. Third parties should use their own IDs and namespaces.

```yaml theme={null}
schema: 1
id: example-ia
backend: itemsadder
provider: kitemarket.itemsadder
requires: {}
resources:
  font-image: km_example:market
config:
  title-offset: 8
  texture-offset: -8
pages:
  '*': {}
  supply:
    font-image: km_example:market
    title-offset: 8
  result:
    state-font-images:
      SUCCESS: km_example:market
```

This is KiteMarket's common theme format, not a vendor-native configuration. `provider` is the stable ID of an actually registered implementation; omitting it does not create an SDK bridge. `pages.'*'` supplies defaults for shared pages, including `themes`; a specific **template ID** overrides font and title/background offsets. The table below distinguishes page IDs from template IDs: `supply-preview` uses the `supply` template. Actual items, amounts, results and actions remain server-owned.

`state-font-images` selects artwork from the server's `result.status`: `SUCCESS`, `PENDING`, `FAILED` and `UNCONFIRMED` describe completion, review required, refusal and an unconfirmed outcome. These presentation values are distinct from ledger operation states. A page mapping takes precedence over a resource-level state mapping; absent mappings use the page/general background. An exact page declaration is used when present, otherwise `*`; omitted font/offset fields use `resources`/`config` defaults.

The built-in ItemsAdder provider is `kitemarket.itemsadder`. Third-party themes may reference their own registered font IDs, such as `km_example:market`, without a theme product ID or entitlement receipt. `requires: {}` omits pack fields and inherits node defaults. For a separate pack, add its actual lowercase SHA-1 (40 digits) and sent UUID as `requires.pack-sha1` and `pack-id`. **Empty strings fail theme validation.** Missing valid identity or an unsuccessfully applied pack falls back. Updated content requires a new actual sent UUID; a registered UUID cannot be assigned a different digest.

Readiness uses public ProtocolLib observations of actual UUID, SHA-1 and URL, correlated with IA's public send event; it does not call internal obfuscated classes or treat a send event alone as readiness. Temporarily enable `gui.itemsadder.diagnostics: true` to inspect `[KITEMARKET_PACK]`, then disable it after registering identity. Failure, discard or removal revokes only that pack, leaving unrelated loaded IA state intact. Disconnects, node changes, removal of every pack or IA reload require confirmation again. Native status messages carry no send generation, so delayed responses to a repeated UUID with identical content cannot be distinguished by the protocol; changed content must use a fresh actual UUID.

<h2 id="resources-and-provider-implementations">
  Resources and provider implementations
</h2>

* **ItemsAdder**: Maintain your own namespace, manually rebuild and send the pack, and register its actual SHA-1 and Minecraft pack UUID. Sending a pack does not mean it has loaded. Market items remain actual ItemStacks; font images decorate standard inventories.
* **Java extensions**: Register your own provider through the public SDK and maintain its actual engine, resources and thread compatibility. Compatibility enum values do not imply that the base plugin bundles the corresponding vendor adapters.

Providers render and capture input; the shared server controller handles transactions. Callbacks must use server-registered action/input identifiers, never client-supplied amounts, identities, or stale pages. Closing pages, expiry, and repeated confirmations retain the shared session safeguards.

The public `KiteMarket-UI-API` module targets Java 11 under its own MIT License. Reference it with `compileOnly`, never bundling a second SDK. Implement `com.kitemc.market.api.ui.UiProvider`, obtain `KiteMarketUiApi` through Bukkit's ServicesManager, and call `api.register(owningPlugin, provider)`. Close the returned handle on plugin disable. `UiPage.token()/pageVersion()` and `UiPrompt.token()` describe page/field identity; `UiPage.actions()` and opening parameters supply opaque action identifiers. Return interactions only through `UiCallbacks.action(token)`, `input(raw)`, and `closed()`; the bound callback validates and schedules them again. Registration does not check official DLC ownership.

`UiProvider.update(...)` defaults to `open(...)`. A native interface may update in place only if it replaces the page identity, every action token and all callbacks, including close handling. After a genuine vendor event changes client/resource readiness, update the provider's own state and call `api.changed(owningPlugin)` for re-evaluation. Only enabled owners with a live registration may notify; the method itself is not proof that resources are ready.

Optional `UiProvider.refresh(player, page, theme)` refreshes display data within the same page, such as a countdown, and defaults to `false`. The token, page version, action map and existing callbacks stay unchanged. Update display clones in the current open view only; never reopen an inventory or invoke an action. Return `true` after applying it, or `false` when unsupported or closed. Existing providers retain static snapshots until normal navigation or a player refresh; unsupported refreshes do not periodically reopen menus. Verify current page identity so a late result cannot overwrite a replacement page.

An IA adapter can call the read-only `api.itemsAdderUnavailable(player, page, theme)` in the player's scheduling context to reuse the host's observed font registry, actual sent UUID/SHA-1 and matching successful load. Only `null` means the page resource is ready; other values describe a fallback reason. This check neither sends packs, changes preferences, evaluates official DLC rights nor grants trading authority. Its default returns `IA_READINESS_UNSUPPORTED` for existing service implementations and does not treat unknown state as readiness. Calling it requires a KiteMarket version shipping this method; do not bundle a replacement SDK into a provider.

Public `examples/ui/` contains the minimal white-frame configuration theme, and `examples/ui-java/` contains a real Java IA adapter. The latter targets Java 21 with a compile-only public vendor API and the real `TexturedInventoryWrapper`. It registers `example.itemsadder` to render actual market pages and registered actions; it never invents balances or transaction outcomes. The public SDK remains Java 11. Do not install this example on Java 11 Legacy or unverified Folia nodes.

The IA adapter fills the protected inventory returned by `TexturedInventoryWrapper.getInternal()`, registers the replacement holder, actions and callbacks, then calls the public `showInventory(player)` to display the font title. Opening only the internal inventory through Bukkit leaves IA's placeholder title. Retain this order and whole-view protection so a previous close is handled separately from the new page.

The `KiteMarket-Examples-1.0.0.zip` example bundle combines query and IA examples, including runnable JARs, themes, MIT resources, bilingual instructions and source/build files. Install the IA example JAR, place `theme.yml` in `plugins/KiteMarket/themes/example-ia-java.yml`, and copy `itemsadder/` into `plugins/ItemsAdder/contents/km_example/`. Rebuild/send the pack using the installed IA instructions, register its identity and select `/km ui itemsadder example-ia-java`. Updates, stale closes, repeated clicks and chat input retain the shared safeguards.

Source and resources identified as MIT in the configuration and Java examples may be modified for commercial interfaces without an additional KiteMC theme license. Other themes retain their own licenses. The examples' real publishing pages have been opened on the [representative environment](/en/kitemarket/compatibility); this record does not certify other packs or every page. Source, compilation and registration do not replace actual client verification.

<h3 id="custom-ia-functional-icons">
  Custom IA functional icons
</h3>

Existing functional buttons may use your own registered IA items. `resources.item-icons` maps vanilla `Material` defaults; `pages.<template>.slot-icons` overrides **physical slots** on that template. Actual `subject()` trade items are never replaced:

```yaml theme={null}
resources:
  font-image: my_theme:market
  item-icons:
    BOOK: my_theme:book_button
pages:
  '*': {}
  browse:
    slot-icons:
      '49': my_theme:back_button
```

`UiItemIcons.resolve(page, theme)` returns read-only bindings, creating no actions or readiness proof. Obtain and clone registered items through the real IA API, preserving host names, lore, quantities and actions. Missing icons report `IA_RESOURCES_PENDING` and fall back. Exact templates do not merge with `'*'`. Resources cannot substitute trade subjects, wallet amounts or transaction rules.

<h2 id="pages-actions-and-lifecycle">
  Pages, actions and lifecycle
</h2>

`UiPage.key()` identifies the logical page; `UiPage.template()` selects theme configuration. Vanilla `menus` uses page IDs, while IA `pages` uses template IDs. The complete 35 pages and their main actions follow; “Same” means the template equals the page ID:

| Page ID | Template ID | Main actions |
| - | - | - |
| `home` | Same | Compact home: three trading categories, player head, wallet, claims and my orders; legacy home retains its entrances |
| `profile` | Same | Interface preferences, personal review queue, history, permission-dependent administration and back |
| `browse` | `browse` / `orders` | Search, filters, pagination, details; personal orders use `orders` |
| `browse-filters` | Same | Type, currency, material, sort and search |
| `order` | `detail` | Purchase, supply, bid and cancellation confirmation |
| `details` | Same | Long text and conditions |
| `editor` | Wizard templates below | Type, item/conditions, quantity, price and duration |
| `confirm` | `confirm` / `wizard-confirm` | Final confirmation and return |
| `preview` | Same | Inventory matching against the draft rule |
| `supply-preview` | `supply` | Protect slots, quantity, maximum, refresh and confirm |
| `number` | Same | Increments, presets, actual available maximum and custom input |
| `materials` | Same | Multi-select, filter and main-hand import |
| `durability` | Same | Range, presets, import and clear |
| `text-condition` | Same | Exact/contains, chat input, import and clear |
| `enchantments` | Same | Chinese/English name or ID search, clear search, selection, import and extra-enchantment option |
| `enchantment-range` | Same | Minimum/maximum and removal |
| `insufficient` | Same | Required funds and deposit entrance |
| `wallet` / `wallet-currency` | `wallet` | Balances and transfer confirmation |
| `assets` | `claims` | View and claim assets |
| `history` / `receipt` | `history` | Pagination, read-only receipts and on-demand operation ID viewing/copying |
| `admin` / `admin-player` | Same | Review queue and player audit entrances |
| `admin-wallet` / `admin-assets` | Same | Read-only player balances and all asset states |
| `admin-orders` / `admin-player-history` | Same | Read-only player orders and history |
| `resolve-source` | `resolve` | Source-quiescence declaration and confirmation |
| `doctor` | Same | Node, database, licensing and transfer diagnostics |
| `inspect` / `evidence` | `inspect` | Structured evidence and permitted review entrances |
| `ui` / `themes` | Same | Backend, theme or server-default selection |
| `result` | Same | Status, receipt, wallet, claims and continue browsing |

Wizard templates follow the step: `wizard-type`, `wizard-item`, `wizard-terms` (buy order), `wizard-sale-terms`, `wizard-auction-terms`, then `wizard-confirm`.

Default compact home source slots are `0` player head, `4` help, `8` claims, `20/22/24` trading categories, `45` wallet and `53` my orders. `profile` uses `20` interface preferences, `22` review queue, `24` history, `31` administration and `49` back; the administration entrance depends on permissions. The review list reuses `history`. Select home content with `gui.home-layout: auto|compact|legacy`; an existing `menus.home` preserves the legacy home under `auto`. See [vanilla GUI customization](/en/kitemarket/guide#customize-the-vanilla-gui) for migration. Read current snapshots and actions instead of assuming all home layouts share one slot set.

Quantity maxima use actual wallet funds, available inventory items and currency limits; auctions use only the main-hand stack. Transfers use real balances and any known backend receive capacity. Changed quotes require another choice, and final submission rechecks limits; a theme must not raise them independently. Receipts provide a separate “View / copy operation ID” action that sends the complete ID and copy control in chat. Summaries stay concise; `inspect` and the read-only API retain IDs.

Sales support partial purchases at a per-item unit price. Their per-order minimum defaults to 1 and can be set from 1 to the listed quantity; a remainder below the minimum must be purchased together. Buy orders use unit prices for partial fulfillment, and auction starting prices apply to the whole lot. Render host amounts, input ranges and item descriptions. Remaining, total and traded quantities are separate, and real item properties remain intact. Display timestamps and countdowns do not replace database deadline checks. Claims use actual item stack limits, leaving assets in claims when inventory space is insufficient.

The current `page.actions()` slot-to-opaque-token map is the action list. Entries without a token are informational. Never fabricate action strings or reuse a previous page's tokens. Every open/update replaces identity, tokens and callbacks. Input returns through `UiCallbacks.input(raw)` and closure through `closed()`. Returning `false` from `prompt()` retains the host's validated chat input and drafts.

| Method | Lifecycle contract |
| - | - |
| `register(owner, provider)` | Enabled owning plugin; returns an idempotent unregister handle |
| `unavailable(...)` | Readiness only: `null` is ready, otherwise a reason code |
| `open(...)` / `update(...)` | Render cloned snapshots in the player context and replace all bindings |
| `refresh(...)` | Optional display update with the same identity and bindings; never reopen; default `false` |
| `isOpen(...)` / `close(...)` | Identify and close only your current view |
| `prompt(...)` | Native input, or `false` for host chat input |
| `changed(owner)` | Recheck after genuine resource changes; no trading authority |

Registration can wait for database initialization. If lookup is null, listen for `ServiceRegisterEvent`; discard old registrations and views when replaced. Unregister on owner disable; the host handles safe fallback. Never manipulate player inventories from Folia's global thread. The example does not claim Folia certification.

<h2 id="troubleshooting">
  Troubleshooting
</h2>

| Symptom | Check |
| - | - |
| Theme absent | `themes/*.yml`, unique ID and a valid candidate reload |
| `UI_PROVIDER_UNAVAILABLE` | Matching registered provider ID and enabled owner |
| `IA_PACK_NOT_REGISTERED` | Actual observed pack UUID and SHA-1, not random values |
| `IA_PACK_NOT_APPLIED` | Matching successful load rather than send/accept only |
| `IA_RESOURCES_PENDING` | Registered font/icon IDs and completed pack rebuild |
| Placeholder title | Use IA `showInventory(player)`, not only the internal Bukkit inventory |
| Rejected action / stale page | Replace all tokens and callbacks; reopen instead of replaying |
| Reload retains old theme | Read the exact field path in logs; invalid candidates never replace valid configuration |

<h2 id="installation-and-scope">
  Installation and scope
</h2>

After creating or obtaining a third-party theme, follow [ItemsAdder integration](/en/kitemarket/dlc) to install its resources and register the actual pack identity. No official DLC product ID, signature or entitlement is needed. The plugin does not supply a commercial IA theme by default; unavailable themes retain the vanilla interface. The base plugin still follows [network licensing](/en/kitemarket/license) for trading and asset exit.

[Compatibility](/en/kitemarket/compatibility) records evidence for the base plugin, providers, and individual themes separately. Public SDKs and provider registration do not replace actual runtime acceptance.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.