Create, place, preview, debug, and connect manifest-backed IWSDK UIKitML assets. Use for spatial panels, browser HUDs, UIKitML layout, fonts, editor previews, or runtime element behavior.
75
94%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Treat UIKitML as a first-class renderable asset. Use the managed editor's isolated renderer for layout, the scene editor for placement, and the application runtime for behavior. Do not change the scene, camera, or component model merely to inspect a panel.
Every scene-authored panel has three different identities:
id attributes, which name fields and controls inside the document.Find or add the manifest entry in src/assets.ts and keep the source under
public/ui/:
import { AssetType, type AssetManifest } from '@iwsdk/core';
const assets = {
'welcome-panel': {
name: 'Welcome panel',
type: AssetType.UIKitML,
url: '/ui/welcome.uikitml',
},
} satisfies AssetManifest;
export default assets;A file under public/ui/ is not placeable until it is registered. The .uikitml
file is the runtime source of truth; never generate an intermediate JSON file.
Run commands from the application directory. Reuse a command-ready session instead of starting a second server or browser:
npx @iwsdk/cli dev status
npx @iwsdk/cli dev up --timeout 60000
npx @iwsdk/cli ui assets --rawdev up owns the managed headed browser. Do not launch a second Playwright browser,
enable Vite's independent opener, or implement a custom UIKit renderer.
Use the editor-backed isolated renderer before debugging scene placement:
npx @iwsdk/cli ui render-preview \
--input-json '{"assetId":"welcome-panel","width":800,"height":600}' \
--output-file artifacts/welcome-panel.pngThis renders the real manifest entry through the real UIKitML parser, fonts, layout, and WebGL path on a neutral background. It waits for UIKit render and resource signals; do not add fixed frame delays. Re-run the command after every layout change. Same-URL sources are reloaded without restarting the server.
For a human-authored placement, open the scene and add the UIKitML asset from the asset drawer. For file authoring, use the same asset-only node contract:
{
"id": "welcome-panel-instance",
"name": "Welcome panel",
"content": { "type": "asset", "asset": "welcome-panel" },
"transform": {
"position": [0, 1.4, -1.5],
"rotationDeg": [0, 180, 0],
"scale": 0.5
}
}npx @iwsdk/cli scene render-file \
--input-json '{"path":"public/scenes/main.iwsdk.scene.json","view":"quarter"}' \
--output-file artifacts/main-editor.png
npx @iwsdk/cli scene open \
--input-json '{"path":"public/scenes/main.iwsdk.scene.json"}' --rawUIKitML surfaces are single-sided. If the isolated preview works but the placed panel
is invisible, inspect its rotation first. Size a world-space panel with its ordinary
entity transform scale. There is no maxWidth/maxHeight fitting layer and no
editor-visible PanelUI component to configure.
Use scene screenshot for editor/scene evidence. browser screenshot is intentionally
runtime-only and switches the managed workspace to runtime before capturing.
UIKitML is HTML/CSS-shaped, but it is not a browser engine. Confirm supported elements and properties from the installed IWSDK reference before assuming browser behavior.
Key rules:
100 = 100 cm = 1 m);id attributes for elements application code must manipulate;After each edit:
ui render-preview and inspect the image;ecs find namePattern, then
use ui inspect with its entity index and a stable element ID to verify current
runtime text, state, and layout; filter by PanelDocument only for legacy PanelUI;browser logs for parser, font, texture, or runtime failures.For a code-owned instance, keep the UIKitMLAsset returned by the asset manager:
import { UIKit, UIKitMLAsset } from '@iwsdk/core';
const panel = await world.assets.instantiate<UIKitMLAsset>('welcome-panel');
const entity = world.createTransformEntity(panel);
const button = panel.requireElementById<UIKit.Text>('xr-button');
button.addEventListener('click', () => world.launchXR());For a scene-authored instance, resolve it by stable scene node ID after the level has loaded:
const panel = world.requireSceneObject<UIKitMLAsset>('welcome-panel-instance');
const button = panel.requireElementById<UIKit.Text>('xr-button');
button.setProperties({ text: 'Enter XR' });Do not locate a panel by transient ECS index, manifest URL, or a scan for an internal
PanelDocument. Use the scene node ID and element IDs.
Add ScreenSpace only when the experience genuinely needs a browser-camera HUD:
entity.addComponent(ScreenSpace, {
width: '420px',
height: '240px',
top: '20px',
right: '20px',
zOffset: 0.2,
});Position and size values are CSS strings. In immersive mode the document returns to
its authored world transform. Do not add ScreenSpace, a backdrop, or a temporary
camera pose merely to make a panel easier to inspect—the editor already provides the
correct isolated and in-scene render paths.
For immersive XR, default to a world-space panel or attach contextual UI to the
object it controls. Use a thresholded Follower targeting world.player.head
only when compact global UI must remain discoverable. Do not head-lock menus,
reading surfaces, or persistent panels; reserve direct world.playerHeadEntity
parenting for tiny, transient, non-interactive markers that require exact view
alignment. Do not use ScreenSpace expecting it to remain camera-attached in
XR. See docs/concepts/spatial-ui/hud.md for the placement guidance.
ui assets lists it.ui inspect confirms the expected live element state after interaction.0778f51
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.