Search docs
Search modules and symbols
Default components

Default components

ui.components() is the shipped library. It drives s2script_lib.xml in addon 3790153369. Plugins pass data, not panel ids. The pool is host-global so two plugins cannot claim the same panel. Import ui from @s2script/cs2 and call it from OnPluginStart — it throws after settle. Toast, badge, and modal hang off the returned object, not ui itself.

import { ui } from '@s2script/cs2';

export function OnPluginStart(): void {
  const panels = ui.components();
}
KindPoolClaim
Toast4, rotatingpanels.toast(slot, spec) — no claim, next free line
Badge4panels.badge(spec)null when exhausted
Modal2, 8 rows eachpanels.modal(spec)null when exhausted

Variant is "primary" | "good" | "warn" | "bad" | "ghost". ghost is the low-emphasis default for footer buttons.

Need a kill feed, scoreboard, or MOTD the library does not cover? Mount your own layout.

Toast

Transient notice. No claim — four lines rotate. Drive from any slot-bearing callback (OnClientActive, player_death, a command).

panels.toast(slot, {
  title?: string;
  message?: string;
  variant?: Variant;     // default unstyled
  holdSeconds?: number;  // default 6; 0 keeps it until replaced
});

Returns HudResult (null on success). A generation stamp per line so a replacement is not yanked by the previous hold timer.

Badge

Persistent corner element. Claim once, then show / hide per player.

const badge = panels.badge({
  corner?: "tl" | "tr" | "bl" | "br";  // default "tr"
  title?: string;
  accent?: "accent" | "good" | "warn" | "bad";
});

badge?.show(slot, { title?: string; text?: string });
badge?.hide(slot);
badge?.release();  // return the pool slot

show overlays title / text on the spec defaults for that player.

Modal

Paged list. onPick, cursor(), and select() use absolute indices into the full row list, not the current page.

const modal = panels.modal({
  title: string | ((slot) => string);
  subtitle?: string | ((slot) => string);  // default: page indicator when paged
  rows: Row[] | ((slot) => Row[]);
  onPick?: (slot, index, row) => void;
  detail?: (slot, row, cursor) => string[];  // up to 4 lines; last is clamped
  buttons?: FooterButton[];                  // up to 5
  pageSize?: number;                         // default 8
  width?: "sm" | "md" | "lg" | "xl";         // default "md" (560px)
});

Row is { a: string; b?: string; c?: string; disabled?: boolean }. a flexes; b and c are right-aligned. disabled greys the row — onPick still fires so you can say why.

FooterButton is { text: string; variant?: Variant; onClick: (slot) => void }. Prev/Next take the trailing two footer slots automatically when the list pages.

modal?.open(slot);
modal?.close(slot);
modal?.isOpen(slot);
modal?.refresh(slot?);       // omit slot → every player who has it open
modal?.page(slot, delta);
modal?.select(slot, index);  // absolute; pages to it
modal?.cursor(slot);         // absolute index, same space as onPick
modal?.forget(slot);
modal?.release();

Put attacker-controlled text on the last detail line and escape it before it reaches the call.

Shared

  • hideAll(slot) — hide every pooled panel for one player.
  • forget(slot) — drop per-player state (disconnect).
  • ensure() — force-spawn the library layout. Optional once components() has run and a client is active.
  • hud — the underlying primitive, for anything the library does not cover.
  • budget() — intern counts for panelIds, classNames, and variables. Each vector is its own 1024 cap (not one shared 3072). Interning is idempotent; repaints are free. Past the cap a name is refused and its value never arrives.

See also

s2script — Source 2 plugin framework

GitHub