diff --git a/frontend/realtime/src/KonvaApp/Engine.ts b/frontend/realtime/src/KonvaApp/Engine.ts
index c9d19facfb..7dde8a037f 100644
--- a/frontend/realtime/src/KonvaApp/Engine.ts
+++ b/frontend/realtime/src/KonvaApp/Engine.ts
@@ -28,6 +28,7 @@ import { LoadingVideosProvider } from "./EngineUtitlities/LoadingVideosProvider"
import { VideoExtractionHandler } from "./EngineUtitlities/VideoExtractionHandler/VideoExtractionHandler";
import { RealTimeDrawEngine, ShapeType } from "./RenderingPrimitives/RealTimeDrawEngine";
+import { NodeColor } from "~/signals/uiEvents/toolbarNode";
// for testing loading files from system
// import { FileUtilities } from "./FileUtilities/FileUtilities";
@@ -180,8 +181,8 @@ export class Engine {
this.setupEventSystem();
this.setAppMode(AppModes.SELECT);
-
-
+
+
}
@@ -364,7 +365,7 @@ export class Engine {
// Listen to Toolbar Main
uiEvents.toolbarMain.UNDO.onClick(() => this.undoStackManager.undo());
uiEvents.toolbarMain.REDO.onClick(() => this.undoStackManager.redo());
-
+
uiEvents.toolbarMain.SAVE.onClick(async (/*event*/) => {
await this.realTimeDrawEngine.saveOutput();
@@ -398,7 +399,7 @@ export class Engine {
this.addImage(image);
});
-
+
uiEvents.onAddTextToEngine((textdata) => {
this.addText(textdata);
@@ -430,7 +431,7 @@ export class Engine {
}
}
});
-
+
uiEvents.promptEvents.onPromptStrengthChanged((strength) => {
this.realTimeDrawEngine.currentStrength = strength;
});
@@ -439,13 +440,12 @@ export class Engine {
this.realTimeDrawEngine.currentPrompt = prompt;
});
- uiEvents.onAddShapeToEngine((shapeData)=> {
-
+ uiEvents.onAddShapeToEngine((shapeData) => {
switch (shapeData.shape) {
case "circle":
this.addShape(ShapeType.Circle, 100);
break;
- case "square":
+ case "square":
this.addShape(ShapeType.Square, 100);
break;
case "triangle":
@@ -454,6 +454,9 @@ export class Engine {
}
});
+ uiEvents.toolbarNode.color.onConfirmChanged((nodeColor) => {
+ this.changeNodeColor(nodeColor);
+ })
}
disableAllButtons() {
@@ -489,7 +492,7 @@ export class Engine {
}
// Sandbox is quickly a way to test your idea.
- public async sandbox() {}
+ public async sandbox() { }
public onMessage(event: MessageEvent) {
console.log("Message From Shared Worker");
@@ -536,6 +539,15 @@ export class Engine {
this.addKeyboardShortcuts();
}
+ private changeNodeColor(nodeColor: NodeColor) {
+ // Find the node that is selected with the id
+ const idSelector = '#' + nodeColor.kNodeId;
+ const selectedNode = this.mediaLayer.findOne(idSelector);
+
+ // TODO: Change the color of the node
+ // Turns out shapes are image nodes... how do we change the image's fill??
+ }
+
public addText(textNodeData: TextNodeData) {
const textNode = new TextNode({
textNodeData: textNodeData,
@@ -558,8 +570,10 @@ export class Engine {
selectionManagerRef: this.selectionManager
});
shapeNode.kNode.zIndex(1); // Replace desiredZIndex with a number
-
+
this.commandManager.createNode(shapeNode);
+ console.debug("Added shapenode:", shapeNode);
+ console.debug("Added node's ID:", shapeNode.kNode._id);
//this.realTimeDrawEngine.addNodes(shapeNode);
}
@@ -617,7 +631,7 @@ export class Engine {
if (
longestVideoNode === undefined ||
node.videoComponent.duration >
- longestVideoNode.videoComponent.duration
+ longestVideoNode.videoComponent.duration
) {
longestVideoNode = node;
}
diff --git a/frontend/realtime/src/KonvaApp/EngineUtitlities/CommandManager.ts b/frontend/realtime/src/KonvaApp/EngineUtitlities/CommandManager.ts
index bb008055f2..58fd59e3c4 100644
--- a/frontend/realtime/src/KonvaApp/EngineUtitlities/CommandManager.ts
+++ b/frontend/realtime/src/KonvaApp/EngineUtitlities/CommandManager.ts
@@ -58,6 +58,10 @@ export class CommandManager {
renderEngineRef: this.renderEngineRef,
});
this.undoStackManagerRef.executeCommand(command);
+
+ // Set the kNode's manual id to same as kNode's internal _id
+ // This helps identify the kNode in the Konva layer with find
+ node.kNode.id(node.kNode._id.toString());
}
deleteNodes() {
const nodes = this.selectionManagerRef.getSelectedNodes();
diff --git a/frontend/realtime/src/KonvaApp/NodesManagers/SelectionManager.ts b/frontend/realtime/src/KonvaApp/NodesManagers/SelectionManager.ts
index 999044072b..0ae333e798 100644
--- a/frontend/realtime/src/KonvaApp/NodesManagers/SelectionManager.ts
+++ b/frontend/realtime/src/KonvaApp/NodesManagers/SelectionManager.ts
@@ -9,6 +9,7 @@ import {
calculateContextualsPosition,
getImageNodeButtonStates,
getMultiSelectButtonStates,
+ getShapeNodeButtonStates,
getTextNodeButtonStates,
getVideoNodeButtonStates,
} from "./ToolbarNodeUtilities";
@@ -271,22 +272,31 @@ export class SelectionManager {
return; // no node
}
if (this.selectedNodes.size === 1) {
- if (node instanceof ImageNode || node instanceof ShapeNode) {
+ if (node instanceof ImageNode) {
showOrUpdate({
+ knodeIds: [node.kNode.id()],
locked: node.isLocked(),
buttonStates: getImageNodeButtonStates({ locked: node.isLocked() }),
+ });
+ }
+ else if (node instanceof ShapeNode) {
+ console.debug("Selected node is of type ShapeNode", node.kNode.id())
+ showOrUpdate({
+ knodeIds: [node.kNode.id()],
+ locked: node.isLocked(),
+ buttonStates: getShapeNodeButtonStates({ locked: node.isLocked() }),
color: node.getNodeData({ x: 0, y: 0 })?.textNodeData?.color ?? "#000000",
});
}
- if (node instanceof TextNode) {
+ else if (node instanceof TextNode) {
showOrUpdate({
- // TODO: Add ID to identify/mutate the color?
+ knodeIds: [node.kNode.id()],
locked: node.isLocked(),
buttonStates: getTextNodeButtonStates({ locked: node.isLocked() }),
-
+ color: node.getNodeData({ x: 0, y: 0 })?.textNodeData?.color ?? "#000000",
});
}
- if (node instanceof VideoNode) {
+ else if (node instanceof VideoNode) {
showOrUpdate({
locked: node.isLocked(),
buttonStates: getVideoNodeButtonStates({ locked: node.isLocked() }),
@@ -317,7 +327,7 @@ export class SelectionManager {
this.hideContextComponents();
return; // no nodes
}
-
+
// console.log("SelectionManager > updateContextComponents for node:", node);
const coord = calculateContextualsPosition(
this.nodeTransformerRef.getKonvaNode(),
diff --git a/frontend/realtime/src/KonvaApp/NodesManagers/ToolbarNodeUtilities.ts b/frontend/realtime/src/KonvaApp/NodesManagers/ToolbarNodeUtilities.ts
index 9b82c82d5d..8e09c585be 100644
--- a/frontend/realtime/src/KonvaApp/NodesManagers/ToolbarNodeUtilities.ts
+++ b/frontend/realtime/src/KonvaApp/NodesManagers/ToolbarNodeUtilities.ts
@@ -152,6 +152,50 @@ export function getVideoNodeButtonStates(
{} as ContextualToolbarProps["buttonStates"],
);
}
+
+export function getShapeNodeButtonStates(
+ props: { locked?: boolean | "unknown" } = {},
+) {
+ const ButtonNames = ToolbarNodeButtonNames;
+ return Object.values(ButtonNames).reduce(
+ (buttonStates, buttonName) => {
+ // transform button depends on locked state
+ if (props.locked !== undefined && buttonName === ButtonNames.TRANSFORM) {
+ buttonStates.TRANSFORM = {
+ disabled: props.locked === "unknown" || props.locked === true,
+ hidden: false,
+ active: true,
+ };
+ return buttonStates;
+ }
+
+ // hidden buttons
+ if (
+ buttonName === ButtonNames.AI_STYLIZE ||
+ buttonName === ButtonNames.SEGMENTATION ||
+ buttonName === ButtonNames.DOWNLOAD ||
+ buttonName === ButtonNames.CHROMA
+ ) {
+ buttonStates[buttonName] = {
+ disabled: true,
+ hidden: true,
+ active: false,
+ };
+ return buttonStates;
+ }
+
+ // all other buttons
+ buttonStates[buttonName] = {
+ disabled: false,
+ hidden: false,
+ active: false,
+ };
+ return buttonStates;
+ },
+ {} as ContextualToolbarProps["buttonStates"],
+ );
+}
+
export function getMultiSelectButtonStates(
props: { locked?: boolean | "unknown" } = {},
) {
diff --git a/frontend/realtime/src/KonvaApp/types/NodeData.ts b/frontend/realtime/src/KonvaApp/types/NodeData.ts
index dae440e821..fb220c43d1 100644
--- a/frontend/realtime/src/KonvaApp/types/NodeData.ts
+++ b/frontend/realtime/src/KonvaApp/types/NodeData.ts
@@ -26,6 +26,10 @@ export type VideoNodeData = {
extractionUrl?: string;
};
+export type ShapeNodeData = {
+ shape: "triangle" | "square" | "circle";
+}
+
export type NodeData = {
type: NodeType;
transform: TransformationData;
diff --git a/frontend/realtime/src/KonvaRootComponent/ContextualToolbarNode.tsx b/frontend/realtime/src/KonvaRootComponent/ContextualToolbarNode.tsx
index 16d32a0c0d..e02482ba7c 100644
--- a/frontend/realtime/src/KonvaRootComponent/ContextualToolbarNode.tsx
+++ b/frontend/realtime/src/KonvaRootComponent/ContextualToolbarNode.tsx
@@ -50,6 +50,20 @@ export const ContextualToolbarNode = () => {
};
}, []);
+ const handleColorChange = (color: string) => {
+ // Don't do anything if no node is known to be bound
+ if (uiAccess.toolbarNode.signal.value.knodeIds.length < 1) {
+ return;
+ }
+
+ // Update the UI component
+ uiAccess.toolbarNode.setColor(color);
+
+ // Signal the engine with the node ID (just grab the first -and expected only- node)
+ const nodeID = uiAccess.toolbarNode.signal.value.knodeIds[0];
+ dispatchUiEvents.toolbarNode.setSelectedColor({ kNodeId: nodeID, color });
+ }
+
const boundedTopPosition = Math.min(position.y, winSize.height - 164);
return (
{
dispatchUiEvents.toolbarNode.lock(e);
}}
color={uiAccess.toolbarNode.signal.value.color}
- onColorChange={(color) => { uiAccess.toolbarNode.setColor(color) }}
+ onColorChange={handleColorChange}
/>
);
diff --git a/frontend/realtime/src/components/features/ToolbarNode/ToolbarNode.tsx b/frontend/realtime/src/components/features/ToolbarNode/ToolbarNode.tsx
index b720864a8f..324aa4272a 100644
--- a/frontend/realtime/src/components/features/ToolbarNode/ToolbarNode.tsx
+++ b/frontend/realtime/src/components/features/ToolbarNode/ToolbarNode.tsx
@@ -97,7 +97,7 @@ export const ToolbarNode = ({
);
} else {
return (
-
+
)
}
})}
diff --git a/frontend/realtime/src/signals/uiAccess/toolbarNode.ts b/frontend/realtime/src/signals/uiAccess/toolbarNode.ts
index bb22159dc9..49308fcff1 100644
--- a/frontend/realtime/src/signals/uiAccess/toolbarNode.ts
+++ b/frontend/realtime/src/signals/uiAccess/toolbarNode.ts
@@ -10,7 +10,7 @@ type ButtonStates = {
};
};
export interface ContextualToolbarProps extends ContextualUi {
- knodeIds: number[];
+ knodeIds: string[];
disabled: boolean;
downloadUrl?: string;
locked: boolean | "unknown";
diff --git a/frontend/realtime/src/signals/uiEvents/addMedia.ts b/frontend/realtime/src/signals/uiEvents/addMedia.ts
index c546061025..25954e9fcd 100644
--- a/frontend/realtime/src/signals/uiEvents/addMedia.ts
+++ b/frontend/realtime/src/signals/uiEvents/addMedia.ts
@@ -1,5 +1,5 @@
import { signal, effect } from "@preact/signals-react";
-import { TextNodeData } from "~/KonvaApp/types";
+import { ShapeNodeData, TextNodeData } from "~/KonvaApp/types";
// ADDING IMAGES
const stagedImage = signal(null);
@@ -50,8 +50,6 @@ const onAddTextToEngine = (callback: (data: TextNodeData) => void) => {
};
// Add shapes
-// TODO: Remove this line and replace with actual shape data that engine needs
-type ShapeNodeData = any;
const stagedShape = signal(null);
const addShapeToEngine = (data: ShapeNodeData) => {
stagedShape.value = data;
diff --git a/frontend/realtime/src/signals/uiEvents/toolbarNode.ts b/frontend/realtime/src/signals/uiEvents/toolbarNode.ts
index 35c6f02219..cabe547a2d 100644
--- a/frontend/realtime/src/signals/uiEvents/toolbarNode.ts
+++ b/frontend/realtime/src/signals/uiEvents/toolbarNode.ts
@@ -44,6 +44,11 @@ const buttonEventsRecords = Object.values(ButtonNames).reduce(
const buttonEventsHandlers = Object.values(ButtonNames).reduce(
(acc, buttonName) => {
+ // We define custom events/handlers for color changes
+ if (buttonName === ButtonNames.COLOR) {
+ return acc;
+ }
+
acc[buttonName] = {
onClick: (callback: MouseEventHandler) => {
const { eventSignal, effectCleanup, lastEventTimestamp } =
@@ -111,9 +116,27 @@ const retryEventHandler = (callback: MouseEventHandler) => {
const retryDispatcher = (e: React.MouseEvent) => {
retryEvent.value = e;
};
+
+export type NodeColor = {
+ kNodeId: string;
+ color: string;
+}
+const selectedColorEvent = signal();
+const setSelectedColor = (nodeColor: NodeColor) => {
+ selectedColorEvent.value = nodeColor;
+}
+const onSelectedColorChanged = (callback: (color: NodeColor) => void) => {
+ effect(() => {
+ if (selectedColorEvent.value) {
+ callback(selectedColorEvent.value);
+ }
+ });
+};
+
export const dispatchers = {
lock: lockDispatcher,
retry: retryDispatcher,
+ setSelectedColor,
...buttonDispatchers,
};
export const eventsHandlers = {
@@ -123,5 +146,8 @@ export const eventsHandlers = {
retry: {
onClick: retryEventHandler,
},
+ color: {
+ onConfirmChanged: onSelectedColorChanged,
+ },
...buttonEventsHandlers,
};