Skip to content

Legacy Scripting Reference

This documentation is for the legacy scripting approach. New projects should use the modern decorator-based system. See Migrating from Legacy Scripting for upgrade instructions.

This page documents the older JSDoc annotation-based scripting approach for reference.

Behaviors in the legacy system use JSDoc annotations and manual event registration:

import { Behavior, ContextManager, useOnBeforeRender } from "@zcomponent/core";
import { Box } from "@zcomponent/three/lib/components/meshes/Box";
import { default as Scene } from "./Scene.zcomp";
interface ConstructionProps {
// Constructor props here
}
/** @zbehavior */
export class MyBehavior extends Behavior<Box> {
protected zcomponent = this.getZComponentInstance(Scene);
constructor(
contextManager: ContextManager,
instance: Box,
protected constructorProps: ConstructionProps
) {
super(contextManager, instance);
// Register event handlers
this.register(this.instance.onClick, evt => {
console.log('Clicked!');
});
// Frame loop
this.register(useOnBeforeRender(contextManager), dt => {
// Called every frame
});
}
dispose() {
return super.dispose();
}
}
// Run at design time
registerBehaviorRunAtDesignTime(MyBehavior);

Components in the legacy system use JSDoc annotations:

import { ContextManager, registerLoadable } from "@zcomponent/core";
import { Group } from "@zcomponent/three/lib/components/Group";
import * as THREE from "three";
interface ConstructorProps {}
/**
* @zcomponent
* @zicon favorite
*/
export class CustomComponent extends Group {
constructor(contextManager: ContextManager, constructorProps: ConstructorProps) {
super(contextManager, constructorProps);
registerLoadable(contextManager, this._load());
}
private async _load() {
const mesh = new THREE.Mesh(
new THREE.SphereGeometry(),
new THREE.MeshBasicMaterial()
);
this.element.add(mesh);
}
}

Contexts share state across components and behaviors:

import { Context, ContextManager, Observable } from "@zcomponent/core";
interface ConstructionProps {}
/** @zcontext */
export class ScoreContext extends Context<ConstructionProps> {
public currentScore = new Observable<number>(0);
constructor(contextManager: ContextManager, constructorProps: ConstructionProps) {
super(contextManager, constructorProps);
}
}
// Hook for accessing the score
export function useCurrentScore(contextManager: ContextManager) {
return contextManager.get(ScoreContext).currentScore;
}

Properties were defined using JSDoc annotations:

/**
* @zcomponent
*/
export class MyComponent extends Group {
/**
* @zui
* @zdefault 0
* @zgroup Appearance
* @ztype proportion
*/
public metalness = new Observable(0);
constructor(contextManager: ContextManager, constructorProps: ConstructorProps) {
super(contextManager, constructorProps);
this.register(this.metalness, value => {
// React to changes
});
}
}
Annotation Description
@zui Expose property in editor
@zdefault value Default value
@zgroup Name Group in properties panel
@ztype proportion Slider 0-1
@ztype angle-degrees Angle input
@ztype color-norm-rgb Color picker
@zvalues files *.png File picker

The frame loop in the legacy system used useOnBeforeRender:

import { useOnBeforeRender } from "@zcomponent/core";
/** @zbehavior */
export class RotateBehavior extends Behavior<Box> {
constructor(contextManager: ContextManager, instance: Box, constructorProps: ConstructionProps) {
super(contextManager, instance);
this.register(useOnBeforeRender(contextManager), dt => {
this.instance.element.rotation.y += 0.01 * dt;
});
}
}

The modern decorator-based system offers several advantages:

  • Cleaner syntax - Decorators are more readable than JSDoc comments
  • Better type safety - TypeScript 5 decorators provide improved type inference
  • Less boilerplate - No need for manual register() calls
  • Better tooling - Enhanced autocomplete and error detection

See Migrating from Legacy Scripting for step-by-step upgrade instructions.