mirror of
https://github.com/litruv/lit.ruv.wtf.git
synced 2026-07-24 10:46:03 +10:00
update to node site
This commit is contained in:
40
website/scripts/nodes/BindEventNode.js
Normal file
40
website/scripts/nodes/BindEventNode.js
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @file BindEventNode.js
|
||||
* @UCLASS(BlueprintType, Blueprintable)
|
||||
*/
|
||||
|
||||
import { NodeBase } from './NodeBase.js';
|
||||
import { NodeRegistry } from './NodeRegistry.js';
|
||||
|
||||
/**
|
||||
* @UCLASS(BlueprintType)
|
||||
* Binds the OnMessage event on a ChatNode. When a message arrives, updates
|
||||
* the username/message output pins and fires the event_out exec pin.
|
||||
*/
|
||||
export class BindEventNode extends NodeBase {
|
||||
/** @UCLASS(BlueprintType) */
|
||||
static NodeType = "bind_event";
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintCallable)
|
||||
* @param {import('./NodeExecutionContext.js').NodeExecutionContext} ctx
|
||||
*/
|
||||
async BlueprintCallable_Execute(ctx) {
|
||||
const chatInstance = ctx.BlueprintPure_ResolveInput("target");
|
||||
const node = ctx.BlueprintPure_GetNode();
|
||||
|
||||
if (!chatInstance || typeof chatInstance.setOnMessage !== "function" || !node) return;
|
||||
|
||||
chatInstance.setOnMessage((username, message) => {
|
||||
const usernamePin = node.outputs.find(p => p.id === "username");
|
||||
const messagePin = node.outputs.find(p => p.id === "message");
|
||||
if (usernamePin) usernamePin.defaultValue = username;
|
||||
if (messagePin) messagePin.defaultValue = message;
|
||||
|
||||
// Fire event_out — async, fire-and-forget
|
||||
ctx.BlueprintCallable_RunExecPin("event_out").catch(console.error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
NodeRegistry.UCLASS_Register(BindEventNode);
|
||||
43
website/scripts/nodes/ButtonNode.js
Normal file
43
website/scripts/nodes/ButtonNode.js
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @file ButtonNode.js
|
||||
* @UCLASS(BlueprintType, Blueprintable)
|
||||
*/
|
||||
|
||||
import { NodeBase } from './NodeBase.js';
|
||||
import { NodeRegistry } from './NodeRegistry.js';
|
||||
|
||||
/**
|
||||
* @UCLASS(BlueprintType)
|
||||
* Renders a play button in the header that triggers node execution.
|
||||
*/
|
||||
export class ButtonNode extends NodeBase {
|
||||
/** @UCLASS(BlueprintType) */
|
||||
static NodeType = "button";
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintNativeEvent)
|
||||
* @param {HTMLElement} article
|
||||
* @param {import('../GraphNode.js').GraphNode} graphNode
|
||||
* @param {import('./NodeRenderContext.js').NodeRenderContext} renderCtx
|
||||
*/
|
||||
BlueprintNativeEvent_OnRender(article, graphNode, renderCtx) {
|
||||
const header = article.querySelector(".node-header");
|
||||
if (!header) return;
|
||||
|
||||
const btn = document.createElement("button");
|
||||
btn.className = "node-run-btn";
|
||||
btn.title = "Execute";
|
||||
btn.innerHTML = `<svg width="9" height="9" viewBox="0 0 9 9" fill="currentColor" aria-hidden="true"><polygon points="1,0 9,4.5 1,9"/></svg>`;
|
||||
|
||||
btn.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
renderCtx.BlueprintCallable_TriggerNodeExecute(graphNode.id);
|
||||
btn.classList.add("node-run-btn--flash");
|
||||
btn.addEventListener("animationend", () => btn.classList.remove("node-run-btn--flash"), { once: true });
|
||||
});
|
||||
|
||||
header.appendChild(btn);
|
||||
}
|
||||
}
|
||||
|
||||
NodeRegistry.UCLASS_Register(ButtonNode);
|
||||
29
website/scripts/nodes/ChatConnectNode.js
Normal file
29
website/scripts/nodes/ChatConnectNode.js
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* @file ChatConnectNode.js
|
||||
* @UCLASS(BlueprintType, Blueprintable)
|
||||
*/
|
||||
|
||||
import { NodeBase } from './NodeBase.js';
|
||||
import { NodeRegistry } from './NodeRegistry.js';
|
||||
|
||||
/**
|
||||
* @UCLASS(BlueprintType)
|
||||
* Calls connect() on a ChatNode instance received via the target input.
|
||||
*/
|
||||
export class ChatConnectNode extends NodeBase {
|
||||
/** @UCLASS(BlueprintType) */
|
||||
static NodeType = "chat_connect";
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintCallable)
|
||||
* @param {import('./NodeExecutionContext.js').NodeExecutionContext} ctx
|
||||
*/
|
||||
async BlueprintCallable_Execute(ctx) {
|
||||
const chatInstance = ctx.BlueprintPure_ResolveInput("target");
|
||||
if (chatInstance && typeof chatInstance.connect === "function") {
|
||||
chatInstance.connect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NodeRegistry.UCLASS_Register(ChatConnectNode);
|
||||
49
website/scripts/nodes/CustomEventNode.js
Normal file
49
website/scripts/nodes/CustomEventNode.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* @file CustomEventNode.js
|
||||
* @UCLASS(BlueprintType, Blueprintable)
|
||||
*/
|
||||
|
||||
import { NodeBase } from './NodeBase.js';
|
||||
import { NodeRegistry } from './NodeRegistry.js';
|
||||
|
||||
const HAND_SVG = `<svg viewBox="0 0 106.17 122.88" fill="currentColor"><path d="M29.96,67.49c-0.16-0.09-0.32-0.19-0.47-0.31c-1.95-1.56-4.08-3.29-5.94-4.81c-2.69-2.2-5.8-4.76-7.97-6.55 c-1.49-1.23-3.17-2.07-4.75-2.39c-1.02-0.2-1.95-0.18-2.67,0.12c-0.59,0.24-1.1,0.72-1.45,1.48c-0.45,0.99-0.66,2.41-0.54,4.32 c0.11,1.69,0.7,3.55,1.48,5.33c1.16,2.63,2.73,5.04,3.89,6.59c0.07,0.09,0.13,0.19,0.19,0.29l23.32,33.31 c0.3,0.43,0.47,0.91,0.53,1.4l0.01,0c0.46,3.85,1.28,6.73,2.49,8.54c0.88,1.31,2.01,1.98,3.42,1.94l0.07,0v-0.01h36.38 c0.09,0,0.17,0,0.26,0.01c2.28-0.03,4.36-0.71,6.25-2.02c2.09-1.44,3.99-3.68,5.72-6.7c0.03-0.05,0.06-0.11,0.1-0.16 c0.67-1.15,1.55-2.6,2.41-4.02c3.72-6.13,6.96-11.45,7.35-19.04L99.8,74.34c-0.02-0.15-0.03-0.3-0.03-0.45 c0-0.14,0.02-1.13,0.03-2.46c0.09-6.92,0.19-15.48-6.14-16.56h-4.05l-0.04,0c-0.02,1.95-0.15,3.93-0.27,5.86 c-0.11,1.71-0.21,3.37-0.21,4.95c0,1.7-1.38,3.08-3.08,3.08c-1.7,0-3.08-1.38-3.08-3.08c0-1.58,0.12-3.42,0.24-5.33 c0.41-6.51,0.89-13.99-4.33-14.93H74.8c-0.23,0-0.45-0.02-0.66-0.07c0.04,2.36-0.12,4.81-0.27,7.16c-0.11,1.71-0.21,3.37-0.21,4.95 c0,1.7-1.38,3.08-3.08,3.08c-1.7,0-3.08-1.38-3.08-3.08c0-1.58,0.12-3.42,0.24-5.33c0.41-6.51,0.89-13.99-4.33-14.93h-4.05 c-0.28,0-0.55-0.04-0.8-0.11V49c0,1.7-1.38,3.08-3.08,3.08c-1.7,0-3.08-1.38-3.08-3.08V17.05c0-5.35-2.18-8.73-4.97-10.14 c-1.02-0.52-2.12-0.78-3.21-0.78c-1.08,0-2.18,0.26-3.19,0.77c-2.76,1.4-4.92,4.79-4.92,10.28v56c0,1.7-1.38,3.08-3.08,3.08 c-1.7,0-3.08-1.38-3.08-3.08V67.49L29.96,67.49z M58.57,31.15c0.26-0.07,0.53-0.11,0.8-0.11h4.24c0.24,0,0.47,0.03,0.69,0.08 c5.65,0.88,8.17,4.18,9.2,8.43c0.39-0.18,0.83-0.29,1.3-0.29h4.24c0.24,0,0.47,0.03,0.69,0.08c6.08,0.94,8.53,4.69,9.41,9.41 c0.15-0.02,0.31-0.04,0.47-0.04h4.24c0.24,0,0.47,0.03,0.69,0.08c11.64,1.8,11.5,13.37,11.38,22.71c0,0.33-0.01,0.68-0.01,2.35 l0,0.07l0.24,10.77c0.01,0.11,0.01,0.23,0,0.34c-0.45,9.16-4.07,15.12-8.24,21.98c-0.7,1.14-1.41,2.32-2.34,3.93 c-0.02,0.04-0.04,0.08-0.07,0.13c-2.18,3.8-4.7,6.71-7.57,8.69c-2.92,2.02-6.16,3.06-9.71,3.1c-0.09,0.01-0.19,0.01-0.28,0.01 H41.58v-0.01c-3.66,0.07-6.5-1.53-8.59-4.66c-1.68-2.51-2.79-6.03-3.4-10.47L6.73,75.07c-0.03-0.04-0.07-0.08-0.1-0.12 c-1.36-1.82-3.21-4.65-4.59-7.79C1,64.8,0.21,62.24,0.05,59.74c-0.2-2.97,0.22-5.36,1.06-7.23c1.05-2.32,2.72-3.83,4.74-4.66 c1.89-0.77,4.01-0.88,6.16-0.45c2.57,0.51,5.22,1.81,7.49,3.68c1.86,1.54,4.95,4.07,7.95,6.52l2.52,2.06V17.18 c0-8.14,3.63-13.39,8.28-15.76C40.12,0.47,42.17,0,44.23,0c2.05,0,4.1,0.48,5.98,1.43c4.69,2.37,8.36,7.62,8.36,15.62V31.15 L58.57,31.15z"/></svg>`;
|
||||
|
||||
/**
|
||||
* @UCLASS(BlueprintType)
|
||||
* Entry-point event node. Adds pulsate animation and a hand-pointer hint.
|
||||
*/
|
||||
export class CustomEventNode extends NodeBase {
|
||||
/** @UCLASS(BlueprintType) */
|
||||
static NodeType = "custom_event";
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintNativeEvent)
|
||||
* @param {HTMLElement} article
|
||||
* @param {import('../GraphNode.js').GraphNode} graphNode
|
||||
*/
|
||||
BlueprintNativeEvent_OnRender(article, graphNode) {
|
||||
const outputs = article.querySelector(".node-outputs");
|
||||
if (!outputs) return;
|
||||
|
||||
const execPin = outputs.querySelector('[data-pin-id="exec_out"]');
|
||||
if (!execPin) return;
|
||||
|
||||
execPin.classList.add("pin--pulsate");
|
||||
execPin.style.position = "relative";
|
||||
|
||||
const handHint = document.createElement("div");
|
||||
handHint.className = "pin-hint-hand";
|
||||
handHint.innerHTML = HAND_SVG;
|
||||
handHint.style.color = "#fff";
|
||||
execPin.appendChild(handHint);
|
||||
|
||||
// Dismiss hand permanently on first click of the pin (handle or label)
|
||||
const dismiss = () => {
|
||||
article.classList.add("hint-dismissed");
|
||||
execPin.removeEventListener("click", dismiss);
|
||||
};
|
||||
execPin.addEventListener("click", dismiss);
|
||||
}
|
||||
}
|
||||
|
||||
NodeRegistry.UCLASS_Register(CustomEventNode);
|
||||
38
website/scripts/nodes/GetMatrixChatNode.js
Normal file
38
website/scripts/nodes/GetMatrixChatNode.js
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* @file GetMatrixChatNode.js
|
||||
* @UCLASS(BlueprintType, Blueprintable)
|
||||
*/
|
||||
|
||||
import { NodeBase } from './NodeBase.js';
|
||||
import { NodeRegistry } from './NodeRegistry.js';
|
||||
import { GlobalVariables } from '../GlobalVariables.js';
|
||||
|
||||
/**
|
||||
* @UCLASS(BlueprintType)
|
||||
* Pure getter node that exposes the "MatrixChat" global as an object output.
|
||||
* Styled with a semi-transparent blue header to visually denote a variable getter.
|
||||
*/
|
||||
export class GetMatrixChatNode extends NodeBase {
|
||||
/** @UCLASS(BlueprintType) */
|
||||
static NodeType = "get_matrix_chat";
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintNativeEvent)
|
||||
* @param {HTMLElement} article
|
||||
* @param {import('../GraphNode.js').GraphNode} graphNode
|
||||
*/
|
||||
BlueprintNativeEvent_OnRender(article, graphNode) {
|
||||
const header = article.querySelector(".node-header");
|
||||
if (header) {
|
||||
header.style.background = "rgba(100, 181, 246, 0.2)";
|
||||
header.style.borderColor = "rgba(100, 181, 246, 0.4)";
|
||||
}
|
||||
|
||||
const outputPin = graphNode.outputs.find(p => p.id === "value");
|
||||
if (outputPin && GlobalVariables.has("MatrixChat")) {
|
||||
outputPin.defaultValue = GlobalVariables.get("MatrixChat");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NodeRegistry.UCLASS_Register(GetMatrixChatNode);
|
||||
35
website/scripts/nodes/InfoNode.js
Normal file
35
website/scripts/nodes/InfoNode.js
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* @file InfoNode.js
|
||||
* @UCLASS(BlueprintType, Blueprintable)
|
||||
*/
|
||||
|
||||
import { NodeBase } from './NodeBase.js';
|
||||
import { NodeRegistry } from './NodeRegistry.js';
|
||||
|
||||
/**
|
||||
* @UCLASS(BlueprintType)
|
||||
* Renders a markdown body below the node header.
|
||||
*/
|
||||
export class InfoNode extends NodeBase {
|
||||
/** @UCLASS(BlueprintType) */
|
||||
static NodeType = "info";
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintNativeEvent)
|
||||
* @param {HTMLElement} article
|
||||
* @param {import('../GraphNode.js').GraphNode} graphNode
|
||||
* @param {import('./NodeRenderContext.js').NodeRenderContext} renderCtx
|
||||
*/
|
||||
BlueprintNativeEvent_OnRender(article, graphNode, renderCtx) {
|
||||
if (!graphNode.markdownSrc) return;
|
||||
|
||||
const body = document.createElement("div");
|
||||
body.className = "node-body";
|
||||
body.setAttribute("aria-live", "polite");
|
||||
article.appendChild(body);
|
||||
|
||||
renderCtx.BlueprintCallable_LoadMarkdown(graphNode.markdownSrc, body);
|
||||
}
|
||||
}
|
||||
|
||||
NodeRegistry.UCLASS_Register(InfoNode);
|
||||
43
website/scripts/nodes/LottieNode.js
Normal file
43
website/scripts/nodes/LottieNode.js
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @file LottieNode.js
|
||||
* @UCLASS(BlueprintType, Blueprintable)
|
||||
*/
|
||||
|
||||
import { NodeBase } from './NodeBase.js';
|
||||
import { NodeRegistry } from './NodeRegistry.js';
|
||||
|
||||
/**
|
||||
* @UCLASS(BlueprintType)
|
||||
* Renders a Lottie animation JSON file inside a node body using the
|
||||
* `<lottie-player>` web component (loaded via CDN in index.html).
|
||||
* Set `lottieSrc` on the graph node data to point at a Lottie JSON file.
|
||||
*/
|
||||
export class LottieNode extends NodeBase {
|
||||
/** @type {string} */
|
||||
static NodeType = "lottie";
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintNativeEvent)
|
||||
* @param {HTMLElement} article
|
||||
* @param {import('../GraphNode.js').GraphNode} graphNode
|
||||
*/
|
||||
BlueprintNativeEvent_OnRender(article, graphNode) {
|
||||
if (!graphNode.lottieSrc) return;
|
||||
|
||||
const body = document.createElement("div");
|
||||
body.className = "node-body node-body--lottie";
|
||||
|
||||
const player = document.createElement("lottie-player");
|
||||
player.setAttribute("src", graphNode.lottieSrc);
|
||||
player.setAttribute("background", "transparent");
|
||||
player.setAttribute("speed", "1");
|
||||
player.setAttribute("loop", "");
|
||||
player.setAttribute("autoplay", "");
|
||||
player.className = "node-lottie";
|
||||
|
||||
body.appendChild(player);
|
||||
article.appendChild(body);
|
||||
}
|
||||
}
|
||||
|
||||
NodeRegistry.UCLASS_Register(LottieNode);
|
||||
84
website/scripts/nodes/MatrixChatNode.js
Normal file
84
website/scripts/nodes/MatrixChatNode.js
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* @file MatrixChatNode.js
|
||||
* @UCLASS(BlueprintType, Blueprintable)
|
||||
*/
|
||||
|
||||
import { NodeBase } from './NodeBase.js';
|
||||
import { NodeRegistry } from './NodeRegistry.js';
|
||||
import { ChatNode } from '../ChatNode.js';
|
||||
import { GlobalVariables } from '../GlobalVariables.js';
|
||||
|
||||
/**
|
||||
* @UCLASS(BlueprintType)
|
||||
* Renders a full Matrix chat UI inside the node body.
|
||||
* Instantiates a ChatNode and registers it as the "MatrixChat" global.
|
||||
*/
|
||||
export class MatrixChatNode extends NodeBase {
|
||||
/** @UCLASS(BlueprintType) */
|
||||
static NodeType = "chat";
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintNativeEvent)
|
||||
* @param {HTMLElement} article
|
||||
* @param {import('../GraphNode.js').GraphNode} graphNode
|
||||
* @param {import('./NodeRenderContext.js').NodeRenderContext} renderCtx
|
||||
*/
|
||||
BlueprintNativeEvent_OnRender(article, graphNode, renderCtx) {
|
||||
const body = document.createElement("div");
|
||||
body.className = "node-body node-body--chat";
|
||||
|
||||
const statusBar = document.createElement("div");
|
||||
statusBar.className = "chat-status-bar";
|
||||
|
||||
const statusEl = document.createElement("span");
|
||||
statusEl.className = "chat-status chat-status-disconnected";
|
||||
statusEl.textContent = "Not connected";
|
||||
statusBar.appendChild(statusEl);
|
||||
|
||||
const messagesContainer = document.createElement("div");
|
||||
messagesContainer.className = "chat-messages";
|
||||
|
||||
const inputContainer = document.createElement("div");
|
||||
inputContainer.className = "chat-input-container";
|
||||
|
||||
const messageInput = document.createElement("input");
|
||||
messageInput.type = "text";
|
||||
messageInput.className = "chat-input";
|
||||
messageInput.placeholder = "Type a message or /nick...";
|
||||
messageInput.addEventListener("focus", () => {
|
||||
// On mobile the virtual keyboard shrinks the viewport. Delay lets the
|
||||
// keyboard finish animating before we scroll the input into view.
|
||||
window.setTimeout(() => {
|
||||
messageInput.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
||||
}, 300);
|
||||
});
|
||||
inputContainer.appendChild(messageInput);
|
||||
|
||||
body.appendChild(statusBar);
|
||||
body.appendChild(messagesContainer);
|
||||
body.appendChild(inputContainer);
|
||||
article.appendChild(body);
|
||||
|
||||
const homeserverPin = graphNode.inputs.find(p => p.id === "homeserver");
|
||||
const roomPin = graphNode.inputs.find(p => p.id === "room");
|
||||
const homeserver = homeserverPin?.defaultValue || "https://matrix.org";
|
||||
const roomAlias = roomPin?.defaultValue || "#general:matrix.org";
|
||||
|
||||
const chatInstance = new ChatNode(homeserver, roomAlias);
|
||||
// Pass resolver function so chat can resolve name at connect time
|
||||
chatInstance.initialize(
|
||||
messagesContainer,
|
||||
messageInput,
|
||||
statusEl,
|
||||
inputContainer,
|
||||
homeserver,
|
||||
roomAlias,
|
||||
() => renderCtx.BlueprintPure_ResolveInputValue?.(graphNode.id, "guestName") || ""
|
||||
);
|
||||
|
||||
renderCtx.BlueprintCallable_RegisterChatInstance(graphNode.id, chatInstance);
|
||||
GlobalVariables.set("MatrixChat", chatInstance);
|
||||
}
|
||||
}
|
||||
|
||||
NodeRegistry.UCLASS_Register(MatrixChatNode);
|
||||
53
website/scripts/nodes/NodeBase.js
Normal file
53
website/scripts/nodes/NodeBase.js
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @file NodeBase.js
|
||||
* Abstract base class for all Blueprint graph node types.
|
||||
*
|
||||
* Method prefixes mirror Unreal Engine UFUNCTION macro conventions:
|
||||
* BlueprintNativeEvent_ — has a native default implementation; subclasses may override
|
||||
* BlueprintCallable_ — has exec-pin side effects; called during graph traversal
|
||||
* BlueprintPure_ — side-effect free; does not advance exec flow
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('../GraphNode.js').GraphNode} GraphNode
|
||||
* @typedef {import('./NodeRenderContext.js').NodeRenderContext} NodeRenderContext
|
||||
* @typedef {import('./NodeExecutionContext.js').NodeExecutionContext} NodeExecutionContext
|
||||
*/
|
||||
|
||||
/**
|
||||
* @UCLASS(Abstract)
|
||||
* Base class all node handlers inherit from. Registered via NodeRegistry.
|
||||
*/
|
||||
export class NodeBase {
|
||||
/**
|
||||
* The graph type string that maps to this class in the registry.
|
||||
* Must be overridden by every subclass.
|
||||
* @UCLASS(BlueprintType)
|
||||
* @type {string}
|
||||
*/
|
||||
static NodeType = "";
|
||||
|
||||
/**
|
||||
* Called after the node's base DOM (header, pins) has been built.
|
||||
* Override to inject type-specific UI into the article element.
|
||||
*
|
||||
* @UFUNCTION(BlueprintNativeEvent)
|
||||
* @param {HTMLElement} article
|
||||
* @param {GraphNode} graphNode
|
||||
* @param {NodeRenderContext} renderCtx
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
BlueprintNativeEvent_OnRender(article, graphNode, renderCtx) {}
|
||||
|
||||
/**
|
||||
* Called when this node is reached during exec graph traversal.
|
||||
* Override to implement node-specific behaviour.
|
||||
* Exec flow continues automatically after this returns.
|
||||
*
|
||||
* @UFUNCTION(BlueprintCallable)
|
||||
* @param {NodeExecutionContext} ctx
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
async BlueprintCallable_Execute(ctx) {}
|
||||
}
|
||||
113
website/scripts/nodes/NodeExecutionContext.js
Normal file
113
website/scripts/nodes/NodeExecutionContext.js
Normal file
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* @file NodeExecutionContext.js
|
||||
* Runtime context passed to node handlers during graph traversal.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('../GraphNode.js').GraphNode} GraphNode
|
||||
* @typedef {import('../GraphConnection.js').GraphConnection} GraphConnection
|
||||
* @typedef {import('../NodeRenderer.js').NodeRenderer} NodeRenderer
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides controlled access to runtime execution services.
|
||||
* Passed to BlueprintCallable_Execute on every node handler invocation.
|
||||
*/
|
||||
export class NodeExecutionContext {
|
||||
/** @type {string} */
|
||||
#nodeId;
|
||||
|
||||
/** @type {NodeRenderer} */
|
||||
#nodeRenderer;
|
||||
|
||||
/** @type {(nodeId: string, pinId: string) => any} */
|
||||
#resolveInputFn;
|
||||
|
||||
/** @type {(nodeId: string, pinId: string) => Promise<void>} */
|
||||
#runExecPinFn;
|
||||
|
||||
/** @type {((nodeId: string, value: string) => void) | null} */
|
||||
#onPrint;
|
||||
|
||||
/** @type {((connId: string, kind: string) => Promise<void>) | null} */
|
||||
#onStep;
|
||||
|
||||
/**
|
||||
* @param {string} nodeId
|
||||
* @param {NodeRenderer} nodeRenderer
|
||||
* @param {(nodeId: string, pinId: string) => any} resolveInputFn
|
||||
* @param {(nodeId: string, pinId: string) => Promise<void>} runExecPinFn
|
||||
* @param {((nodeId: string, value: string) => void) | null} onPrint
|
||||
* @param {((connId: string, kind: string) => Promise<void>) | null} onStep
|
||||
*/
|
||||
constructor(nodeId, nodeRenderer, resolveInputFn, runExecPinFn, onPrint, onStep) {
|
||||
this.#nodeId = nodeId;
|
||||
this.#nodeRenderer = nodeRenderer;
|
||||
this.#resolveInputFn = resolveInputFn;
|
||||
this.#runExecPinFn = runExecPinFn;
|
||||
this.#onPrint = onPrint;
|
||||
this.#onStep = onStep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of the currently executing node.
|
||||
*
|
||||
* @UFUNCTION(BlueprintPure)
|
||||
* @returns {string}
|
||||
*/
|
||||
BlueprintPure_GetNodeId() {
|
||||
return this.#nodeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the GraphNode data for the currently executing node.
|
||||
*
|
||||
* @UFUNCTION(BlueprintPure)
|
||||
* @returns {GraphNode | null}
|
||||
*/
|
||||
BlueprintPure_GetNode() {
|
||||
return this.#nodeRenderer.getNodes().find(n => n.id === this.#nodeId) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the runtime value of a named input pin, following connections.
|
||||
*
|
||||
* @UFUNCTION(BlueprintPure)
|
||||
* @param {string} pinId
|
||||
* @returns {any}
|
||||
*/
|
||||
BlueprintPure_ResolveInput(pinId) {
|
||||
return this.#resolveInputFn(this.#nodeId, pinId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all live graph connections.
|
||||
*
|
||||
* @UFUNCTION(BlueprintPure)
|
||||
* @returns {GraphConnection[]}
|
||||
*/
|
||||
BlueprintPure_GetConnections() {
|
||||
return this.#nodeRenderer.getConnections();
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers exec flow from an output pin on this node.
|
||||
*
|
||||
* @UFUNCTION(BlueprintCallable)
|
||||
* @param {string} pinId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
BlueprintCallable_RunExecPin(pinId) {
|
||||
return this.#runExecPinFn(this.#nodeId, pinId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires the print callback with a string value.
|
||||
*
|
||||
* @UFUNCTION(BlueprintCallable)
|
||||
* @param {string} value
|
||||
*/
|
||||
BlueprintCallable_Print(value) {
|
||||
this.#onPrint?.(this.#nodeId, value);
|
||||
}
|
||||
}
|
||||
50
website/scripts/nodes/NodeRegistry.js
Normal file
50
website/scripts/nodes/NodeRegistry.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @file NodeRegistry.js
|
||||
* Static registry mapping node type strings to their handler instances.
|
||||
*/
|
||||
|
||||
import { NodeBase } from './NodeBase.js';
|
||||
|
||||
/**
|
||||
* Static registry for all Blueprint node types.
|
||||
* Node classes self-register by calling UCLASS_Register during module init.
|
||||
*/
|
||||
export class NodeRegistry {
|
||||
/** @type {Map<string, NodeBase>} */
|
||||
static #registry = new Map();
|
||||
|
||||
/**
|
||||
* Registers a node class by its static NodeType.
|
||||
* Call once per class, typically at the bottom of the class file.
|
||||
*
|
||||
* @UFUNCTION(BlueprintCallable)
|
||||
* @param {typeof NodeBase} NodeClass
|
||||
*/
|
||||
static UCLASS_Register(NodeClass) {
|
||||
if (!NodeClass.NodeType) {
|
||||
throw new Error(`[NodeRegistry] "${NodeClass.name}" is missing a static NodeType.`);
|
||||
}
|
||||
this.#registry.set(NodeClass.NodeType, new NodeClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the handler instance for a given node type, or null.
|
||||
*
|
||||
* @UFUNCTION(BlueprintPure)
|
||||
* @param {string} nodeType
|
||||
* @returns {NodeBase | null}
|
||||
*/
|
||||
static BlueprintPure_Get(nodeType) {
|
||||
return this.#registry.get(nodeType) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all currently registered type strings.
|
||||
*
|
||||
* @UFUNCTION(BlueprintPure)
|
||||
* @returns {string[]}
|
||||
*/
|
||||
static BlueprintPure_GetRegisteredTypes() {
|
||||
return [...this.#registry.keys()];
|
||||
}
|
||||
}
|
||||
137
website/scripts/nodes/NodeRenderContext.js
Normal file
137
website/scripts/nodes/NodeRenderContext.js
Normal file
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* @file NodeRenderContext.js
|
||||
* Render-time context passed to node handlers when building their DOM.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('../GraphNode.js').GraphNode} GraphNode
|
||||
*/
|
||||
|
||||
/**
|
||||
* Wraps all render-time services nodes may need during BlueprintNativeEvent_OnRender.
|
||||
* Keeps NodeRenderer internals encapsulated while giving nodes controlled access.
|
||||
*/
|
||||
export class NodeRenderContext {
|
||||
/** @type {(src: string, target: HTMLElement) => Promise<void>} */
|
||||
#loadMarkdown;
|
||||
|
||||
/** @type {(p: Promise<void>) => void} */
|
||||
#addMarkdownPromise;
|
||||
|
||||
/** @type {((nodeId: string, pinId?: string) => void) | null} */
|
||||
#onNodeExecute;
|
||||
|
||||
/** @type {(nodeId: string, instance: any) => void} */
|
||||
#registerChatInstance;
|
||||
|
||||
/** @type {(nodeId: string, toggleEl: HTMLButtonElement) => void} */
|
||||
#startTimer;
|
||||
|
||||
/** @type {(nodeId: string) => void} */
|
||||
#stopTimer;
|
||||
|
||||
/** @type {() => string} */
|
||||
#svgPlay;
|
||||
|
||||
/** @type {() => string} */
|
||||
#svgStop;
|
||||
|
||||
/** @type {(nodeId: string, pinId: string) => any} */
|
||||
#resolveInputValue;
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* loadMarkdown: (src: string, target: HTMLElement) => Promise<void>,
|
||||
* addMarkdownPromise: (p: Promise<void>) => void,
|
||||
* onNodeExecute: ((nodeId: string, pinId?: string) => void) | null,
|
||||
* registerChatInstance: (nodeId: string, instance: any) => void,
|
||||
* startTimer: (nodeId: string, toggleEl: HTMLButtonElement) => void,
|
||||
* stopTimer: (nodeId: string) => void,
|
||||
* svgPlay: () => string,
|
||||
* svgStop: () => string,
|
||||
* resolveInputValue: (nodeId: string, pinId: string) => any,
|
||||
* }} callbacks
|
||||
*/
|
||||
constructor(callbacks) {
|
||||
this.#loadMarkdown = callbacks.loadMarkdown;
|
||||
this.#addMarkdownPromise = callbacks.addMarkdownPromise;
|
||||
this.#onNodeExecute = callbacks.onNodeExecute;
|
||||
this.#registerChatInstance = callbacks.registerChatInstance;
|
||||
this.#startTimer = callbacks.startTimer;
|
||||
this.#stopTimer = callbacks.stopTimer;
|
||||
this.#svgPlay = callbacks.svgPlay;
|
||||
this.#svgStop = callbacks.svgStop;
|
||||
this.#resolveInputValue = callbacks.resolveInputValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintCallable)
|
||||
* @param {string} src
|
||||
* @param {HTMLElement} target
|
||||
*/
|
||||
BlueprintCallable_LoadMarkdown(src, target) {
|
||||
this.#addMarkdownPromise(this.#loadMarkdown(src, target));
|
||||
}
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintCallable)
|
||||
* @param {string} nodeId
|
||||
* @param {string} [pinId]
|
||||
*/
|
||||
BlueprintCallable_TriggerNodeExecute(nodeId, pinId) {
|
||||
this.#onNodeExecute?.(nodeId, pinId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintCallable)
|
||||
* @param {string} nodeId
|
||||
* @param {any} instance
|
||||
*/
|
||||
BlueprintCallable_RegisterChatInstance(nodeId, instance) {
|
||||
this.#registerChatInstance(nodeId, instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintCallable)
|
||||
* @param {string} nodeId
|
||||
* @param {HTMLButtonElement} toggleEl
|
||||
*/
|
||||
BlueprintCallable_StartTimer(nodeId, toggleEl) {
|
||||
this.#startTimer(nodeId, toggleEl);
|
||||
}
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintCallable)
|
||||
* @param {string} nodeId
|
||||
*/
|
||||
BlueprintCallable_StopTimer(nodeId) {
|
||||
this.#stopTimer(nodeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintPure)
|
||||
* @returns {string}
|
||||
*/
|
||||
BlueprintPure_GetSvgPlay() {
|
||||
return this.#svgPlay();
|
||||
}
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintPure)
|
||||
* @returns {string}
|
||||
*/
|
||||
BlueprintPure_GetSvgStop() {
|
||||
return this.#svgStop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintPure)
|
||||
* Resolves an input pin value by following connections.
|
||||
* @param {string} nodeId
|
||||
* @param {string} pinId
|
||||
* @returns {any}
|
||||
*/
|
||||
BlueprintPure_ResolveInputValue(nodeId, pinId) {
|
||||
return this.#resolveInputValue(nodeId, pinId);
|
||||
}
|
||||
}
|
||||
27
website/scripts/nodes/PrintNode.js
Normal file
27
website/scripts/nodes/PrintNode.js
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @file PrintNode.js
|
||||
* @UCLASS(BlueprintType, Blueprintable)
|
||||
*/
|
||||
|
||||
import { NodeBase } from './NodeBase.js';
|
||||
import { NodeRegistry } from './NodeRegistry.js';
|
||||
|
||||
/**
|
||||
* @UCLASS(BlueprintType)
|
||||
* Logs a string value and fires it through the print callback.
|
||||
*/
|
||||
export class PrintNode extends NodeBase {
|
||||
/** @UCLASS(BlueprintType) */
|
||||
static NodeType = "print";
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintCallable)
|
||||
* @param {import('./NodeExecutionContext.js').NodeExecutionContext} ctx
|
||||
*/
|
||||
async BlueprintCallable_Execute(ctx) {
|
||||
const value = ctx.BlueprintPure_ResolveInput("value");
|
||||
ctx.BlueprintCallable_Print(value);
|
||||
}
|
||||
}
|
||||
|
||||
NodeRegistry.UCLASS_Register(PrintNode);
|
||||
38
website/scripts/nodes/PureNode.js
Normal file
38
website/scripts/nodes/PureNode.js
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* @file PureNode.js
|
||||
* @UCLASS(BlueprintType, Blueprintable)
|
||||
*/
|
||||
|
||||
import { NodeBase } from './NodeBase.js';
|
||||
import { NodeRegistry } from './NodeRegistry.js';
|
||||
|
||||
/**
|
||||
* @UCLASS(BlueprintType)
|
||||
* Pure data node — optionally renders an image body. No exec logic.
|
||||
*/
|
||||
export class PureNode extends NodeBase {
|
||||
/** @UCLASS(BlueprintType) */
|
||||
static NodeType = "pure";
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintNativeEvent)
|
||||
* @param {HTMLElement} article
|
||||
* @param {import('../GraphNode.js').GraphNode} graphNode
|
||||
*/
|
||||
BlueprintNativeEvent_OnRender(article, graphNode) {
|
||||
if (!graphNode.imageSrc) return;
|
||||
|
||||
const body = document.createElement("div");
|
||||
body.className = "node-body node-body--image";
|
||||
|
||||
const img = document.createElement("img");
|
||||
img.src = graphNode.imageSrc;
|
||||
img.alt = "";
|
||||
img.className = "node-image";
|
||||
body.appendChild(img);
|
||||
|
||||
article.appendChild(body);
|
||||
}
|
||||
}
|
||||
|
||||
NodeRegistry.UCLASS_Register(PureNode);
|
||||
124
website/scripts/nodes/RandomNameNode.js
Normal file
124
website/scripts/nodes/RandomNameNode.js
Normal file
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* @file RandomNameNode.js
|
||||
* @UCLASS(BlueprintType, Blueprintable)
|
||||
*/
|
||||
|
||||
import { NodeBase } from './NodeBase.js';
|
||||
import { NodeRegistry } from './NodeRegistry.js';
|
||||
|
||||
/**
|
||||
* @UCLASS(BlueprintType)
|
||||
* Generates random names from first/second part combinations.
|
||||
* Pre-runs on load and has a button to regenerate.
|
||||
*/
|
||||
export class RandomNameNode extends NodeBase {
|
||||
/** @UCLASS(BlueprintType) */
|
||||
static NodeType = "random_name";
|
||||
|
||||
/** @type {Array<string>} */
|
||||
static #firstParts = [];
|
||||
|
||||
/** @type {Array<string>} */
|
||||
static #secondParts = [];
|
||||
|
||||
/** @type {Promise<void> | null} */
|
||||
static #loadPromise = null;
|
||||
|
||||
/**
|
||||
* Load name parts from JSON
|
||||
*/
|
||||
static async #loadNameParts() {
|
||||
if (this.#loadPromise) return this.#loadPromise;
|
||||
|
||||
this.#loadPromise = (async () => {
|
||||
try {
|
||||
const response = await fetch('data/nameParts.json');
|
||||
const data = await response.json();
|
||||
this.#firstParts = data.firstParts || [];
|
||||
this.#secondParts = data.secondParts || [];
|
||||
} catch (err) {
|
||||
console.error('Failed to load name parts:', err);
|
||||
this.#firstParts = ['Random'];
|
||||
this.#secondParts = ['Name'];
|
||||
}
|
||||
})();
|
||||
|
||||
return this.#loadPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate random name
|
||||
* @returns {string}
|
||||
*/
|
||||
static generateName() {
|
||||
if (this.#firstParts.length === 0 || this.#secondParts.length === 0) {
|
||||
return 'Loading...';
|
||||
}
|
||||
const first = this.#firstParts[Math.floor(Math.random() * this.#firstParts.length)];
|
||||
const second = this.#secondParts[Math.floor(Math.random() * this.#secondParts.length)];
|
||||
return `${first}${second}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintNativeEvent)
|
||||
* @param {HTMLElement} article
|
||||
* @param {import('../GraphNode.js').GraphNode} graphNode
|
||||
* @param {import('./NodeRenderContext.js').NodeRenderContext} renderCtx
|
||||
*/
|
||||
async BlueprintNativeEvent_OnRender(article, graphNode, renderCtx) {
|
||||
await RandomNameNode.#loadNameParts();
|
||||
|
||||
// Generate initial name if not set
|
||||
if (!graphNode._generatedName) {
|
||||
graphNode._generatedName = RandomNameNode.generateName();
|
||||
}
|
||||
|
||||
console.log('[RandomNameNode] Generated name:', graphNode._generatedName, 'for node', graphNode.id);
|
||||
|
||||
// Add button to header
|
||||
const header = article.querySelector(".node-header");
|
||||
if (header) {
|
||||
const btn = document.createElement("button");
|
||||
btn.className = "node-run-btn";
|
||||
btn.title = "Generate New Name";
|
||||
btn.innerHTML = `<svg width="9" height="9" viewBox="0 0 9 9" fill="currentColor" aria-hidden="true"><polygon points="1,0 9,4.5 1,9"/></svg>`;
|
||||
|
||||
btn.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
graphNode._generatedName = RandomNameNode.generateName();
|
||||
nameDisplay.textContent = graphNode._generatedName;
|
||||
|
||||
btn.classList.add("node-run-btn--flash");
|
||||
btn.addEventListener("animationend", () => btn.classList.remove("node-run-btn--flash"), { once: true });
|
||||
});
|
||||
|
||||
header.appendChild(btn);
|
||||
}
|
||||
|
||||
// Display name in body
|
||||
const body = document.createElement("div");
|
||||
body.className = "node-body node-body--text";
|
||||
|
||||
const nameDisplay = document.createElement("div");
|
||||
nameDisplay.className = "node-name-display";
|
||||
nameDisplay.textContent = graphNode._generatedName;
|
||||
|
||||
body.appendChild(nameDisplay);
|
||||
article.appendChild(body);
|
||||
}
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintNativeEvent)
|
||||
* @param {import('../GraphNode.js').GraphNode} graphNode
|
||||
* @param {string} pinId
|
||||
* @returns {any}
|
||||
*/
|
||||
BlueprintNativeEvent_GetOutputValue(graphNode, pinId) {
|
||||
if (pinId === 'name') {
|
||||
return graphNode._generatedName || '';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
NodeRegistry.UCLASS_Register(RandomNameNode);
|
||||
36
website/scripts/nodes/SequenceNode.js
Normal file
36
website/scripts/nodes/SequenceNode.js
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @file SequenceNode.js
|
||||
* @UCLASS(BlueprintType, Blueprintable)
|
||||
*/
|
||||
|
||||
import { NodeBase } from './NodeBase.js';
|
||||
import { NodeRegistry } from './NodeRegistry.js';
|
||||
|
||||
/**
|
||||
* @UCLASS(BlueprintType)
|
||||
* Fires all then_N outputs in order, then continues exec flow.
|
||||
*/
|
||||
export class SequenceNode extends NodeBase {
|
||||
/** @UCLASS(BlueprintType) */
|
||||
static NodeType = "sequence";
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintCallable)
|
||||
* @param {import('./NodeExecutionContext.js').NodeExecutionContext} ctx
|
||||
*/
|
||||
async BlueprintCallable_Execute(ctx) {
|
||||
const node = ctx.BlueprintPure_GetNode();
|
||||
if (!node) return;
|
||||
|
||||
// Fire every then_N output in order
|
||||
const thenPins = node.outputs
|
||||
.filter(p => p.id.startsWith("then_"))
|
||||
.sort((a, b) => a.id.localeCompare(b.id));
|
||||
|
||||
for (const pin of thenPins) {
|
||||
await ctx.BlueprintCallable_RunExecPin(pin.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NodeRegistry.UCLASS_Register(SequenceNode);
|
||||
46
website/scripts/nodes/TimerNode.js
Normal file
46
website/scripts/nodes/TimerNode.js
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* @file TimerNode.js
|
||||
* @UCLASS(BlueprintType, Blueprintable)
|
||||
*/
|
||||
|
||||
import { NodeBase } from './NodeBase.js';
|
||||
import { NodeRegistry } from './NodeRegistry.js';
|
||||
|
||||
/**
|
||||
* @UCLASS(BlueprintType)
|
||||
* Renders a toggle button in the header that starts/stops an interval timer.
|
||||
*/
|
||||
export class TimerNode extends NodeBase {
|
||||
/** @UCLASS(BlueprintType) */
|
||||
static NodeType = "timer";
|
||||
|
||||
/**
|
||||
* @UFUNCTION(BlueprintNativeEvent)
|
||||
* @param {HTMLElement} article
|
||||
* @param {import('../GraphNode.js').GraphNode} graphNode
|
||||
* @param {import('./NodeRenderContext.js').NodeRenderContext} renderCtx
|
||||
*/
|
||||
BlueprintNativeEvent_OnRender(article, graphNode, renderCtx) {
|
||||
const header = article.querySelector(".node-header");
|
||||
if (!header) return;
|
||||
|
||||
const toggle = document.createElement("button");
|
||||
toggle.className = "node-timer-btn";
|
||||
toggle.title = "Start timer";
|
||||
toggle.innerHTML = renderCtx.BlueprintPure_GetSvgPlay();
|
||||
|
||||
toggle.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
const running = toggle.dataset.running === "true";
|
||||
if (running) {
|
||||
renderCtx.BlueprintCallable_StopTimer(graphNode.id);
|
||||
} else {
|
||||
renderCtx.BlueprintCallable_StartTimer(graphNode.id, toggle);
|
||||
}
|
||||
});
|
||||
|
||||
header.appendChild(toggle);
|
||||
}
|
||||
}
|
||||
|
||||
NodeRegistry.UCLASS_Register(TimerNode);
|
||||
25
website/scripts/nodes/index.js
Normal file
25
website/scripts/nodes/index.js
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @file index.js
|
||||
* Imports all node classes so they self-register with NodeRegistry.
|
||||
* Import this module once at app startup before rendering any nodes.
|
||||
*/
|
||||
|
||||
export { NodeBase } from './NodeBase.js';
|
||||
export { NodeRegistry } from './NodeRegistry.js';
|
||||
export { NodeExecutionContext } from './NodeExecutionContext.js';
|
||||
export { NodeRenderContext } from './NodeRenderContext.js';
|
||||
|
||||
// Node types — each file calls NodeRegistry.UCLASS_Register on load
|
||||
export { PrintNode } from './PrintNode.js';
|
||||
export { SequenceNode } from './SequenceNode.js';
|
||||
export { CustomEventNode } from './CustomEventNode.js';
|
||||
export { ButtonNode } from './ButtonNode.js';
|
||||
export { TimerNode } from './TimerNode.js';
|
||||
export { InfoNode } from './InfoNode.js';
|
||||
export { PureNode } from './PureNode.js';
|
||||
export { MatrixChatNode } from './MatrixChatNode.js';
|
||||
export { GetMatrixChatNode } from './GetMatrixChatNode.js';
|
||||
export { ChatConnectNode } from './ChatConnectNode.js';
|
||||
export { BindEventNode } from './BindEventNode.js';
|
||||
export { LottieNode } from './LottieNode.js';
|
||||
export { RandomNameNode } from './RandomNameNode.js';
|
||||
Reference in New Issue
Block a user