### Setup BaklavaJS Editor and Dependency Engine
Source: https://github.com/newcat/baklavajs/blob/master/docs/execution/setup.md
Initializes the BaklavaJS editor and sets up the DependencyEngine for graph execution. The engine must be started to begin processing.
```javascript
import { Editor } from "@baklavajs/core";
import { DependencyEngine } from "@baklavajs/engine";
const editor = new Editor();
const engine = new DependencyEngine(editor);
engine.start();
```
--------------------------------
### Importing Events Module
Source: https://github.com/newcat/baklavajs/blob/master/packages/events/README.md
Import the events module to start using its functionalities. This is a common setup step.
```javascript
const events = require('@baklavajs/events');
```
--------------------------------
### Install BaklavaJS Packages
Source: https://context7.com/newcat/baklavajs/llms.txt
Install the combined package or individual scoped packages for BaklavaJS.
```bash
# All-in-one
npm install baklavajs
# or individually
npm install @baklavajs/core @baklavajs/engine @baklavajs/renderer-vue @baklavajs/interface-types @baklavajs/themes
```
--------------------------------
### Install BaklavaJS Packages
Source: https://github.com/newcat/baklavajs/blob/master/docs/getting-started.md
Install the core BaklavaJS package or individual components using npm, yarn, or pnpm.
```bash
# npm
npm i baklavajs
# yarn
yarn add baklavajs
# pnpm
pnpm add baklavajs
```
--------------------------------
### Browser Build with CDN
Source: https://context7.com/newcat/baklavajs/llms.txt
Use BaklavaJS directly from a CDN in plain HTML without any build tools. This setup includes the necessary CSS theme and the core JavaScript bundle.
```html
```
--------------------------------
### Create MathNode using Class-based Approach (JavaScript)
Source: https://github.com/newcat/baklavajs/blob/master/docs/nodes/nodes.md
This JavaScript example shows a class-based implementation for a 'MathNode', similar to the TypeScript version. Remember to call `this.initializeIo()` within the constructor.
```javascript
import { Node, NodeInterface, NumberInterface, SelectInterface } from "baklavajs";
export default class MathNode extends Node {
constructor() {
super();
this.type = "MathNode";
this.title = this.type;
this.inputs = {
number1: new NumberInterface("Number", 1),
number2: new NumberInterface("Number", 2),
operation: new SelectInterface("Operation", "Add", ["Add", "Subtract"]).setPort(false),
};
this.outputs = {
output: new NodeInterface("Output", 0),
};
this.calculate = ({ number1, number2, operation }) => {
let output;
if (operation === "Add") {
output = number1 + number2;
}
else if (operation === "Subtract") {
output = number1 - number2;
}
else {
throw new Error("Unknown operation: " + operation);
}
return { output };
};
this.initializeIo();
}
}
```
--------------------------------
### Create MathNode using Class-based Approach (TypeScript)
Source: https://github.com/newcat/baklavajs/blob/master/docs/nodes/nodes.md
This TypeScript example demonstrates a class-based approach for creating a 'MathNode' with dynamic inputs and outputs. It's crucial to call `this.initializeIo()` in the constructor for proper node functionality.
```typescript
import { Node, NodeInterface, CalculateFunction, NumberInterface, SelectInterface } from "baklavajs";
interface Inputs {
number1: number;
number2: number;
operation: string;
}
interface Outputs {
output: number;
}
export default class MathNode extends Node {
public type = "MathNode";
public title = this.type;
public inputs = {
number1: new NumberInterface("Number", 1),
number2: new NumberInterface("Number", 2),
operation: new SelectInterface("Operation", "Add", ["Add", "Subtract"]).setPort(false),
};
public outputs = {
output: new NodeInterface("Output", 0),
};
public constructor() {
super();
this.initializeIo();
}
public calculate: CalculateFunction = ({ number1, number2, operation }) => {
let output;
if (operation === "Add") {
output = number1 + number2;
} else if (operation === "Subtract") {
output = number1 - number2;
} else {
throw new Error("Unknown operation: " + operation);
}
return { output };
}
}
```
--------------------------------
### Customize Toolbar with Custom Commands
Source: https://github.com/newcat/baklavajs/blob/master/docs/visual-editor/toolbar.md
Register a custom command and add it to the toolbar alongside default commands. This example shows how to toggle the minimap.
```typescript
import { useBaklava, TOOLBAR_COMMANDS, TOOLBAR_SUBGRAPH_COMMANDS } from "@baklavajs/renderer-vue";
import { defineComponent, h, markRaw } from "vue";
const baklavaView = useBaklava();
// Register a custom command
const TOGGLE_MINIMAP_COMMAND = "TOGGLE_MINIMAP";
baklavaView.commandHandler.registerCommand(TOGGLE_MINIMAP_COMMAND, {
execute: () => {
baklavaView.settings.enableMinimap = !baklavaView.settings.enableMinimap;
},
canExecute: () => true,
});
// Customize the toolbar with a mix of default and custom commands
baklavaView.settings.toolbar.commands = [
TOOLBAR_COMMANDS.COPY,
TOOLBAR_COMMANDS.PASTE,
TOOLBAR_COMMANDS.DELETE_NODES,
TOOLBAR_COMMANDS.UNDO,
TOOLBAR_COMMANDS.REDO,
{
command: TOGGLE_MINIMAP_COMMAND,
title: "Toggle Minimap",
icon: markRaw(
defineComponent(() => {
return () => h("div", "Map");
}),
),
},
];
// Customize subgraph commands
baklavaView.settings.toolbar.subgraphCommands = [
TOOLBAR_SUBGRAPH_COMMANDS.SAVE_SUBGRAPH,
TOOLBAR_SUBGRAPH_COMMANDS.SWITCH_TO_MAIN_GRAPH,
];
```
--------------------------------
### Importing a Dark Theme
Source: https://github.com/newcat/baklavajs/blob/master/docs/visual-editor/customization.md
Import a specific theme's CSS file to apply its styling to the Baklava editor. Ensure the theme package is installed.
```javascript
import "@baklavajs/themes/syrup-dark.css";
```
--------------------------------
### Node Interface Middleware Example
Source: https://github.com/newcat/baklavajs/blob/master/docs/nodes/interfaces.md
Utilize middleware functions to add custom logic or meta-data to node interfaces in a type-safe manner. The middleware receives the interface instance as the first argument.
```javascript
function setType(intf, type) {
intf.type = type.name;
}
// usage - just an example, the interface types plugin works a bit different
new NodeInterface("My Interface", 0).use(setType, "number");
```
--------------------------------
### Styling Nodes by Type with CSS
Source: https://github.com/newcat/baklavajs/blob/master/docs/visual-editor/customization.md
Use CSS selectors targeting data attributes to style specific node types. This example makes nodes of type 'MyNodeType' have a light blue background.
```css
.baklava-node[data-node-type="MyNodeType"] {
background-color: lightblue;
}
```
--------------------------------
### Define Node with Sidebar Interfaces
Source: https://github.com/newcat/baklavajs/blob/master/docs/visual-editor/sidebar.md
Use `displayInSidebar` to show node interfaces in the sidebar. This example defines a node with a dropdown and a hidden number input, both displayed in the sidebar.
```typescript
import { defineNode, NodeInterface, NumberInterface, SelectInterface, displayInSidebar } from "baklavajs";
export default defineNode({
type: "SidebarNode",
inputs: {
sidebarOption1: () =>
new SelectInterface("Dropdown", "foo", ["foo", "bar", "blub"]).use(displayInSidebar, true).setPort(false),
sidebarOption2: () => new NumberInterface("Test", 4).setHidden(true).use(displayInSidebar, true),
},
});
```
--------------------------------
### Standalone BaklavaJS Integration
Source: https://github.com/newcat/baklavajs/blob/master/docs/browser-build.md
Include this HTML structure in your project to use BaklavaJS directly in the browser. It requires linking to the BaklavaJS bundle and theme CSS from a CDN. This setup allows for immediate use without any build process.
```html
Document
```
--------------------------------
### Define a Math Node with defineNode()
Source: https://github.com/newcat/baklavajs/blob/master/docs/nodes/nodes.md
Use `defineNode` to create a reusable node type. This example defines a 'MathNode' that accepts two numbers and an operation ('Add' or 'Subtract'), performing the calculation and returning the result. It utilizes `NumberInterface` for numeric inputs and `SelectInterface` for the operation choice.
```typescript
import { defineNode, NodeInterface, NumberInterface, SelectInterface } from "baklavajs";
export default defineNode({
type: "MathNode",
inputs: {
number1: () => new NumberInterface("Number", 1),
number2: () => new NumberInterface("Number", 10),
operation: () => new SelectInterface("Operation", "Add", ["Add", "Subtract"]).setPort(false),
},
outputs: {
output: () => new NodeInterface("Output", 0),
},
calculate({ number1, number2, operation }) {
let output: number;
if (operation === "Add") {
output = number1 + number2;
} else if (operation === "Subtract") {
output = number1 - number2;
} else {
throw new Error("Unknown operation: " + operation);
}
return { output };
},
});
```
--------------------------------
### Manual Engine Execution
Source: https://github.com/newcat/baklavajs/blob/master/docs/execution/forward.md
Manually triggers a single run of the forward engine starting from a specified node. The first argument can be used for global calculation data, and the third for node update event data.
```javascript
const triggerNode = editor.graph.nodes[0];
const result = await engine.runOnce(undefined, triggerNode, undefined);
```
--------------------------------
### Prevent Adding a Connection
Source: https://github.com/newcat/baklavajs/blob/master/docs/event-system.md
Example of using a preventable event to stop a connection from being added based on custom logic. The `prevent` function must be called to block the action.
```typescript
editor.graphEvents.beforeAddConnection.subscribe(token, (conn, prevent) => {
// check, whether the user should be able to create this connection.
if (/* user not allowed to create connection */) {
prevent();
return;
}
});
```
--------------------------------
### Automatic Engine Execution
Source: https://github.com/newcat/baklavajs/blob/master/docs/execution/forward.md
Starts the forward engine to automatically re-run whenever a node's interface value changes. Execution will restart from the node whose value was modified.
```javascript
engine.start();
// Now any value change will trigger forward execution from the affected node
```
--------------------------------
### Define a Custom Node Type
Source: https://github.com/newcat/baklavajs/blob/master/docs/getting-started.md
Create a new node type using `defineNode`, specifying its inputs, outputs, and default values. This example defines a node with two number inputs, an operation selector, and a single output.
```typescript
// file: MyNode.ts
import { defineNode, NodeInterface, NumberInterface, SelectInterface } from "baklavajs";
export default defineNode({
type: "MyNode",
inputs: {
number1: () => new NumberInterface("Number", 1),
number2: () => new NumberInterface("Number", 10),
operation: () => new SelectInterface("Operation", "Add", ["Add", "Subtract"]).setPort(false),
},
outputs: {
output: () => new NodeInterface("Output", 0),
},
});
```
--------------------------------
### Custom Node Renderer with Conditional Logic
Source: https://github.com/newcat/baklavajs/blob/master/docs/visual-editor/customization.md
Replace the default node renderer with a custom component for specific node types. This example uses 'MyNodeRenderer' for 'MyNodeType' and falls back to the default 'BaklavaNode' for others.
```vue
```
--------------------------------
### Initialize Editor and Forward Engine
Source: https://github.com/newcat/baklavajs/blob/master/docs/execution/forward.md
Sets up the BaklavaJS editor and initializes the ForwardEngine. The engine requires an Editor instance to manage the graph.
```javascript
import { Editor } from "@baklavajs/core";
import { ForwardEngine } from "@baklavajs/engine";
const editor = new Editor();
const engine = new ForwardEngine(editor);
```
--------------------------------
### Initialize BaklavaJS Core
Source: https://github.com/newcat/baklavajs/blob/master/packages/engine/README.md
Import and initialize the baklavajs-core module. Further API demonstration is pending.
```javascript
const baklavajsCore = require('baklavajs-core');
// TODO: DEMONSTRATE API
```
--------------------------------
### Import baklavajs-full
Source: https://github.com/newcat/baklavajs/blob/master/packages/full/README.md
Import the full baklavajs package. Further API demonstration is pending.
```javascript
const baklavajsFull = require('baklavajs-full');
// TODO: DEMONSTRATE API
```
--------------------------------
### useBaklava() — Create the View Model
Source: https://context7.com/newcat/baklavajs/llms.txt
Returns a reactive view model that integrates the editor, renderer, command handler, history, clipboard, and settings. This view model should be passed to the `` component.
```APIDOC
## useBaklava()
### Description
Returns a reactive view model (`IBaklavaViewModel`) that wires together the editor, renderer, command handler, history, clipboard, and settings. Pass it to the `` component.
### Usage
```vue
```
```
--------------------------------
### Import BaklavaJS Core
Source: https://github.com/newcat/baklavajs/blob/master/packages/core/README.md
Import the BaklavaJS core library for use in your project. This is the initial step for integrating BaklavaJS.
```javascript
const baklavajsCore = require('baklavajs-core');
```
--------------------------------
### Create View Model with useBaklava()
Source: https://context7.com/newcat/baklavajs/llms.txt
Use the `useBaklava()` hook to create a reactive view model for the BaklavaJS editor. Pass this view model to the `` component. Ensure to import the necessary CSS for theming.
```vue
```
--------------------------------
### Use Subset of Default Toolbar Commands
Source: https://github.com/newcat/baklavajs/blob/master/docs/visual-editor/toolbar.md
Customize the toolbar to display only specific default commands. Import `TOOLBAR_COMMANDS` and assign an array of desired commands to `baklavaView.settings.toolbar.commands`.
```typescript
import { useBaklava, TOOLBAR_COMMANDS } from "@baklavajs/renderer-vue";
const baklavaView = useBaklava();
// Use only copy, paste, and undo commands
baklavaView.settings.toolbar.commands = [TOOLBAR_COMMANDS.COPY, TOOLBAR_COMMANDS.PASTE, TOOLBAR_COMMANDS.UNDO];
```
--------------------------------
### Create a Button Interface
Source: https://github.com/newcat/baklavajs/blob/master/docs/nodes/pre-defined-interfaces.md
Use ButtonInterface to display a button that triggers a callback when clicked.
```javascript
import { ButtonInterface } from "baklavajs";
new ButtonInterface("Name", () => console.log("Button clicked"));
```
--------------------------------
### Subscribe to a Normal Event with Tokens
Source: https://github.com/newcat/baklavajs/blob/master/docs/event-system.md
Demonstrates subscribing to and emitting a normal event using a Symbol as a token for managing the listener. Ensure the token is kept to unsubscribe.
```typescript
import { BaklavaEvent } from "@baklavajs/events";
const ev = new BaklavaEvent(null);
const token = Symbol();
ev.subscribe(token, (data) => {
console.log("Event triggered:", data);
});
ev.emit("Hello World");
ev.unsubscribe(token);
ev.emit("This won't be printed");
// Console output:
// Event triggered: Hello World
```
--------------------------------
### Create View Model with useBaklava
Source: https://github.com/newcat/baklavajs/blob/master/docs/visual-editor/setup.md
Use the `useBaklava` function to create a view model for the visual editor. This function accepts an existing editor instance if needed. Note that due to Vue's reactivity, you should use `viewModel.editor` afterwards.
```vue
```
--------------------------------
### Initialize BaklavaJS Editor
Source: https://github.com/newcat/baklavajs/blob/master/packages/full/test.html
Initializes the BaklavaJS editor instance and attaches it to a specified DOM element. Ensure the target element exists in the HTML.
```javascript
const vm = BaklavaJS.createBaklava(document.getElementById("editor")); console.log(vm);
```
--------------------------------
### Create a TextInput Interface
Source: https://github.com/newcat/baklavajs/blob/master/docs/nodes/pre-defined-interfaces.md
Use TextInputInterface to display a text field for user input.
```javascript
import { TextInputInterface } from "baklavajs";
new TextInputInterface("Name", "Edit me");
```
--------------------------------
### Force Update Dynamic Node Interfaces
Source: https://github.com/newcat/baklavajs/blob/master/docs/nodes/dynamic-nodes.md
Demonstrates how to use `forceUpdateInputs` to ensure an interface is always updated, even if its type changes, which can break existing connections.
```javascript
onUpdate({ type }) {
const inputs = type === "Number"
? { myInput: () => new NumberInterface("Input", 0) }
: { myInput: () => new TextInputInterface("Input", "") };
return {
input,
forceUpdateInputs: ["myInput"]
};
}
```
--------------------------------
### Manually Set Execution Flow
Source: https://github.com/newcat/baklavajs/blob/master/docs/execution/forward.md
Demonstrates how to manually set the execution flow on a NodeInterface using setExecutionFlow. It's crucial to use NodeInterface for type safety.
```typescript
import { setExecutionFlow } from "@baklavajs/engine";
const intf = new NodeInterface()("Exec", false);
setExecutionFlow(intf);
```
--------------------------------
### Class-Based Node Definition (`Node`)
Source: https://context7.com/newcat/baklavajs/llms.txt
An alternative approach for defining nodes that require more control, such as dynamically adding interfaces in the constructor. This method requires calling `this.initializeIo()`.
```APIDOC
## Class-Based Node Definition (`Node`)
### Description
An alternative approach for nodes that need fine-grained control (e.g., dynamic interfaces added in the constructor). Requires calling `this.initializeIo()`.
### Usage
```ts
import { Node, NodeInterface, NumberInterface, CalculateFunction } from "baklavajs";
interface Inputs { a: number; b: number; }
interface Outputs { sum: number; }
export default class AddNode extends Node {
public type = "AddNode";
public title = "Add";
public inputs = {
a: new NumberInterface("A", 0),
b: new NumberInterface("B", 0),
};
public outputs = {
sum: new NodeInterface("Sum", 0),
};
constructor() {
super();
this.initializeIo(); // required
}
public calculate: CalculateFunction = ({ a, b }) => ({ sum: a + b });
}
```
```
--------------------------------
### Create a Number Interface
Source: https://github.com/newcat/baklavajs/blob/master/docs/nodes/pre-defined-interfaces.md
Use NumberInterface for numeric input that accepts decimal values. Optional minimum and maximum values can be provided.
```javascript
import { NumberInterface } from "baklavajs";
// without min and max values
new NumberInterface("Name", 0.5);
// with min and max values
new NumberInterface("Name", 0.5, 0, 1);
```
--------------------------------
### Create a Slider Interface
Source: https://github.com/newcat/baklavajs/blob/master/docs/nodes/pre-defined-interfaces.md
Use SliderInterface to display a slider for numeric input. Minimum and maximum values are required.
```javascript
import { SliderInterface } from "baklavajs";
new SliderInterface("Name", 0.5, 0, 1);
```
--------------------------------
### Subgraph Toolbar Commands
Source: https://github.com/newcat/baklavajs/blob/master/docs/visual-editor/toolbar.md
Configure the commands available in the subgraph toolbar. You can include default commands like `SWITCH_TO_MAIN_GRAPH` or add your own custom subgraph-specific commands.
```typescript
import { TOOLBAR_SUBGRAPH_COMMANDS } from "@baklavajs/renderer-vue";
// Default subgraph commands include:
// - SAVE_SUBGRAPH: Save changes to the subgraph
// - SWITCH_TO_MAIN_GRAPH: Return to the main graph
// You can customize the subgraph toolbar:
baklavaView.settings.toolbar.subgraphCommands = [
TOOLBAR_SUBGRAPH_COMMANDS.SWITCH_TO_MAIN_GRAPH,
// Add your custom subgraph commands here
];
```
--------------------------------
### Create a Text Interface
Source: https://github.com/newcat/baklavajs/blob/master/docs/nodes/pre-defined-interfaces.md
Use TextInterface to display a value as read-only text. The name may change in future versions.
```javascript
import { TextInterface } from "baklavajs";
new TextInterface("Name", "Hello World!");
```
--------------------------------
### Forward Engine
Source: https://context7.com/newcat/baklavajs/llms.txt
Executes the graph following explicit execution-flow connections, similar to Unreal Blueprints. It supports branching, loops, and event-driven execution.
```APIDOC
## Forward Engine — Execution-Flow Graph
Executes the graph following explicit execution-flow connections (like Unreal Blueprints). Supports branching, loops, and event-driven execution.
```ts
import { Editor } from "@baklavajs/core";
import { ForwardEngine, ExecutionFlowInterface, ForwardCalculationContext } from "@baklavajs/engine";
import { defineNode, NodeInterface } from "baklavajs";
// A branch node: routes execution based on a boolean condition
const BranchNode = defineNode({
type: "BranchNode",
inputs: {
execIn: () => new ExecutionFlowInterface("Exec"),
condition: () => new NodeInterface("Condition", false),
},
outputs: {
trueOut: () => new ExecutionFlowInterface("True"),
falseOut: () => new ExecutionFlowInterface("False"),
},
calculate({ condition }) {
return { trueOut: !!condition, falseOut: !condition };
},
});
// A for-loop node using executeOutput
const ForLoopNode = defineNode({
type: "ForLoopNode",
inputs: {
execIn: () => new ExecutionFlowInterface("Exec"),
start: () => new NodeInterface("Start", 0),
end: () => new NodeInterface("End", 5),
},
outputs: {
loopBody: () => new ExecutionFlowInterface("Loop Body"),
completed: () => new ExecutionFlowInterface("Completed"),
index: () => new NodeInterface("Index", 0),
},
async calculate({ start, end }, context) {
const { executeOutput } = context as ForwardCalculationContext;
for (let i = start; i < end; i++) {
await executeOutput("loopBody", { index: i });
}
return { loopBody: false, completed: true, index: end };
},
});
// --- Setup ---
const editor = new Editor();
editor.registerNodeType(BranchNode);
editor.registerNodeType(ForLoopNode);
const engine = new ForwardEngine(editor);
// Manual execution starting from a specific node
const triggerNode = editor.graph.nodes[0];
const result = await engine.runOnce(undefined, triggerNode, undefined);
// Automatic: re-runs from the changed node on every value change
engine.start();
```
```
--------------------------------
### Create an Integer Interface
Source: https://github.com/newcat/baklavajs/blob/master/docs/nodes/pre-defined-interfaces.md
Use IntegerInterface for numeric input accepting only integers. Optional minimum and maximum values can be provided.
```javascript
import { IntegerInterface } from "baklavajs";
// without min and max values
new IntegerInterface("Name", 5);
// with min and max values
new IntegerInterface("Name", 5, 0, 10);
```
--------------------------------
### Configure Visual Editor Settings
Source: https://context7.com/newcat/baklavajs/llms.txt
Mutate the `settings` property to adjust visual behavior like connection display, line styles, minimap, node resizing, zoom range, and context menu items.
```typescript
import { useBaklava } from "@baklavajs/renderer-vue";
const baklava = useBaklava();
// Show connection values on hover
baklava.settings.displayValueOnHover = true;
// Use straight lines instead of curves for connections
baklava.settings.useStraightConnections = true;
// Enable the minimap overlay
baklava.settings.enableMinimap = true;
// Allow nodes to be resized by the user
baklava.settings.nodes.resizable = true;
// Customize zoom range
baklava.settings.panZoom.minScale = 0.1;
baklava.settings.panZoom.maxScale = 5;
// Add entries to the right-click context menu
baklava.settings.contextMenu.additionalItems = [
{ label: "Fit to screen", action: () => baklava.commandHandler.executeCommand("ZOOM_TO_FIT") },
];
```
--------------------------------
### Create a Select Interface
Source: https://github.com/newcat/baklavajs/blob/master/docs/nodes/pre-defined-interfaces.md
Use SelectInterface to display a dropdown for single value selection. It accepts an array of strings or objects with 'text' and 'value' properties.
```javascript
import { SelectInterface } from "baklavajs";
new SelectInterface("Name", "Add", ["Add", "Subtract"]);
new SelectInterface("Name", 1, [
{ text: "A", value: 1 },
{ text: "B", value: 2 },
{ text: "C", value: 3 },
]);
```
--------------------------------
### Create and Register a New BaklavaJS Command
Source: https://github.com/newcat/baklavajs/blob/master/docs/visual-editor/commands.md
Define and register a custom command with specific execution logic. The command can accept arguments and return a value, with optional 'canExecute' validation.
```typescript
import type { ICommandHandler, ICommand } from "../commands";
// first type argument is the return value of the command
// second type argument are the inputs to the command
type MyCommand = ICommand;
viewModel.commandHandler.registerCommand("MyCommand", {
canExecute: (id) => !!viewModel.displayedGraph.findNodeById(id),
execute: (id) => {
const node = viewModel.displayedGraph.findNodeById(id)!;
viewModel.displayedGraph.removeNode(node);
return node.type;
},
});
// for invoking this command:
viewModel.commandHandler.executeCommand("MyCommand", true, "myNodeId");
```
--------------------------------
### Initialize and Register Interface Types Plugin
Source: https://github.com/newcat/baklavajs/blob/master/docs/nodes/interface-types.md
Instantiate `BaklavaInterfaceTypes` with the editor instance and register your custom types. This plugin manages the type checking and conversion logic for node interfaces.
```javascript
// In your App.vue or wherever you use the
import { BaklavaInterfaceTypes, NodeInterfaceType } from "@baklavajs/interface-types";
import { stringType, numberType, booleanType } from "./interfaceTypes";
const baklavaView = useBaklava();
const editor = baklavaView.editor;
const nodeInterfaceTypes = new BaklavaInterfaceTypes(editor, { viewPlugin: baklavaView });
nodeInterfaceTypes.addTypes(stringType, numberType, booleanType);
```
--------------------------------
### Execute Existing BaklavaJS Commands
Source: https://github.com/newcat/baklavajs/blob/master/docs/visual-editor/commands.md
Invoke a predefined command by its string name using the command handler. Ensure the command exists before execution.
```typescript
import { Commands } from "@baklavajs/renderer-vue";
viewModel.commandHandler.executeCommand(Commands.UNDO_COMMAND);
```
--------------------------------
### Create a TextareaInput Interface
Source: https://github.com/newcat/baklavajs/blob/master/docs/nodes/pre-defined-interfaces.md
Use TextareaInputInterface to display a textarea field for multi-line user input.
```javascript
import { TextareaInputInterface } from "baklavajs";
new TextareaInputInterface("Name", "Edit me");
```
--------------------------------
### Subscribe to Node Events with Entity Argument
Source: https://github.com/newcat/baklavajs/blob/master/docs/event-system.md
Demonstrates subscribing to an event on all nodes using proxied events. The listener receives both the event data and the emitting node.
```typescript
const token = Symbol()
editor.nodeEvents.update.subscribe(token, (data, node) => {
// put your code here
});
```
--------------------------------
### Provide Global Calculation Data (Manual Mode)
Source: https://github.com/newcat/baklavajs/blob/master/docs/execution/setup.md
Passes global data to the engine when running calculations manually using `runOnce`. This data is available in the `calculate` function of each node.
```typescript
engine.runOnce({ offset: 5 });
```
--------------------------------
### Dependency Engine
Source: https://context7.com/newcat/baklavajs/llms.txt
Topologically sorts the graph and runs every node's `calculate` function in order. This engine is equivalent to the Baklava v1 engine behavior.
```APIDOC
## Dependency Engine — Graph Execution
Topologically sorts the graph and runs every node's `calculate` function in order. Equivalent to the Baklava v1 engine behavior.
```ts
import { Editor } from "@baklavajs/core";
import { DependencyEngine, applyResult, allowMultipleConnections } from "@baklavajs/engine";
import { defineNode, NodeInterface, NumberInterface } from "baklavajs";
// Node that accepts multiple incoming connections on one input
const SumNode = defineNode({
type: "SumNode",
inputs: {
values: () => new NodeInterface("Values", []).use(allowMultipleConnections),
},
outputs: {
total: () => new NodeInterface("Total", 0),
},
calculate({ values }) {
return { total: values.reduce((a, b) => a + b, 0) };
},
});
// --- Setup ---
const editor = new Editor();
editor.registerNodeType(SumNode);
const engine = new DependencyEngine(editor);
// Write results back to node interfaces so they display in the graph
const token = Symbol("applyResult");
engine.events.afterRun.subscribe(token, (result) => {
engine.pause();
applyResult(result, editor);
engine.resume();
});
// Auto-run whenever the graph changes
engine.start();
// Or run once manually, with optional global calculation data
const result = await engine.runOnce({ timestamp: Date.now() });
// result is a Map
```
```
--------------------------------
### Provide Global Calculation Data (Automatic Mode)
Source: https://github.com/newcat/baklavajs/blob/master/docs/execution/setup.md
Registers a hook to provide global data for every automatic calculation triggered after `engine.start()`. This hook is called before each calculation cycle.
```typescript
const token = Symbol("token");
engine.hooks.gatherCalculationData.subscribe(token, () => {
return { offset: 5 };
});
engine.start();
```
--------------------------------
### Add Custom Toolbar Command
Source: https://github.com/newcat/baklavajs/blob/master/docs/visual-editor/toolbar.md
Integrate custom functionality into the toolbar. Register a command with the command handler, then define its appearance and add it to the toolbar's command list.
```typescript
import { useBaklava, DEFAULT_TOOLBAR_COMMANDS } from "@baklavajs/renderer-vue";
import { defineComponent, h, markRaw } from "vue";
const baklavaView = useBaklava();
// 1. Register a custom command
const CLEAR_ALL_COMMAND = "CLEAR_ALL";
baklavaView.commandHandler.registerCommand(CLEAR_ALL_COMMAND, {
execute: () => {
// Clear all nodes from the graph
baklavaView.displayedGraph.nodes.forEach((node) => {
baklavaView.displayedGraph.removeNode(node);
});
},
// Optional: Define when the command can be executed
canExecute: () => baklavaView.displayedGraph.nodes.length > 0,
});
// 2. & 3. Add the command to the toolbar
baklavaView.settings.toolbar.commands = [
...DEFAULT_TOOLBAR_COMMANDS,
{
command: CLEAR_ALL_COMMAND,
title: "Clear All", // Tooltip text
icon: markRaw(
defineComponent(() => {
return () => h("div", "Clear All");
}),
),
},
];
```
--------------------------------
### Extend Editor Actions with Custom Commands
Source: https://context7.com/newcat/baklavajs/llms.txt
Register custom commands or invoke built-in commands like undo/redo and zoom-to-fit using the `commandHandler`. Custom commands can define `canExecute` and `execute` logic.
```typescript
import { Commands } from "@baklavajs/renderer-vue";
import type { ICommand } from "@baklavajs/renderer-vue";
const baklava = useBaklava();
const { commandHandler, displayedGraph } = baklava;
// Execute a built-in command
commandHandler.executeCommand(Commands.UNDO_COMMAND);
commandHandler.executeCommand(Commands.REDO_COMMAND);
// Register a custom command
// Type params:
type DeleteNodeCommand = ICommand;
commandHandler.registerCommand("DELETE_NODE_BY_ID", {
canExecute: (id) => !!displayedGraph.findNodeById(id),
execute: (id) => {
const node = displayedGraph.findNodeById(id)!;
displayedGraph.removeNode(node);
return node.type; // returned to the caller
},
});
// Invoke the custom command
const deletedType = commandHandler.executeCommand(
"DELETE_NODE_BY_ID",
true, // check canExecute before running
"node-id-42"
);
console.log("Deleted node of type:", deletedType);
```
--------------------------------
### Custom Icon with SVG Library
Source: https://github.com/newcat/baklavajs/blob/master/docs/visual-editor/toolbar.md
Utilize icons from an SVG library like Heroicons for custom toolbar command icons. Import the desired icon component and pass it to the `icon` property, wrapped in `markRaw`.
```typescript
import { markRaw } from "vue";
import { TrashIcon } from "@heroicons/vue/24/outline";
// Use the imported icon component directly
{
command: CLEAR_ALL_COMMAND,
title: "Clear All",
icon: markRaw(TrashIcon)
}
```
--------------------------------
### Forward Engine for Execution-Flow Graphs
Source: https://context7.com/newcat/baklavajs/llms.txt
The Forward Engine executes graphs based on explicit execution-flow connections, supporting branching and loops. Use `executeOutput` for custom execution logic.
```typescript
import { Editor } from "@baklavajs/core";
import { ForwardEngine, ExecutionFlowInterface, ForwardCalculationContext } from "@baklavajs/engine";
import { defineNode, NodeInterface } from "baklavajs";
// A branch node: routes execution based on a boolean condition
const BranchNode = defineNode({
type: "BranchNode",
inputs: {
execIn: () => new ExecutionFlowInterface("Exec"),
condition: () => new NodeInterface("Condition", false),
},
outputs: {
trueOut: () => new ExecutionFlowInterface("True"),
falseOut: () => new ExecutionFlowInterface("False"),
},
calculate({ condition }) {
return { trueOut: !!condition, falseOut: !condition };
},
});
// A for-loop node using executeOutput
const ForLoopNode = defineNode({
type: "ForLoopNode",
inputs: {
execIn: () => new ExecutionFlowInterface("Exec"),
start: () => new NodeInterface("Start", 0),
end: () => new NodeInterface("End", 5),
},
outputs: {
loopBody: () => new ExecutionFlowInterface("Loop Body"),
completed: () => new ExecutionFlowInterface("Completed"),
index: () => new NodeInterface("Index", 0),
},
async calculate({ start, end }, context) {
const { executeOutput } = context as ForwardCalculationContext;
for (let i = start; i < end; i++) {
await executeOutput("loopBody", { index: i });
}
return { loopBody: false, completed: true, index: end };
},
});
// --- Setup ---
const editor = new Editor();
editor.registerNodeType(BranchNode);
editor.registerNodeType(ForLoopNode);
const engine = new ForwardEngine(editor);
// Manual execution starting from a specific node
const triggerNode = editor.graph.nodes[0];
const result = await engine.runOnce(undefined, triggerNode, undefined);
// Automatic: re-runs from the changed node on every value change
engine.start();
```
--------------------------------
### Define Node with defineNode()
Source: https://context7.com/newcat/baklavajs/llms.txt
Define a node type using the `defineNode()` function, providing its type, title, inputs, outputs, and calculation logic. This is the recommended approach for most nodes.
```typescript
// MathNode.ts
import { defineNode, NodeInterface, NumberInterface, SelectInterface } from "baklavajs";
export default defineNode({
type: "MathNode",
title: "Math",
inputs: {
number1: () => new NumberInterface("Number 1", 1),
number2: () => new NumberInterface("Number 2", 10),
operation: () =>
new SelectInterface("Operation", "Add", ["Add", "Subtract", "Multiply"]).setPort(false),
},
outputs: {
result: () => new NodeInterface("Result", 0),
},
calculate({ number1, number2, operation }) {
if (operation === "Add") return { result: number1 + number2 };
if (operation === "Subtract") return { result: number1 - number2 };
if (operation === "Multiply") return { result: number1 * number2 };
throw new Error(`Unknown operation: ${operation}`);
},
});
```
--------------------------------
### Extend NodeInterface Class
Source: https://github.com/newcat/baklavajs/blob/master/docs/nodes/interfaces.md
Create custom node interface classes by extending `NodeInterface` to encapsulate common configurations like components and middleware. This reduces code duplication.
```javascript
import { markRaw } from "vue";
import { NodeInterface, setType } from "baklavajs";
import MyComponent from "MyComponent.vue";
class MyNodeInterface extends NodeInterface {
constructor(name: string) {
super(name, 0);
this.setComponent(markRaw(MyComponent));
// this is just an example, the interface types plugin works a bit different
this.use(setType, "number");
}
}
const i = new MyNodeInterface("My Interface");
i.value; // -> 0
i.component === MyComponent; // -> true
i.type; // -> "number"
```
--------------------------------
### Replace UI Components with Custom Slots
Source: https://context7.com/newcat/baklavajs/llms.txt
Utilize named slots on `` to replace built-in UI components like node renderers or the entire toolbar with custom Vue components.
```vue
```
--------------------------------
### Define Dynamic Node with Changing Interfaces
Source: https://context7.com/newcat/baklavajs/llms.txt
Use `defineDynamicNode` to create nodes where inputs/outputs change at runtime. Requires an `onUpdate` function to specify the dynamic interface state based on static inputs.
```typescript
import { defineDynamicNode, NodeInterface, NumberInterface, SelectInterface, TextInputInterface } from "baklavajs";
export default defineDynamicNode({
type: "DynamicMathNode",
inputs: {
operation: () => new SelectInterface("Operation", "Add", ["Add", "Subtract", "Sine"]).setPort(false),
},
outputs: {
result: () => new NodeInterface("Result", 0),
},
onUpdate({ operation }) {
// "Sine" only needs one input; binary operations need two
if (operation === "Sine") {
return { inputs: { a: () => new NumberInterface("Value", 0) } };
}
return {
inputs: {
a: () => new NumberInterface("A", 0),
b: () => new NumberInterface("B", 0),
},
};
},
calculate(inputs) {
switch (inputs.operation) {
case "Add": return { result: inputs.a + inputs.b };
case "Subtract": return { result: inputs.a - inputs.b };
case "Sine": return { result: Math.sin(inputs.a) };
default: throw new Error("Unknown operation");
}
},
});
```
--------------------------------
### Create a Checkbox Interface
Source: https://github.com/newcat/baklavajs/blob/master/docs/nodes/pre-defined-interfaces.md
Use CheckboxInterface to display a checkbox. It expects a boolean value.
```javascript
import { CheckboxInterface } from "baklavajs";
new CheckboxInterface("Name", true);
```
--------------------------------
### BaklavaJS Event System
Source: https://context7.com/newcat/baklavajs/llms.txt
Utilize BaklavaEvent and PreventableBaklavaEvent for a token-based pub/sub system. Subscribe to events with tokens for easy unsubscription and use preventable events to cancel actions.
```typescript
import { BaklavaEvent, PreventableBaklavaEvent } from "@baklavajs/events";
// Normal event
const onDataChanged = new BaklavaEvent(null);
const token = Symbol("myToken");
onDataChanged.subscribe(token, (value) => console.log("Value:", value));
onDataChanged.emit(42); // → "Value: 42"
onDataChanged.unsubscribe(token);
onDataChanged.emit(99); // (no output — unsubscribed)
// Preventable event — fired before the action so it can be cancelled
const beforeConnect = new PreventableBaklavaEvent(null);
const guardToken = Symbol("guard");
beforeConnect.subscribe(guardToken, (connectionId, prevent) => {
if (connectionId === "forbidden") {
prevent();
}
});
const wasPrevented = beforeConnect.emit("forbidden"); // → true
const allowed = beforeConnect.emit("ok"); // → false
// Real usage: prevent specific connections in the graph
const editor = baklava.editor;
editor.graphEvents.beforeAddConnection.subscribe(guardToken, (conn, prevent) => {
if (conn.from.id === "restricted-output") {
prevent();
}
});
// Listen to events on ALL nodes via proxied events
const updateToken = Symbol("update");
editor.nodeEvents.update.subscribe(updateToken, (data, node) => {
console.log(`Node ${node.id} updated`, data);
});
// Global calculation data hook
engine.hooks.gatherCalculationData.subscribe(Symbol(), () => ({
frameTime: performance.now(),
}));
```
--------------------------------
### Backend Usage of BaklavaInterfaceTypes
Source: https://github.com/newcat/baklavajs/blob/master/docs/nodes/interface-types.md
Instantiates BaklavaInterfaceTypes with only the editor instance, omitting the useBaklava return value for backend-only calculations.
```javascript
new BaklavaInterfaceTypes(editor);
```