From bc373d291eb478a5b9067cf914dfc05c2ec09d20 Mon Sep 17 00:00:00 2001 From: Ankitkumar Satapara Date: Wed, 29 Apr 2026 19:20:37 +0530 Subject: [PATCH] Implemented reading input from the textbox instead of connectors --- .../Nodify.Calculator/ConnectorViewModel.cs | 13 ++++ Examples/Nodify.Calculator/EditorView.xaml | 3 +- .../Execution/Handlers/NodeHandlers.cs | 74 ++++++++++++++++--- 3 files changed, 80 insertions(+), 10 deletions(-) diff --git a/Examples/Nodify.Calculator/ConnectorViewModel.cs b/Examples/Nodify.Calculator/ConnectorViewModel.cs index 32899c8..33f5444 100644 --- a/Examples/Nodify.Calculator/ConnectorViewModel.cs +++ b/Examples/Nodify.Calculator/ConnectorViewModel.cs @@ -31,6 +31,19 @@ namespace Nodify.Calculator .Then(() => ValueObservers.ForEach(o => o.Value = value)); } + private string _userInput = string.Empty; + /// + /// Free-form text the user types directly into the connector's input textbox + /// (visible only when the connector is not connected). Used by handlers like + /// NEW_OBJECT to read non-numeric values (strings, bools, JSON snippets, ...) + /// that (a double) cannot represent. + /// + public string UserInput + { + get => _userInput; + set => SetProperty(ref _userInput, value); + } + private ConnectorShape _shape; public ConnectorShape Shape { diff --git a/Examples/Nodify.Calculator/EditorView.xaml b/Examples/Nodify.Calculator/EditorView.xaml index e4b68d8..6b31234 100644 --- a/Examples/Nodify.Calculator/EditorView.xaml +++ b/Examples/Nodify.Calculator/EditorView.xaml @@ -273,7 +273,8 @@ - diff --git a/Examples/Nodify.Calculator/Execution/Handlers/NodeHandlers.cs b/Examples/Nodify.Calculator/Execution/Handlers/NodeHandlers.cs index c38ba22..35384c0 100644 --- a/Examples/Nodify.Calculator/Execution/Handlers/NodeHandlers.cs +++ b/Examples/Nodify.Calculator/Execution/Handlers/NodeHandlers.cs @@ -95,10 +95,23 @@ namespace Nodify.Calculator.Execution.Handlers var propPath = parenIdx > 0 ? title.Substring(0, parenIdx).Trim() : title.Trim(); if (string.IsNullOrEmpty(propPath)) continue; - var val = ctx.ReadInput(inp); + // If a wire is connected → read from upstream node. + // Otherwise fall back to whatever the user typed in the inline TextBox (UserInput). + // If neither is provided, skip the field so the JSON only carries explicit values. + string val; + if (inp.IsConnected) + { + val = ctx.ReadInput(inp); + } + else + { + if (string.IsNullOrWhiteSpace(inp.UserInput)) continue; + val = inp.UserInput; + } + ctx.Log($"[NEW OBJECT] {propPath} = {(val?.Length > 80 ? val.Substring(0, 80) + "..." : val ?? "null")}"); - SetNestedValue(root, propPath, val); + SetNestedValue(root, propPath, val, inp.DataType); } var json = root.ToString(Formatting.None); @@ -109,9 +122,10 @@ namespace Nodify.Calculator.Execution.Handlers /// /// Sets a value at a dot-notation path in a JObject, creating intermediate objects as needed. - /// e.g., "Address.Street" with value "Main St" → { "Address": { "Street": "Main St" } } + /// The leaf token is coerced according to so that string fields + /// remain JSON strings, numeric fields remain JSON numbers, etc. /// - private static void SetNestedValue(JObject root, string path, string val) + private static void SetNestedValue(JObject root, string path, string val, string dataType) { var parts = path.Split('.'); var current = root; @@ -129,14 +143,56 @@ namespace Nodify.Calculator.Execution.Handlers } var leaf = parts[parts.Length - 1]; - if (val != null) + current[leaf] = CoerceValue(val, dataType); + } + + private static JToken CoerceValue(string val, string dataType) + { + if (val == null) return JValue.CreateNull(); + + var dt = (dataType ?? "").Trim().ToLowerInvariant(); + + // Lists / nested objects / unspecified — try JSON parse first, fall back to string. + if (dt.StartsWith("list<") || dt == "object" || string.IsNullOrEmpty(dt)) { - try { current[leaf] = JToken.Parse(val); } - catch { current[leaf] = val; } + try { return JToken.Parse(val); } + catch { return new JValue(val); } } - else + + switch (dt) { - current[leaf] = null; + case "string": + case "char": + case "guid": + case "datetime": + return new JValue(val); + + case "bool": + case "boolean": + if (bool.TryParse(val, out var b)) return new JValue(b); + return new JValue(val); + + case "int": + case "long": + case "short": + case "byte": + if (long.TryParse(val, System.Globalization.NumberStyles.Any, + System.Globalization.CultureInfo.InvariantCulture, out var l)) + return new JValue(l); + return new JValue(val); + + case "double": + case "float": + case "decimal": + if (double.TryParse(val, System.Globalization.NumberStyles.Any, + System.Globalization.CultureInfo.InvariantCulture, out var d)) + return new JValue(d); + return new JValue(val); + + default: + // Custom model type referenced by name → expect a JSON object string. + try { return JToken.Parse(val); } + catch { return new JValue(val); } } } }