Skip to content

Commit

Permalink
feat: add/update message types & handling
Browse files Browse the repository at this point in the history
BREAKING CHANGE: update/rename message types (and message `type` IDs)

- add support to configure API via messages
  - add `ConfigureMessage` type and handling
- add `InfoMessage` and `GetInfoMessage` types & handling
- unify naming of all message types (i.e. `Msg` suffix => `Message`)
- update message names to be more reflective of usage
  - remove `set` from message types which are only informative
    - rename `genart:setparams` => `genart:params` (new type: `ParamsMessage`)
    - rename `genart:settraits` => `genart:traits` (new type: `TraitsMessage`)
- use hyphenation in message `type` IDs
  - e.g. `genart:paramchange` => `genart:param-change`
- add support for `*` wildcard `apiID` in messages
- add/update docs
  • Loading branch information
postspectacular committed Dec 1, 2024
1 parent 471c988 commit 35b627d
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 77 deletions.
8 changes: 4 additions & 4 deletions src/adapters/urlparams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type {
PRNG,
RampParam,
RangeParam,
ResizeMsg,
ResizeMessage,
RunMode,
ScreenConfig,
} from "../api.js";
Expand Down Expand Up @@ -38,7 +38,7 @@ class URLParamsAdapter implements PlatformAdapter {
this.params = new URLSearchParams(location.search);
this._screen = this.screen;
this.initPRNG();
$genart.on("genart:paramchange", async (e) => {
$genart.on("genart:param-change", async (e) => {
const value = await this.serializeParam(e.param);
this.params.set(e.paramID, value);
// (optional) send updated params to parent GUI for param editing
Expand All @@ -54,7 +54,7 @@ class URLParamsAdapter implements PlatformAdapter {
location.search = this.params.toString();
}
});
$genart.on("genart:statechange", ({ state }) => {
$genart.on("genart:state-change", ({ state }) => {
if (state === "ready" && this.params.get(AUTO) !== "0") {
$genart.start();
}
Expand All @@ -68,7 +68,7 @@ class URLParamsAdapter implements PlatformAdapter {
dpr !== newScreen.dpr
) {
this._screen = newScreen;
$genart.emit<ResizeMsg>({
$genart.emit<ResizeMessage>({
type: "genart:resize",
screen: newScreen,
});
Expand Down
62 changes: 43 additions & 19 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,17 @@ export interface GenArtAPIOpts {
*/
id: string;
/**
* If true (default), the API will emit a {@link AnimFrameMsg} for each
* If true, the API will accept {@link SetConfigMessage}s allowing the API
* behavior to be reconfigured by external tooling.
*
* @remarks
* For security reasons, this should only be enabled during development.
*
* @defaultValue false
*/
allowExternalConfig: boolean;
/**
* If true (default), the API will emit a {@link AnimFrameMessage} for each
* single frame update.
*
* @defaultValue true
Expand All @@ -72,10 +82,24 @@ export interface GenArtAPI {
* @remarks
* The ID will be part of any {@link APIMessage} sent and will also be
* checked by any `genart:...` message received. A message will only be
* processed if its {@link APIMessage.apiID} matches this value.
*
* The initial value is auto-generated, but it's recommended to set it at
* startup of the artwork (via {@link GenArtAPI.configure}).
* processed if its {@link APIMessage.apiID} matches this value or if equal
* to the string `"*""`, i.e. the wildcard catch-all ID, which will be
* matched by any active `GenArtAPI` instance.
*
* Use cases for the wildcard ID (`"*"`) are related to handling multiple
* artworks running in a page/app, regardless if they're sharing the same
* document or in multiple iframes, for example:
*
* - Detection/registration of all currently running `GenArtAPI` instances
* by broadcasting a {@link GetInfoMessage}, to which each instance then
* responds with a {@link InfoMessage} (which then also includes each
* instance's actual configured `id`)
* - Starting/stopping all currently running `GenArtAPI` instances via
* single message, e.g. `postMessage({ type: "genart:start", apiID: "*"
* }, "*")`.
*
* The initial ID value is auto-generated, but it's considered best practice
* to set it at startup of the artwork (via {@link GenArtAPI.configure}).
*/
readonly id: string;

Expand Down Expand Up @@ -113,7 +137,7 @@ export interface GenArtAPI {
* The API's current state.
*
* @remarks
* Also see {@link StateChangeMsg}.
* Also see {@link StateChangeMessage}.
*/
readonly state: APIState;

Expand Down Expand Up @@ -184,7 +208,7 @@ export interface GenArtAPI {
* platform-specific param handling and then calls
* {@link GenArtAPI.updateParams} to apply any param
* customizations/overrides sourced via the adapter. Finally, once done, it
* sends a {@link SetParamsMsg} message to the current & parent window for
* sends a {@link ParamsMessage} message to the current & parent window for
* other software components to be notified (e.g. param editors)
*
* The function returns a promise of a typesafe getter function (based on
Expand Down Expand Up @@ -285,7 +309,7 @@ export interface GenArtAPI {
* Iterates over all registered parameters and calls
* {@link PlatformAdapter.updateParam} and {@link GenArtAPI.setParamValue}
* to apply any param customizations/overrides sourced via the adapter. If
* `notify` is given, sends a {@link ParamChangeMsg} for each changed
* `notify` is given, sends a {@link ParamChangeMessage} for each changed
* param/value.
*
* @remarks
Expand All @@ -298,7 +322,7 @@ export interface GenArtAPI {

/**
* Updates the given param's value, or if `key` is specified one its nested
* params' value, then emits a {@link ParamChangeMsg} (depending on
* params' value, then emits a {@link ParamChangeMessage} (depending on
* `notify`, default: "all")
*
* @param id
Expand All @@ -318,14 +342,14 @@ export interface GenArtAPI {
* specified one its nested params. Only params which support randomization
* will be handled, otherwise silently ignored. If randomization succeeded,
* calls {@link GenArtAPI.setParamValue} to apply the new value and emit a
* {@link ParamChangeMsg} (depending on `notify`, default: "all").
* {@link ParamChangeMessage} (depending on `notify`, default: "all").
*
* @remarks
* The optional `rnd` function is passed to {@link ParamImpl.randomize} to
* produce a new random value. The default is `Math.random`.
*
* In the reference implementation of {@link GenArtAPI}, this function can
* also be triggered via a {@link RandomizeParamMsg}.
* also be triggered via a {@link RandomizeParamMessage}.
*
* @param id
* @param key
Expand Down Expand Up @@ -362,8 +386,8 @@ export interface GenArtAPI {
* that's the case and `rnd` is given, `getParamValue()` will produce a
* randomized value using {@link ParamImpl.randomize}, but this value is
* ephemeral and will NOT modify the param spec's `.value` or trigger a
* {@link RandomizeParamMsg} message being broadcast. If `rnd` is given but
* the param type does NOT support randomization, the param's value is
* {@link RandomizeParamMessage} message being broadcast. If `rnd` is given
* but the param type does NOT support randomization, the param's value is
* produced normally (see above).
*
* **Important: It's the artist's responsibility to ensure deterministic
Expand All @@ -382,7 +406,7 @@ export interface GenArtAPI {
): ParamValue<T[K]>;

/**
* Emits a {@link ParamErrorMsg} message (called from
* Emits a {@link ParamErrorMessage} message (called from
* {@link GenArtAPI.setParamValue}, if needed, but can be triggered by
* others too...)
*
Expand All @@ -401,7 +425,7 @@ export interface GenArtAPI {
* Usually these traits are derived from the random seed and currently
* configured parameters. The API will forward this object to
* {@link PlatformAdapter.setTraits} for platform-specific processing, but
* also emits a {@link SetTraitsMsg} message to the current & parent
* also emits a {@link TraitsMessage} message to the current & parent
* windows.
*
* @example
Expand Down Expand Up @@ -468,8 +492,8 @@ export interface GenArtAPI {
* @remarks
* If both platform adapter and time provider are already known, this will
* trigger the GenArtAPI to go into the `ready` state and emit a
* {@link StateChangeMsg} message. In most cases, a platform adapter should
* react to this message and call {@link GenArtAPI.start} to trigger
* {@link StateChangeMessage} message. In most cases, a platform adapter
* should react to this message and call {@link GenArtAPI.start} to trigger
* auto-playback of the artwork when `ready` state is entered.
*
* @param fn
Expand All @@ -486,14 +510,14 @@ export interface GenArtAPI {
* (re)initialized (otherwise just continues).
*
* Triggers the API to go into `play` state and emits a
* {@link StateChangeMsg}, as well as `genart:start` or `genart:resume`
* {@link StateChangeMessage}, as well as `genart:start` or `genart:resume`
* messages. Function is idempotent if API is already in `play` state.
*
* An error will be thrown if API is not in `ready` or `stop` state, i.e.
* the API must have a {@link PlatformAdapter}, a {@link TimeProvider} and a
* {@link UpdateFn} must have been configured.
*
* Whilst the animation loop is active, a {@link AnimFrameMsg} will be
* Whilst the animation loop is active, a {@link AnimFrameMessage} will be
* emitted at the end of each frame update. These messages contain the time
* & frame information of the currently rendered frame and are intended for
* 3rd party tooling (i.e. editors, players, sequencers).
Expand Down
109 changes: 83 additions & 26 deletions src/api/messages.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { GenArtAPIOpts } from "../api.js";
import type { NestedParam, NestedParamSpecs } from "./params.js";
import type { ScreenConfig } from "./screen.js";
import type { APIState } from "./state.js";
Expand All @@ -16,34 +17,38 @@ export interface APIMessage {
* see {@link GenArtAPI.id}.
*/
apiID: string;
/** @internal */
/**
* Flag used to indicate the message was emitted by the same instance.
*
* @internal
*/
__self?: boolean;
}

/**
* Message type emitted by {@link GenArtAPI.setTraits} to inform external
* tooling about artwork defined {@link Traits}.
*/
export interface SetTraitsMsg extends APIMessage {
type: "genart:settraits";
export interface TraitsMessage extends APIMessage {
type: "genart:traits";
traits: Traits;
}

/**
* Message type emitted at the end of {@link GenArtAPI.setParams} to inform
* external tooling about artwork defined {@link ParamSpecs}.
*/
export interface SetParamsMsg extends APIMessage {
type: "genart:setparams";
export interface ParamsMessage extends APIMessage {
type: "genart:params";
params: NestedParamSpecs;
}

/**
* Command message type received by {@link GenArtAPI} to remotely trigger
* {@link GenArtAPI.setParamValue}.
*/
export interface SetParamValueMsg extends APIMessage {
type: "genart:setparamvalue";
export interface SetParamValueMessage extends APIMessage {
type: "genart:set-param-value";
/**
* ID of parameter to update.
*/
Expand All @@ -62,8 +67,8 @@ export interface SetParamValueMsg extends APIMessage {
* Command message type received by {@link GenArtAPI} to remotely trigger
* {@link GenArtAPI.randomizeParamValue}.
*/
export interface RandomizeParamMsg extends APIMessage {
type: "genart:randomizeparam";
export interface RandomizeParamMessage extends APIMessage {
type: "genart:randomize-param";
/**
* ID of parameter to randomize.
*/
Expand All @@ -79,8 +84,8 @@ export interface RandomizeParamMsg extends APIMessage {
* Message type emitted by {@link GenArtAPI.setParamValue} when a parameter has
* been changed/updated.
*/
export interface ParamChangeMsg extends APIMessage {
type: "genart:paramchange";
export interface ParamChangeMessage extends APIMessage {
type: "genart:param-change";
param: NestedParam;
paramID: string;
/**
Expand All @@ -94,8 +99,8 @@ export interface ParamChangeMsg extends APIMessage {
* Message type emitted by {@link GenArtAPI.setParamValue} if the given value is
* not valid or the param couldn't be updated for any other reason.
*/
export interface ParamErrorMsg extends APIMessage {
type: "genart:paramerror";
export interface ParamErrorMessage extends APIMessage {
type: "genart:param-error";
paramID: string;
error?: string;
}
Expand All @@ -104,8 +109,8 @@ export interface ParamErrorMsg extends APIMessage {
* Message type emitted when the {@link GenArtAPI} internally switches into a
* new state. See {@link APIState} for details.
*/
export interface StateChangeMsg extends APIMessage {
type: "genart:statechange";
export interface StateChangeMessage extends APIMessage {
type: "genart:state-change";
/**
* New API state
*/
Expand All @@ -121,7 +126,7 @@ export interface StateChangeMsg extends APIMessage {
* change occurred and the artwork (or 3rd party tooling) should respond/adapt
* to these new dimensions provided.
*/
export interface ResizeMsg extends APIMessage {
export interface ResizeMessage extends APIMessage {
type: "genart:resize";
/**
* New screen/canvas configuration
Expand All @@ -135,7 +140,7 @@ export interface ResizeMsg extends APIMessage {
* of the currently rendered frame and is intended for 3rd party tooling (i.e.
* editors, players, sequencers).
*/
export interface AnimFrameMsg extends APIMessage {
export interface AnimFrameMessage extends APIMessage {
type: "genart:frame";
/**
* Current animation time (in seconds)
Expand Down Expand Up @@ -175,25 +180,77 @@ export interface StopMessage extends APIMessage {
type: "genart:stop";
}

/**
* Message type sent when {@link GenArtAPI.configure} is called or a
* {@link ConfigureMessage} or {@link GetInfoMessage} is received by the API.
* Includes all current config options, API state, timing info,
* {@link GenArtAPI.version} and more.
*/
export interface InfoMessage extends APIMessage {
type: "genart:info";
opts: GenArtAPIOpts;
state: APIState;
version: string;
/**
* Current animation time (in milliseconds). See {@link TimeProvider.now}.
*/
time: number;
/**
* Current animation frame number. See {@link TimeProvider.now}.
*/
frame: number;
/**
* Random seed used by this instance's {@link PRNG}.
*/
seed: string;
}

/**
* Command message type received by {@link GenArtAPI} to trigger an
* {@link InfoMessage} being sent in response.
*/
export interface GetInfoMessage extends APIMessage {
type: "genart:get-info";
}

/**
* Command message type received by {@link GenArtAPI}. Only if the
* {@link GenArtAPIOpts.allowExternalConfig} option is enabled, the message
* payload's options are passed to {@link GenArtAPI.configure}, which then
* results in a {@link InfoMessage} being sent in response.
*
* @remarks
* For security reasons, the {@link GenArtAPIOpts.id} and
* {@link GenArtAPIOpts.allowExternalConfig} options cannot be changed
* themselves using this mechanism.
*/
export interface ConfigureMessage extends APIMessage {
type: "genart:configure";
opts: Partial<Omit<GenArtAPIOpts, "id">>;
}

/**
* LUT mapping message types (names) to their respective type of API message.
* Used for type checking/inference in {@link GenArtAPI.on}.
*/
export interface MessageTypeMap {
"genart:capture": CaptureMessage;
// "genart:capturerequest": APIMessage;
"genart:paramchange": ParamChangeMsg;
"genart:paramerror": ParamErrorMsg;
"genart:randomizeparam": RandomizeParamMsg;
"genart:frame": AnimFrameMsg;
"genart:resize": ResizeMsg;
"genart:configure": ConfigureMessage;
"genart:frame": AnimFrameMessage;
"genart:get-info": GetInfoMessage;
"genart:info": InfoMessage;
"genart:param-change": ParamChangeMessage;
"genart:param-error": ParamErrorMessage;
"genart:randomize-param": RandomizeParamMessage;
"genart:resize": ResizeMessage;
"genart:resume": ResumeMessage;
"genart:setparams": SetParamsMsg;
"genart:setparamvalue": SetParamValueMsg;
"genart:settraits": SetTraitsMsg;
"genart:params": ParamsMessage;
"genart:set-param-value": SetParamValueMessage;
"genart:start": StartMessage;
"genart:statechange": StateChangeMsg;
"genart:state-change": StateChangeMessage;
"genart:stop": StopMessage;
"genart:traits": TraitsMessage;
}

/**
Expand Down
Loading

0 comments on commit 35b627d

Please sign in to comment.