actually bruh

This commit is contained in:
2026-05-26 13:31:21 -04:00
parent 4f4f731bc7
commit ade5f069e3
31 changed files with 1109 additions and 179 deletions
+132
View File
@@ -0,0 +1,132 @@
# UI
Built on top of [HyUI](https://github.com/Elliesaur/HyUI), a third-party library that wraps Hytale's native Custom UI API in an HTML-like markup (HYUIML). HyUI is bundled into the Staff jar via the shade plugin so you don't need it installed separately.
## Reading the source
Each page lives in [src/main/java/net/kewwbec/staff/ui/](../src/main/java/net/kewwbec/staff/ui/). Pages don't extend the SDK's `InteractiveCustomUIPage` anymore - they're plain classes that build a `PageBuilder` and expose an `open(store)` method. Navigation between pages is `new OtherPage(...).open(store)`.
## Page flow
```
/staffui
|
v
[StaffHubPage]
|
+-> Player Lookup -> [PlayerLookupPage] (text search, click result for detail)
|
v
[PlayerDetailPage]
|
+-> Ban / TempBan / Mute / TempMute / Kick / Warn --> [PunishFormPage]
+-> Unban / Unmute (applied inline, page re-opens)
+-> History --> [HistoryPage]
```
Every non-hub page has a Back button. ESC always closes (lifetime is `CanDismiss`).
## Page reference
| Page | Purpose |
|---|---|
| [StaffHubPage](../src/main/java/net/kewwbec/staff/ui/StaffHubPage.java) | Top-level menu. Currently just routes to Player Lookup; add buttons here for new sections. |
| [PlayerLookupPage](../src/main/java/net/kewwbec/staff/ui/PlayerLookupPage.java) | Text input + Search button. Online players are listed first; if you type a name not online, the players_seen table is consulted. Click a result to open detail. |
| [PlayerDetailPage](../src/main/java/net/kewwbec/staff/ui/PlayerDetailPage.java) | Player info + active ban/mute lines + action buttons. Unban / Unmute fire immediately (no form); the page reopens with refreshed data. |
| [PunishFormPage](../src/main/java/net/kewwbec/staff/ui/PunishFormPage.java) | Reason field, plus duration field for timed types. Validation errors re-open the same page with the entered values and a red error line. |
| [HistoryPage](../src/main/java/net/kewwbec/staff/ui/HistoryPage.java) | Last 50 entries for the target. Read-only. |
## HyUI patterns we use
All pages follow the same skeleton:
```java
public final class SomePage {
private final PlayerRef playerRef;
private final StaffUIContext sCtx;
private final PageBuilder page;
public SomePage(PlayerRef playerRef, StaffUIContext sCtx, ...args) {
this.playerRef = playerRef;
this.sCtx = sCtx;
// Build the HYUIML based on constructor state
String html = """
<div class="page-overlay">
<div class="container" data-hyui-title="Title">
<div class="container-contents">
<button id="someBtn">Do thing</button>
</div>
</div>
</div>
""";
this.page = PageBuilder.pageForPlayer(playerRef)
.withLifetime(CustomPageLifetime.CanDismiss)
.fromHtml(html);
// Wire listeners by element id
page.addEventListener("someBtn", CustomUIEventBindingType.Activating, (ignored, pCtx) -> {
// pCtx.getValue("inputId", String.class).orElse("") to read input values
// Navigate by opening a new page instance
});
}
public void open(Store<EntityStore> store) { page.open(store); }
}
```
### Reading input values
Inside an event listener, the second argument is the HyUI page context, which exposes `getValue(id, type)`:
```java
page.addEventListener("submitBtn", CustomUIEventBindingType.Activating, (ignored, pCtx) -> {
String reason = pCtx.getValue("reasonInput", String.class).orElse("").trim();
...
});
```
### Re-rendering after state changes
We do *not* mutate the page in place. Instead, we open a fresh page instance with new state:
```java
new PunishFormPage(playerRef, sCtx, uuid, name, type, durationFromForm, reasonFromForm, "Error: ...").open(store);
```
This is simpler than HyUI's `updatePage(true)` re-render dance and keeps each page render side-effect-free. The cost is rebuilding the HTML each navigation - cheap compared to anything else we do.
### HYUIML class reference (the bits we use)
- `<div class="page-overlay">` - root, dims background, captures input
- `<div class="container" data-hyui-title="...">` - main framed window with header
- `<div class="container-contents">` - inner content area
- `<button id="...">Text</button>` - clickable button (use `class="back-button"` for back-themed)
- `<input type="text" id="..." value="..." placeholder="...">` - text input
- `<p>` / `<label>` - text
The full HYUIML reference is in `extras to look at/HyUI-Docs/hyuiml-htmlish-in-hytale.md`.
## Adding a new page
1. Create `src/main/java/net/kewwbec/staff/ui/MyPage.java` following the skeleton above.
2. Use HTMLish in the `fromHtml` block. Stick to alphanumeric IDs (HyUI sanitizes them, but bugs hide in non-trivial chars).
3. Wire listeners with `page.addEventListener(id, eventType, handler)`.
4. To navigate to it, call `new MyPage(...).open(store)` from another page's handler.
5. To open it from a command, do the same thing in the command's `execute` method.
## Things to be aware of
- **HyUI must be on the classpath at runtime.** This is handled by the shade plugin in [pom.xml](../pom.xml), which packs HyUI into the Staff jar. Don't drop the shade plugin without a replacement.
- **Sanitized IDs.** Use plain alphanumeric ids in HYUIML. Use the same string in `addEventListener` - HyUI translates internally.
- **Escape user input** when embedding into HTML strings. We have a tiny `escape(String)` helper in each page; use it for any value that came from a player.
- **`value` attribute is what carries text-field state across re-renders.** When you rebuild a form page with an error, set `value="..."` on each input so the user doesn't lose what they typed.
- **No `<script>`, no full CSS.** Logic is in Java; layout is via Hytale's `LayoutMode` and `flex-weight` via `style="..."` on `<div>`s if needed.
## What this UI deliberately doesn't do
- **Network-wide active punishments view.** Could be added as a Hub button if needed.
- **Bulk actions.** No "ban everyone in this list" - intentional.
- **In-place updates.** Every state change rebuilds the page. Simpler reasoning, slightly more network chatter.
- **Custom themes.** We rely on HyUI's default Container/Button styling. Theming via `<style>` blocks is possible later.