Skip to content

Commit

Permalink
add config to hex
Browse files Browse the repository at this point in the history
  • Loading branch information
CenekSanzak committed Feb 18, 2025
1 parent df1f0c4 commit 216be1e
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 16 deletions.
32 changes: 22 additions & 10 deletions src/components/Workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
OperationTags,
outputTypes,
IOTypes,
ConfigValues,
} from "@/operations/types";
import { operations } from "@/operations/operations";
import {
Expand Down Expand Up @@ -87,21 +88,32 @@ const Workspace: React.FC = () => {
state.edges
);
const sortedConnectedNodes = topologicalSort(connectedNodes, state.edges);

const lastConnectedNode =
sortedConnectedNodes[sortedConnectedNodes.length - 1];

const lastNodeY = lastConnectedNode
? lastConnectedNode.position.y +
(lastConnectedNode.measured?.height || 0)
: 0;
const lastNodeX = lastConnectedNode ? lastConnectedNode.position.x : 250;

const newNode: Node = {
id: generateShortId(operation.id),
type: "custom",
data: operation,
position: { x: lastNodeX, y: lastNodeY + 80 },
data: {
...operation,
onConfigChange: (newConfig: ConfigValues) => {
if (operation.funcBuilder) {
dispatch({
type: "UPDATE_NODE_CONFIG",
nodeId: newNode.id,
config: newConfig,
});
}
},
},
position: {
x: lastConnectedNode ? lastConnectedNode.position.x : 250,
y: lastConnectedNode
? lastConnectedNode.position.y +
(lastConnectedNode.measured?.height || 0) +
80
: 10,
},
};

if (operation.tags.includes(OperationTags.IO)) {
Expand Down Expand Up @@ -228,7 +240,7 @@ const Workspace: React.FC = () => {
}, [state.nodes, state.edges, state.selectedNodeId, state.dirtyNodes]);

const debouncedCalculate = useMemo(
() => debounce(calculate, 250),
() => debounce(calculate, 100),
[calculate]
);

Expand Down
1 change: 1 addition & 0 deletions src/components/flow/FlowGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export const FlowGraph = React.memo<FlowGraphProps>(
onNodesDelete={handleNodesDelete}
onNodeClick={(_, node) => onNodeClick?.(node.id)}
fitView
proOptions={{ hideAttribution: true }}
>
<Background variant={BackgroundVariant.Dots} gap={12} size={1} />
<Controls />
Expand Down
60 changes: 58 additions & 2 deletions src/nodes/customNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ const propsAreEqual = (
prevProps.id === nextProps.id &&
prevProps.selected === nextProps.selected &&
prevProps.data.value === nextProps.data.value &&
prevProps.data.outputValues === nextProps.data.outputValues
prevProps.data.outputValues === nextProps.data.outputValues &&
prevProps.data.configValues === nextProps.data.configValues
);
};

const CustomNode = ({ id, data, selected }: CustomNodeProps) => {
const input_length = data.inputs ? Object.keys(data.inputs).length : 0;
const output_length = data.outputs ? Object.keys(data.outputs).length : 0;

const inputHandles = useMemo(() => {
if (!data.inputs) return null;
return Object.entries(data.inputs).map(([key, type], index) => (
Expand Down Expand Up @@ -75,6 +75,61 @@ const CustomNode = ({ id, data, selected }: CustomNodeProps) => {
));
}, [data.outputs, output_length]);

const configInputs = useMemo(() => {
if (!data.configValues) return null;
return (
<div className="mt-2 border-t pt-2">
{Object.entries(data.configValues).map(([key, value]) => (
<div key={key} className="flex items-center text-xs">
<label className="mr-2">{key}:</label>
{typeof value === "boolean" ? (
<input
type="checkbox"
checked={value}
onChange={(e) => {
if (data.onConfigChange) {
data.onConfigChange({
...data.configValues,
[key]: e.target.checked,
});
}
}}
/>
) : typeof value === "number" ? (
<input
type="number"
value={value}
onChange={(e) => {
if (data.onConfigChange) {
data.onConfigChange({
...data.configValues,
[key]: Number(e.target.value),
});
}
}}
className="w-16 px-1"
/>
) : (
<input
type="text"
value={value}
onChange={(e) => {
if (data.onConfigChange) {
data.onConfigChange({
...data.configValues,
[key]: e.target.value,
});
}
}}
className="w-24 px-1"
/>
)}
</div>
))}
</div>
);
}, [data]);

const nodeClassName = useMemo(
() =>
`p-4 border rounded shadow-md bg-white items-center cursor-pointer hover:bg-gray-50 ${
Expand All @@ -91,6 +146,7 @@ const CustomNode = ({ id, data, selected }: CustomNodeProps) => {
</div>
{inputHandles}
<div className="mt-2">{data.value?.slice(0, 30) || ""}</div>
{configInputs}
{outputHandles}
</div>
);
Expand Down
19 changes: 18 additions & 1 deletion src/operations/string/encoding.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { Operation, OperationTags, IOTypes } from "@/operations/types";
import {
Operation,
OperationTags,
IOTypes,
ConfigValues,
} from "@/operations/types";
import {
encode_base16,
decode_base16,
Expand All @@ -10,6 +15,7 @@ import {
decode_base64_standard,
encode_base64_url,
decode_base64_url,
build_base16_encoder,
} from "wasm";
import { validateOutputTypeStringToString } from "@/operations/string/utils";

Expand All @@ -19,6 +25,17 @@ export const Base16Encode: Operation = {
description: "Encodes the input string to Base16 (hexadecimal)",
link: "https://en.wikipedia.org/wiki/Hexadecimal",
value: "",
configValues: {
uppercase: false,
},
funcBuilder: (config?: ConfigValues) => {
const builtFunction = build_base16_encoder(JSON.stringify(config || {}));
return validateOutputTypeStringToString(
(input: string) => builtFunction(input),
[IOTypes.Text],
[IOTypes.Text]
);
},
func: validateOutputTypeStringToString(
encode_base16,
[IOTypes.Text],
Expand Down
7 changes: 7 additions & 0 deletions src/operations/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export enum IOTypes {
Binary = "Binary",
}

export type ConfigValues = {
[key: string]: string | number | boolean;
};

export type outputTypes = string | number | number[] | string[];

export type OperationFunction = (...args: outputTypes[]) => outputTypes[];
Expand All @@ -24,11 +28,14 @@ export interface Operation {
description: string;
value: string;
outputValues?: { [key: string]: outputTypes };
configValues?: ConfigValues;
id: string;
funcBuilder?: (configValues?: ConfigValues) => OperationFunction;
func: OperationFunction;
tags: OperationTags[];
inputs: { [key: string]: IOTypes };
outputs: { [key: string]: IOTypes };
link?: string;
onConfigChange?: (newConfig: ConfigValues) => void;
[key: string]: unknown;
}
33 changes: 31 additions & 2 deletions src/state/graphReducer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Operation } from "@/operations/types";
import { ConfigValues, Operation } from "@/operations/types";
import {
Edge,
Node,
Expand Down Expand Up @@ -36,7 +36,8 @@ export type GraphAction =
| { type: "APPLY_NODE_CHANGES"; changes: NodeChange[] }
| { type: "APPLY_EDGE_CHANGES"; changes: EdgeChange[] }
| { type: "SET_EDGES_AND_NODES"; edges: Edge[]; nodes: Node[] }
| { type: "SET_NODES_AND_CLEAR_DIRTY"; nodes: Node[] };
| { type: "SET_NODES_AND_CLEAR_DIRTY"; nodes: Node[] }
| { type: "UPDATE_NODE_CONFIG"; nodeId: string; config: ConfigValues };

export const initialGraphState: GraphState = {
nodes: [],
Expand Down Expand Up @@ -157,6 +158,34 @@ export const graphReducer = (
dirtyNodes: new Set(),
};

case "UPDATE_NODE_CONFIG": {
const node = state.nodes.find((n) => n.id === action.nodeId);
const operation = node?.data as Operation;
const funcBuilder = operation?.funcBuilder;
if (typeof funcBuilder !== "function") return state;

const newNodes = state.nodes.map((node) =>
node.id === action.nodeId
? {
...node,
data: {
...node.data,
configValues: action.config,
func: funcBuilder(action.config),
},
}
: node
);

const affectedNodes = getDownstreamNodes(state.edges, action.nodeId);
affectedNodes.push(action.nodeId);
return {
...state,
nodes: newNodes,
dirtyNodes: new Set([...state.dirtyNodes, ...affectedNodes]),
};
}

default:
return state;
}
Expand Down
2 changes: 2 additions & 0 deletions wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ base85 = "1.0.0"
md-5 = "0.10.6"
sha1 = "0.10.6"
sha2 = "0.10.6"
serde_json = "1.0.138"
js-sys = "0.3.77"

[dev-dependencies]
wasm-bindgen-test = "0.3.34"
Expand Down
22 changes: 21 additions & 1 deletion wasm/src/string_ops/encoding.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,34 @@
use wasm_bindgen::prelude::*;
use base64::{Engine as _, engine::general_purpose};
use data_encoding::{HEXLOWER as BASE16, BASE32};
use data_encoding::{HEXLOWER as BASE16, BASE32, HEXUPPER};
use base85::{encode, decode};
use crate::utils::process_lines;
use serde_json::Value;
use wasm_bindgen::JsValue;

#[wasm_bindgen]
pub fn encode_base16(s: &str) -> Vec<String> {
vec![process_lines(s, |line| BASE16.encode(line.as_bytes()))]
}

#[wasm_bindgen]
pub fn build_base16_encoder(config: &str) -> Result<js_sys::Function, JsValue> {
let config: Value = serde_json::from_str(config)
.map_err(|e| JsValue::from_str(&format!("Invalid config JSON: {}", e)))?;

let uppercase = config.get("uppercase")
.and_then(|v| v.as_bool())
.unwrap_or(false);

let encoder = if uppercase { HEXUPPER } else { BASE16 };

let closure: Closure<dyn Fn(String) -> Vec<String>> = Closure::new(move |s: String| {
vec![process_lines(&s, |line| encoder.encode(line.as_bytes()))]
});

Ok(closure.into_js_value().unchecked_into::<js_sys::Function>())
}

#[wasm_bindgen]
pub fn decode_base16(s: &str) -> Vec<String> {
vec![process_lines(s, |line| {
Expand Down

0 comments on commit 216be1e

Please sign in to comment.