diff --git a/src/AdvancedCalculator/AdvancedCalculator.csproj b/src/AdvancedCalculator/AdvancedCalculator.csproj
index 086d785..8375ef8 100644
--- a/src/AdvancedCalculator/AdvancedCalculator.csproj
+++ b/src/AdvancedCalculator/AdvancedCalculator.csproj
@@ -8,6 +8,10 @@
+
+
+ Assets/Fonts/materialdesignicons-webfont.ttf
+
diff --git a/src/AdvancedCalculator/App.axaml b/src/AdvancedCalculator/App.axaml
index ed5dc8d..5e686cd 100644
--- a/src/AdvancedCalculator/App.axaml
+++ b/src/AdvancedCalculator/App.axaml
@@ -7,4 +7,9 @@
+
+
+
+ avares://AdvancedCalculator/Assets/Fonts#Material Design Icons
+
diff --git a/src/AdvancedCalculator/Converters/BoolToGridLengthConverter.cs b/src/AdvancedCalculator/Converters/BoolToGridLengthConverter.cs
new file mode 100644
index 0000000..d1de3fb
--- /dev/null
+++ b/src/AdvancedCalculator/Converters/BoolToGridLengthConverter.cs
@@ -0,0 +1,27 @@
+using System;
+using Avalonia.Data.Converters;
+using Avalonia;
+
+namespace AdvancedCalculator.Converters;
+
+public class BoolToGridLengthConverter : IValueConverter
+{
+ public static readonly BoolToGridLengthConverter Instance = new();
+
+ public object? Convert(object? value, Type targetType, object? parameter, System.Globalization.CultureInfo? culture)
+ {
+ var isOpen = value is bool b && b;
+ // Return star when true, otherwise zero pixels
+ return isOpen ? new GridLength(1, GridUnitType.Star) : new GridLength(0, GridUnitType.Pixel);
+ }
+
+ public object? ConvertBack(object? value, Type targetType, object? parameter, System.Globalization.CultureInfo? culture)
+ {
+ if (value is GridLength gl)
+ {
+ return gl.Value > 0;
+ }
+ return false;
+ }
+}
+
diff --git a/src/AdvancedCalculator/Models/FunctionDefinitionItem.cs b/src/AdvancedCalculator/Models/FunctionDefinitionItem.cs
new file mode 100644
index 0000000..4cfbb1d
--- /dev/null
+++ b/src/AdvancedCalculator/Models/FunctionDefinitionItem.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+
+namespace AdvancedCalculator.Models;
+
+public class FunctionDefinitionItem
+{
+ public string FunctionName { get; private set; } = string.Empty;
+ public string FunctionDescription { get; private set; } = string.Empty;
+ public string Icon { get; private set; } = string.Empty;
+ public IEnumerable> FunctionArguments { get; private set; } = new List>();
+
+ public static IEnumerable DefinedFunctions
+ {
+ get
+ {
+ yield return new FunctionDefinitionItem { FunctionName = "sin", Icon = IconFont.SineWave, FunctionDescription = "Returns the sine value of a given expression.", FunctionArguments = new[] { new KeyValuePair("expression", "An expression to compute.") } };
+ yield return new FunctionDefinitionItem { FunctionName = "cos", Icon = IconFont.CosineWave, FunctionDescription = "Returns the cosine value of a given expression.", FunctionArguments = new[] { new KeyValuePair("expression", "An expression to compute.") } };
+ yield return new FunctionDefinitionItem { FunctionName = "tan", Icon = IconFont.MathTan, FunctionDescription = "Returns the tangent value of a given expression.", FunctionArguments = new[] { new KeyValuePair("expression", "An expression to compute.") } };
+ yield return new FunctionDefinitionItem { FunctionName = "round", Icon = IconFont.RoundedCorner, FunctionDescription = "Rounds an expression to the nearest whole number.", FunctionArguments = new[] { new KeyValuePair("expression", "An expression to compute.") } };
+ yield return new FunctionDefinitionItem { FunctionName = "sqrt", Icon = IconFont.SquareRoot, FunctionDescription = "Returns the square root of a given expression.", FunctionArguments = new[] { new KeyValuePair("expression", "An expression to compute.") } };
+ yield return new FunctionDefinitionItem { FunctionName = "abs", Icon = IconFont.PlusCircle, FunctionDescription = "Returns the absolute value of a given expression.", FunctionArguments = new[] { new KeyValuePair("expression", "An expression to compute.") } };
+ yield return new FunctionDefinitionItem { FunctionName = "exp", Icon = IconFont.AlphaECircle, FunctionDescription = "Returns the constant e to a given power.", FunctionArguments = new[] { new KeyValuePair("power", "An expression to compute.") } };
+ yield return new FunctionDefinitionItem { FunctionName = "log", Icon = IconFont.MathLog, FunctionDescription = "Returns the log of the first expression to the base of the second expression.", FunctionArguments = new[] { new KeyValuePair("value", "An expression to compute."), new KeyValuePair("base", "An expression to compute.") } };
+ yield return new FunctionDefinitionItem { FunctionName = "precision", Icon = IconFont.DecimalIncrease, FunctionDescription = "Returns the value of expression1 to a given precision.", FunctionArguments = new[] { new KeyValuePair("value", "An expression to compute."), new KeyValuePair("precision", "An expression to compute.") } };
+ }
+ }
+}
+
diff --git a/src/AdvancedCalculator/Models/HistoryItem.cs b/src/AdvancedCalculator/Models/HistoryItem.cs
new file mode 100644
index 0000000..1f6c0ed
--- /dev/null
+++ b/src/AdvancedCalculator/Models/HistoryItem.cs
@@ -0,0 +1,8 @@
+namespace AdvancedCalculator.Models;
+
+public class HistoryItem
+{
+ public string Input { get; set; } = string.Empty;
+ public string Output { get; set; } = string.Empty;
+}
+
diff --git a/src/AdvancedCalculator/Models/IconFont.cs b/src/AdvancedCalculator/Models/IconFont.cs
new file mode 100644
index 0000000..07ffb75
--- /dev/null
+++ b/src/AdvancedCalculator/Models/IconFont.cs
@@ -0,0 +1,19 @@
+namespace AdvancedCalculator.Models;
+
+// Slimmed icon font mapping for required glyphs only
+public static class IconFont
+{
+ public const string ArrowRightDropCircle = "\U000f0059";
+ public const string DecimalIncrease = "\U000f01b5";
+ public const string Function = "\U000f0295";
+ public const string PlusCircle = "\U000f0417";
+ public const string RoundedCorner = "\U000f0607";
+ public const string SquareRoot = "\U000f0784";
+ public const string SineWave = "\U000f095b";
+ public const string Variable = "\U000f0ae7";
+ public const string AlphaECircle = "\U000f0bf8";
+ public const string MathTan = "\U000f0c98";
+ public const string MathLog = "\U000f1085";
+ public const string CosineWave = "\U000f1479";
+}
+
diff --git a/src/AdvancedCalculator/Models/VariableItem.cs b/src/AdvancedCalculator/Models/VariableItem.cs
new file mode 100644
index 0000000..22a900c
--- /dev/null
+++ b/src/AdvancedCalculator/Models/VariableItem.cs
@@ -0,0 +1,15 @@
+namespace AdvancedCalculator.Models;
+
+public class VariableItem
+{
+ public string VariableName { get; set; } = string.Empty;
+ public string Icon { get; set; } = string.Empty;
+ public string Value { get; set; } = string.Empty;
+
+ // True when this variable is defined by an equation/expression
+ public bool IsExpression { get; set; }
+
+ // Computed value for an expression variable (pretty-printed)
+ public string? ExpressionComputation { get; set; }
+}
+
diff --git a/src/AdvancedCalculator/Services/CalculatorService.cs b/src/AdvancedCalculator/Services/CalculatorService.cs
new file mode 100644
index 0000000..aaf47c2
--- /dev/null
+++ b/src/AdvancedCalculator/Services/CalculatorService.cs
@@ -0,0 +1,67 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using AdvancedCalculator.Models;
+
+// Intentionally keep the csmic/CSMic dependency surface in this file only.
+// This allows easy adaptation if the interpreter API changes.
+namespace AdvancedCalculator.Services;
+
+public class CalculatorService : ICalculatorService
+{
+ private readonly object _lock = new();
+
+ // We attempt to use the legacy namespace to maximize compatibility.
+ // If using the newer CSMic.StandardLibrary with different namespaces,
+ // update the using directives and type names here.
+ private readonly csmic.InputInterpreter _interpreter = new();
+
+ public Task InterpretAsync(string input)
+ {
+ return Task.Run(() =>
+ {
+ lock (_lock)
+ {
+ _interpreter.Interpret(input);
+ var output = _interpreter.Output ?? string.Empty;
+
+ // Build variables list
+ var variables = new List();
+ foreach (var kvp in _interpreter.Variables)
+ {
+ var name = kvp.Key;
+ var variable = kvp.Value;
+ var valueString = variable?.Value?.ToString() ?? string.Empty;
+
+ var isExpression = variable?.Type == csmic.VariableType.Equation;
+ var item = new VariableItem
+ {
+ VariableName = name,
+ Value = valueString,
+ IsExpression = isExpression,
+ ExpressionComputation = null,
+ Icon = IconFont.Variable
+ };
+
+ if (isExpression && !string.IsNullOrWhiteSpace(valueString))
+ {
+ // Compute the expression-based variable's current value
+ _interpreter.Interpret(valueString);
+ item.ExpressionComputation = _interpreter.Output ?? string.Empty;
+ item.Icon = IconFont.Function;
+ }
+
+ variables.Add(item);
+ }
+
+ return new InterpretResult
+ {
+ Output = output,
+ Variables = variables
+ };
+ }
+ });
+ }
+}
+
diff --git a/src/AdvancedCalculator/Services/ICalculatorService.cs b/src/AdvancedCalculator/Services/ICalculatorService.cs
new file mode 100644
index 0000000..bd0c0ec
--- /dev/null
+++ b/src/AdvancedCalculator/Services/ICalculatorService.cs
@@ -0,0 +1,17 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using AdvancedCalculator.Models;
+
+namespace AdvancedCalculator.Services;
+
+public interface ICalculatorService
+{
+ Task InterpretAsync(string input);
+}
+
+public class InterpretResult
+{
+ public string Output { get; set; } = string.Empty;
+ public IReadOnlyList Variables { get; set; } = new List();
+}
+
diff --git a/src/AdvancedCalculator/ViewModels/MainViewModel.cs b/src/AdvancedCalculator/ViewModels/MainViewModel.cs
index aba0c84..1611402 100644
--- a/src/AdvancedCalculator/ViewModels/MainViewModel.cs
+++ b/src/AdvancedCalculator/ViewModels/MainViewModel.cs
@@ -1,6 +1,64 @@
-namespace AdvancedCalculator.ViewModels;
+using System.Collections.ObjectModel;
+using System.Threading.Tasks;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using AdvancedCalculator.Models;
+using AdvancedCalculator.Services;
+
+namespace AdvancedCalculator.ViewModels;
public partial class MainViewModel : ViewModelBase
{
- public string Greeting => "Welcome to Avalonia!";
+ private readonly ICalculatorService _calculatorService;
+
+ public MainViewModel()
+ : this(new CalculatorService())
+ {
+ }
+
+ public MainViewModel(ICalculatorService calculatorService)
+ {
+ _calculatorService = calculatorService;
+ History = new ObservableCollection();
+ Variables = new ObservableCollection();
+ }
+
+ [ObservableProperty]
+ private string _inputText = string.Empty;
+
+ [ObservableProperty]
+ private bool _isFunctionsPanelOpen;
+
+ [ObservableProperty]
+ private int _selectedHistoryIndex = -1;
+
+ public ObservableCollection History { get; }
+ public ObservableCollection Variables { get; }
+
+ [RelayCommand]
+ private void ToggleFunctions()
+ {
+ IsFunctionsPanelOpen = !IsFunctionsPanelOpen;
+ }
+
+ [RelayCommand(AllowConcurrentExecutions = false)]
+ private async Task Submit()
+ {
+ var text = InputText?.Trim();
+ if (string.IsNullOrEmpty(text))
+ return;
+
+ var result = await _calculatorService.InterpretAsync(text);
+
+ History.Add(new HistoryItem { Input = text, Output = result.Output });
+
+ Variables.Clear();
+ foreach (var v in result.Variables)
+ {
+ Variables.Add(v);
+ }
+
+ InputText = string.Empty;
+ SelectedHistoryIndex = History.Count - 1;
+ }
}
diff --git a/src/AdvancedCalculator/Views/MainView.axaml b/src/AdvancedCalculator/Views/MainView.axaml
index eef679f..71ffac9 100644
--- a/src/AdvancedCalculator/Views/MainView.axaml
+++ b/src/AdvancedCalculator/Views/MainView.axaml
@@ -3,14 +3,99 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:AdvancedCalculator.ViewModels"
- mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+ xmlns:m="clr-namespace:AdvancedCalculator.Models"
+ xmlns:conv="clr-namespace:AdvancedCalculator.Converters"
+ mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="600"
x:Class="AdvancedCalculator.Views.MainView"
x:DataType="vm:MainViewModel">
-
-
-
-
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+