msw-ui-system
MSW `.ui` single entry point — design + component API + builder + runtime. Anchor/pivot/RectTransform, UIGroup/CanvasGroup hierarchy, layout recipes (HUD/popup/toast/menu/inventory/scroll-list), full API tables for ButtonComponent/TextGUIRendererComponent/SpriteGUIRendererComponent/ScrollLayoutGroup/GridView/TextInput/Slider/Mask/AvatarGUIRenderer + UI enums (AlignmentType/TextOverflowMode/ImageType/FillAmount), `.mlua` runtime patterns (popup open-close, toast, HP bar, GridView, drag, tab, cooldown, world nametag), UI-client-only caveats (nil on server, no RPC), `.ui`↔`.mlua` UUID auto-binding (write+injectBindings), resolution/safe-area/touch. UIBuilder (msw_ui_builder.cjs): all node types (empty/panel/text/sprite/button/slider/scrollLayout/textInput/group/mask/gridView/avatar/skeleton etc.), component add/replace/patch/remove, 13 anchor presets+stretch, auto-inject .mlua UUID bindings after write.
Other options
Summary
MSW `.ui` single entry point — design + component API + builder + runtime. Anchor/pivot/RectTransform, UIGroup/CanvasGroup hierarchy, layout recipes (HUD/popup/toast/menu/inventory/scroll-list), full API tables for ButtonComponent/TextGUIRendererComponent/SpriteGUIRendererComponent/ScrollLayoutGroup/GridView/TextInput/Slider/Mask/AvatarGUIRenderer + UI enums (AlignmentType/TextOverflowMode/ImageType/FillAmount), `.mlua` runtime patterns (popup open-close, toast, HP bar, GridView, drag, tab, cooldown, world nametag), UI-client-only caveats (nil on server, no RPC), `.ui`↔`.mlua` UUID auto-binding (write+injectBindings), resolution/safe-area/touch. UIBuilder (msw_ui_builder.cjs): all node types (empty/panel/text/sprite/button/slider/scrollLayout/textInput/group/mask/gridView/avatar/skeleton etc.), component add/replace/patch/remove, 13 anchor presets+stretch, auto-inject .mlua UUID bindings after write.
Raw SKILL.md
12.1K bytes---
name: msw-ui-system
description: "MSW `.ui` single entry point — design + component API + builder + runtime. Anchor/pivot/RectTransform, UIGroup/CanvasGroup hierarchy, layout recipes (HUD/popup/toast/menu/inventory/scroll-list), full API tables for ButtonComponent/TextGUIRendererComponent/SpriteGUIRendererComponent/ScrollLayoutGroup/GridView/TextInput/Slider/Mask/AvatarGUIRenderer + UI enums (AlignmentType/TextOverflowMode/ImageType/FillAmount), `.mlua` runtime patterns (popup open-close, toast, HP bar, GridView, drag, tab, cooldown, world nametag), UI-client-only caveats (nil on server, no RPC), `.ui`↔`.mlua` UUID auto-binding (write+injectBindings), resolution/safe-area/touch. UIBuilder (msw_ui_builder.cjs): all node types (empty/panel/text/sprite/button/slider/scrollLayout/textInput/group/mask/gridView/avatar/skeleton etc.), component add/replace/patch/remove, 13 anchor presets+stretch, auto-inject .mlua UUID bindings after write."
---
# msw-ui-system
MSW `.ui` single entry point — **design guide + component API + builder invocation + runtime patterns** bundled into one skill.
Role division with existing skills:
| Skill | Responsibility |
|-------|----------------|
| `msw-ui-system` (this skill) | Everything `.ui` — design (which/when/why), component API/enum (what), builder invocation (how to mutate), runtime mlua patterns. **`.ui` mutations must always go through this skill's builder** |
| `references/templates/` | Pre-built style bundles — `.ui` + ruid-map + button handler packages |
---
## 0. Routing
Branch to sub-references based on request keywords.
| Trigger | Reference Document |
|---------|--------------------|
| "anchor/pivot/coordinates/why is the position wrong", "RectTransform", "stretch" | [`references/ui-fundamentals.md`](references/ui-fundamentals.md) §1–§8 |
| "mobile", "safe area", "1920", "MobileOnly", "ActivePlatform", "touch size", "PC reserved zone", "font size by device" | [`references/ui-fundamentals.md`](references/ui-fundamentals.md) §9 |
| "UIGroup", "above popup", "z-order", "displayOrder", "CanvasGroup", "opacity propagation", "Enable vs Visible" | [`references/ui-hierarchy.md`](references/ui-hierarchy.md); for runtime sibling reorder also read [`references/runtime-patterns.md`](references/runtime-patterns.md) §7 |
| "which component", "Sprite vs Text vs Button", "9-slice", "scroll list", "GridView vs ScrollLayoutGroup" | [`references/component-api.md`](references/component-api.md) §"Component Selection Guide" |
| "sprite pivot", "9-slice border", "slice boundary", "set sliced asset metadata", "resource storage properties" | Set asset-side metadata via `msw-mcp` `asset_update_resource_storage_info` directly; for the `.ui` side see [`references/component-api.md`](references/component-api.md) §"SpriteGUIRenderer — ImageType Selection" |
| "make a HUD", "popup placement", "toast", "menu", "inventory grid", "scroll list" | [`references/layout-recipes.md`](references/layout-recipes.md) |
| "connect .mlua after building with .ui builder", "property default UUID", "binding without drag" | [`../msw-general/references/builder-protocol-ui.md`](../msw-general/references/builder-protocol-ui.md) §3.6 Binding Injection (unified entry point — load with the [`builder-protocol.md`](../msw-general/references/builder-protocol.md) core) |
| Runtime UI component field read/write, component property name/type (`ButtonComponent.Colors`, `TextGUIRendererComponent.Overflow`, `SpriteGUIRendererComponent.FillAmount`…) | [`references/component-api.md`](references/component-api.md) **required before every `.mlua` access to UI component fields** |
| Enum values (`AlignmentType`, `TextOverflowMode`, `ImageType`, `UIBasicParticleType`…) | [`references/component-api.md`](references/component-api.md) §Enums |
| Runtime mlua patterns (popup open/close, toast fade, HP bar, GridView, drag, tab, cooldown), Runtime UI Caveats (client-only, server-side nil, etc.) | [`references/runtime-patterns.md`](references/runtime-patterns.md) |
| **`.ui` builder invocation methods** (UIBuilder API, anchor presets, write auto-lint, component add/patch/remove) | [`../msw-general/references/builder-protocol-ui.md`](../msw-general/references/builder-protocol-ui.md) §3 UIBuilder (unified entry point — load with the [`builder-protocol.md`](../msw-general/references/builder-protocol.md) core; `.map` MapBuilder / `.model` ModelBuilder live in sibling per-builder files) |
| "sound", "sfx", "click sound", "hover sound", "button audio", "PlaySound" | [`references/ui-sound.md`](references/ui-sound.md) |
---
## 1. Basic Workflow
```
(1) Clarify intent Layout sketch (ASCII or verbal) + which group to attach to
(2) Check design guide Match at least one of ui-fundamentals / ui-hierarchy / component-api §Component Selection Guide
(3) Builder Preflight Read ../msw-general/references/builder-protocol.md (core) + builder-protocol-ui.md §3 (unified call-protocol entry point)
(4) Match recipe Select the closest template from layout-recipes.md
(5) Invoke builder Create/patch via scripts/msw_ui_builder.cjs (protocol: builder-protocol-ui.md §3)
(6) Inject bindings Auto-inject .mlua property default UUIDs via b.write(path, { bind: {...} }) or b.injectBindings(...) (builder-protocol-ui.md §3.6 Binding Injection)
(7) Self-verify write() auto-runs scripts/ui_lint.cjs (strict ON by default)
(8) Preview Visual check via scripts/preview_ui_layout.cjs
(9) Sound pass For any interactive button, offer click/hover SFX wiring (references/ui-sound.md)
(10) Maker Refresh Apply to engine
```
## 2. Global Rules
### NEVER
1. **Do not directly edit `.ui` JSON** — `.ui` creation/modification **must** go through `scripts/msw_ui_builder.cjs`. Manual editing breaks UUID·ValueType·`@components` consistency and causes silent drops.
2. **Read existing `.ui` files through the builder too** — Query via `UIBuilder.read(filepath)` / `.find()` / `.listEntities()`. Do not directly grep/parse raw JSON.
- `.ui` direct `Read` and shell commands such as `cat` / `type` / `Get-Content` / `rg` / `grep` / `sed` / `awk` / `cp` / `mv` are blocked by the registered guard. Use `UIBuilder.read/load/snapshot` for reads and `b.write()` for writes. Deleting an entire `.ui` file has no builder API — delete it with `node -e "require('fs').unlinkSync('ui/<File>.ui')"` then `refresh` (shell `rm`/`cat` are guard-blocked; a `node -e` builder call is not).
3. Set `Position` directly — Use only `anchoredPosition` (Position is engine-managed)
4. Express size via OffsetMin/Max on fixed anchors (AnchorsMin == AnchorsMax) while also using `anchoredPosition` — Do not mix the two modes
5. Builder creates new UUIDs but `.mlua` property defaults are not updated — Binding breaks
### ALWAYS
1. **Builder Protocol Preflight — [`../msw-general/references/builder-protocol.md`](../msw-general/references/builder-protocol.md) (core) + [`../msw-general/references/builder-protocol-ui.md`](../msw-general/references/builder-protocol-ui.md) §3 must be in context before any `.ui` mutation** (read them only if never loaded this session or lost to compaction) (UIBuilder API, write auto-lint, pos / anchor rules, binding injection, coverage gaps). The core carries the shared contract and cross-builder flow; `.map` MapBuilder / `.model` ModelBuilder live in sibling per-builder files — one unified entry point because the cross-flow is interlocked.
2. Check at least one design guide before invoking the builder (`ui-fundamentals` / `ui-hierarchy` / `component-api` §Component Selection Guide)
3. Match a recipe first; build from scratch only as a last resort
4. For edge placement use the formula: `pos = ±(margin + size/2)`
5. Separate popups and toasts into their **own `.ui` root UIGroup**, standalone show/hide; use `empty()` / `panel()` for inner containers, never nested `group()`
6. Verify text `Alignment` default is `UpperLeft(0)` — 95% of "I centered it but it sticks to the left" issues
7. Button touch target ≥ 88×88 (mobile support)
8. **After creating any interactive button** — proactively suggest wiring click/hover SFX via [`references/ui-sound.md`](references/ui-sound.md) (default UI SFX RUIDs available). Skip only if the user explicitly opts out or the button is purely decorative.
9. **Build the tree nested, not flat.** Controls of one unit (window + title/close, row + chip/value, slot + icon/count) must share a parent via `"Parent/Child"` paths so they move / fade / toggle / bind as a block. Create each parent before its children; missing parents fail lint (`L025` ERROR).
10. **Do not stack root-level text over a sibling box.** `ui_lint` reports this as `L030` WARN. Nest the text under the box, use `button()` for clickable labeled boxes, or put a direct label on `panel()` / `sprite()` via their `text` options.
---
## 3. Sub-documents
- [`references/ui-fundamentals.md`](references/ui-fundamentals.md) — Coordinate system, RectTransform 3 elements, anchor mode determination (§1–§8) + Resolution·safe area·PC reserved zones·touch targets·font sizes·platform separation (§9)
- [`references/ui-hierarchy.md`](references/ui-hierarchy.md) — UIGroup / displayOrder / CanvasGroup / Enable vs Visible
- [`references/component-api.md`](references/component-api.md) — §"Component Selection Guide" (which/when/why) + full component property/method/event tables (what) + all UI-related enum values (§Enums)
- [`references/layout-recipes.md`](references/layout-recipes.md) — Layout template collection
- [`references/runtime-patterns.md`](references/runtime-patterns.md) — `.mlua` runtime patterns (popup/toast/HP/grid/drag…) + Runtime UI Caveats
- [`references/ui-sound.md`](references/ui-sound.md) — UI sound integration (`_SoundService:PlaySound`, click/hover hook, default UI SFX RUIDs)
- [`../msw-general/references/builder-protocol-ui.md`](../msw-general/references/builder-protocol-ui.md) §3 — **`.ui` CJS builder call protocol (unified entry point — load with the [`builder-protocol.md`](../msw-general/references/builder-protocol.md) core)** — `.map` MapBuilder / `.model` ModelBuilder live in sibling per-builder files. panel / text / sprite / button / slider / scroll / script / group / mask / grid / avatar / touchReceive / skeleton / areaParticle / basicParticle, component CRUD, anchor presets, write auto-lint, and `.mlua` property UUID auto-binding all live in §3 + §3.6.
- [`references/templates/templates.md`](references/templates/templates.md) — Pre-built style bundle index (`style-N-*` `.ui`, [`ruid-map.md`](references/templates/style-1-black/ruid-map.md), `Popupbutton.mlua`)
## 4. Scripts
- `scripts/msw_ui_builder.cjs` — `.ui` builder core (UIBuilder class). Read [`../msw-general/references/builder-protocol.md`](../msw-general/references/builder-protocol.md) (core) + [`../msw-general/references/builder-protocol-ui.md`](../msw-general/references/builder-protocol-ui.md) §3 (unified entry point) before use.
- `scripts/preview_ui_layout.cjs` — `.ui` layout visual check + touch target warnings
- `scripts/ui_lint.cjs` — `.ui` file self-verification (auto-called by `write()`)
- `scripts/ui_recipe.cjs` — Recipe-based scaffolding
---
## Out of Scope
- `.map` / `.model` / `.tileset` builders — Outside this skill's scope
- `.ui` JSON schema (raw field shapes, `@type`/`@components` wrapping, AlignmentOption 0–15 mapping, etc.) — Handled internally by the builder. Users/AI do not need to know directly
- Accessibility patterns (alt text, screen-reader hints, focus order) — Not covered
- Error-state UI patterns (disabled-button styling beyond `Transition.Disabled`, validation messages, loading spinners) — Not covered; design ad-hoc per project
- Automated UI testing / layout assertions beyond `ui_lint.cjs` and `preview_ui_layout.cjs` — Not provided
- Custom shader materials (`MaterialId`) — Field is exposed but authoring shaders is outside this skill's scope

