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

# Widgets

> Widget clicks, catalog-addressed identifiers, typed SDK reads, and the correctness rules for interface actions.

Interfaces are the largest source of silent failures in automation. This page covers the write surface (`WidgetActions`), the read surfaces (`sdk.query`, `sdk.widgets`), and the correctness rules that keep widget code revision-safe.

## WidgetActions

`WidgetActions` (`com.n3plugins.Api.actions`) clicks interface components: spell icons, dialogue buttons, tab panels, and CS2 components with no named menu action. Every action queues its mouse-click packet before the widget operation, returns `InteractionResult`, and routes through the pacer.

### Click by action name

`interact` requires a unique action-name match and resolves it to a one-based op index:

| Method                                       | Target            |
| -------------------------------------------- | ----------------- |
| `interact(int widgetId, String... actions)`  | Packed widget ID  |
| `interact(Widget widget, String... actions)` | A resolved widget |

```java theme={null}
WidgetActions.interact(packedWidgetId, "Continue");
WidgetActions.interact(widget, "Select");
```

Components with an `onOp` listener are invoked through RuneLite's native `CC_OP` path so the client listener runs; other named components use the direct widget packet path. The native path uses the shared revision-cached menu dispatcher. Resolution or invocation failure returns `PACKET_NOT_QUEUED`.

When the component exposes none of the listed actions, `interact` returns `ACTION_NOT_FOUND` and queues nothing. Multiple matching action slots also fail closed instead of picking the first. For an action followed by numeric input, use `interactThenResumeCount(widget, count, actions)`.

### Click by raw op index

Many components carry no named action, so the name path fails. `clickOp` fires a one-based op directly:

| Method                           | Target                   |
| -------------------------------- | ------------------------ |
| `clickOp(int widgetId, int op)`  | Packed widget ID, raw op |
| `clickOp(Widget widget, int op)` | Resolved widget, raw op  |

```java theme={null}
WidgetAddress close = WidgetCatalog.getInstance()
    .requireAddress("net.runelite.api.gameval.InterfaceID.SharedBank.CLOSE");
WidgetActions.clickOp(close.getPackedId(), 1);
```

`clickOp` guards null or hidden widgets and still applies pacing and click ordering. Confirm the op index against the widget inspector on a live run, and always source the packed ID from a catalog address or a current gameval `InterfaceID`/`ComponentID` constant. The correctness audit rejects unreviewed raw production literals.

### Sub-menus

`subAction` reaches a nested menu entry:

```java theme={null}
WidgetActions.subAction(widget, "Cast", "Resurrect");
```

## WidgetCatalog

`WidgetCatalog` is the revision-aware name and address index. It loads gameval `InterfaceID` declarations plus reference `WidgetID`/`WidgetInfo` declarations from the bundled widget-mapping snapshot pinned to the current RuneLite and game revision.

Discovery operations: `findExact`, `search`, `findByPackedId`, `findAliases`, `findByViewMode`. Resolution: `resolveAddress(name)` and `requireAddress(name)` fail closed for ambiguous, incomplete, or reference-only targets. A reference-only or `LIVE_PENDING` declaration resolves only when its packed target also has a current authoritative actionable mapping.

Packed widget IDs in production code come from a catalog address or from current gameval constants. Never hardcode bitshifted `(group << 16) | child` expressions and never turn a searchable reference-only raw value into an action target.

`WidgetMapping` exposes the qualified name, namespace, raw and decomposed values, mapping kind, view mode, authority, aliases, and disposition. `WidgetAddress` is the complete actionable group/child target with an optional dynamic index.

## Typed SDK reads

The `sdk.query` classes expose typed reads; writes stay in `Api.actions`:

| Read surface             | Highlights                                                                                                                                                                                             |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `Widgets`                | `get(packedId)`, `get(group, child)`, `get(WidgetAddress)`, `search()`                                                                                                                                 |
| `Inventory`              | `getItems()`, `find`, `count`, `contains`, `getEmptySlots()`                                                                                                                                           |
| `Bank`                   | `isOpen()`, `getItems()`, `search()`, `count`, `isNotedMode()`, plus `snapshot()`, `withdrawMode()`, `requestedQuantity()`, `placeholderMode()`, `rearrangeMode()`, and typed `BankWidget` descriptors |
| `Equipment`              | `getItems()`, `get(slot)`, `isEquipped(itemId)`                                                                                                                                                        |
| `Dialogue`               | `isPresent()`, `getHeader()`, `getText()`, `getOptions()`, `getContinueWidget()`, `snapshot()`                                                                                                         |
| `Shop`, `ShopInventory`  | Shop stock and the shop-side inventory panel                                                                                                                                                           |
| `GrandExchangeInventory` | The GE-side inventory panel                                                                                                                                                                            |
| `RunePouch`              | `contains`, `containsAll`, `hasAmount`, `quantity`                                                                                                                                                     |
| `Production`             | Make-X and legacy smithing reads: `isOpen()`, `products()`, `findProduct(itemId or name)`, `selectedProduct()`, `quantityButtons()`, `selectedQuantity()`, `makeXQuantity()`                           |
| `Minigames`              | Minigame teleport reads: `snapshot()`, `canTeleport()`, `cooldownValue()`, `destinations()`, `destination(...)`                                                                                        |

`ItemEntry` is the value type returned by inventory, bank, shop, and GE helpers, carrying `itemId`, `name`, `quantity`, and the backing `Widget`; `interact(actions...)` dispatches to that widget.

Read the interface before deciding which result-aware action to queue:

```java theme={null}
if (Production.isOpen()) {
    Production.findProduct("Shortbow").ifPresent(product ->
        log.debug("Make option {} at child {}", product.getName(), product.getChildIndex()));
}

InteractionResult result = ProductionActions.chooseOption("Shortbow");
```

Product and quantity selected-state are widget-state heuristics; confirm the production outcome through your own postconditions.

## Resolving text to a clickable widget

When visible text belongs to a descriptive child rather than the actionable one, use `WidgetInteractionResolver.findByText(root, text, actions)`. It examines the match and a bounded parent chain, requires exactly one visible named-action owner, and returns explicit resolved, missing, or ambiguous status. It never selects an arbitrary sibling.

Query name predicates strip HTML tags widget-side, so pass bare strings.

## Correctness rules

The suite enforces these boundaries with a static audit (`auditStageOneCorrectness`) and code review:

1. **Canonical identifiers only.** Widget addresses from `WidgetCatalog` or current gameval constants. Raw two-argument lookups, packed literals, and removed wrapper classes are findings.
2. **Unique matches.** `ActionResolver.findUniqueActionIndex(...)` fails closed on zero or multiple matches. `hasAction(...)` keeps any-match semantics for query filters.
3. **Click before operation.** Every widget write queues the mouse-click packet first; a paced or failed click queues no widget packet, and a failed widget packet returns `PACKET_NOT_QUEUED` without recording pacing.
4. **Native path for client-owned operations.** Listener-backed components select native `CC_OP` automatically; `interactViaMenuAction(...)` is the explicit native path used by side-tab actions. Bank closing queues a click before the native close operation because it is client-local.
5. **Dispatch is not confirmation.** `DISPATCHED` means the action was accepted for dispatch; `CONFIRMED` means the requested postcondition was observed. `accepted()` includes both; `succeeded()` is limited to legacy success or observed confirmation, so a dispatched action alone cannot complete a workflow step.
6. **No direct packet calls in modern code.** Deprecated named packet facades delegate to `WidgetActions`; production callers use the result-aware surface.

Walker and break-handler state machines retry `PACED` and transient results instead of advancing or treating them as terminal.
