Skip to content
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
283cbe4
first implementation of history
01Dri May 2, 2026
f4ac497
HasCalculationOperator method to just add calc in history if have a o…
01Dri May 2, 2026
05eae58
setting to enable or disable history
01Dri May 2, 2026
e4f5dee
badge in history item and fix some bugs
01Dri May 2, 2026
dc2dfb7
calculator tests with history
01Dri May 2, 2026
a7c1f33
validation to add or not result in history
01Dri May 6, 2026
df72b99
debounce logic to add result in history
01Dri May 10, 2026
4fc3016
code quality
01Dri May 10, 2026
6d93ce0
fix unit tests and code quality
01Dri May 10, 2026
ed6cccf
order to remove the last item added
01Dri Jul 4, 2026
83efd29
new modes to save history
01Dri Jul 4, 2026
e7d0dd3
query and enter mode
01Dri Jul 4, 2026
0d1b2bb
Merge branch 'dev' into feature/history-calc-plugin
01Dri Jul 4, 2026
2f7f2bb
Update Plugins/Flow.Launcher.Plugin.Calculator/Storage/History.cs
01Dri Jul 4, 2026
264d9ad
code quality after ai review
01Dri Jul 4, 2026
a745873
Merge branch 'feature/history-calc-plugin' of https://github.com/01Dr…
01Dri Jul 4, 2026
a7efe5d
revert changes pt-br
01Dri Jul 5, 2026
285859d
Change history badge icon to clearer one
DavidGBrett Jul 14, 2026
197bb14
Merge branch 'dev' into feature/history-calc-plugin
01Dri Oct 14, 2026
5f56d2a
refactor: review adjusts
01Dri Oct 14, 2026
00c3354
Capitalize Just now string in calculator plugin
DavidGBrett Oct 17, 2026
9ca1fa8
Give calculation result max score so its above the history results in…
DavidGBrett Oct 17, 2026
87cd651
refactor: fix pr comments
01Dri Oct 18, 2026
a09ec49
refactor: back result score
01Dri Oct 18, 2026
2df2c6c
Reduce the score of the calculation result slightly so that its still…
DavidGBrett Oct 18, 2026
2b1e56f
Keep the calculation result above history without overriding topmost …
DavidGBrett Oct 18, 2026
56818d4
Save calculator history when result is executed
DavidGBrett Oct 18, 2026
791f2f8
Fix history flushing for result execution
DavidGBrett Oct 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 91 additions & 2 deletions Flow.Launcher.Test/Plugins/CalculatorTest.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Flow.Launcher.Plugin.Calculator;
using Flow.Launcher.Plugin.Calculator.Storage;
using Mages.Core;
using NUnit.Framework;
using NUnit.Framework.Legacy;
Expand All @@ -17,7 +19,8 @@ public class CalculatorPluginTest
DecimalSeparator = DecimalSeparator.UseSystemLocale,
MaxDecimalPlaces = 10,
ShowErrorMessage = false, // Make sure we return the empty results when error occurs
UseThousandsSeparator = true // Default value
UseThousandsSeparator = true, // Default value
EnableHistory = true,
};
private readonly Engine _engine = new(new Configuration
{
Expand All @@ -40,6 +43,14 @@ public CalculatorPluginTest()
if (engineField == null)
Assert.Fail("Could not find static field 'MagesEngine' on Flow.Launcher.Plugin.Calculator.Main");
engineField.SetValue(null, _engine);

SetHistory(new History());
}

[SetUp]
public void SetUp()
{
SetHistory(new History());
}

[Test]
Expand Down Expand Up @@ -80,6 +91,53 @@ public void ThousandsSeparatorTest_LargeNumber()
ClassicAssert.AreEqual("1234567", result);
}

[Test]
public void CalculationHistory_IsStoredWhenEnabled()
{
_settings.EnableHistory = true;

_plugin.Query(new Plugin.Query { Search = "1+1" });
WaitForHistoryDebounce();

ClassicAssert.AreEqual(1, GetHistory().Items.Count);
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
var item = GetHistory().Items[0];
ClassicAssert.AreEqual("1+1", item.Query);
ClassicAssert.AreEqual("2", item.CalcResult);
ClassicAssert.AreNotEqual(default(DateTime), item.CalculatedAt);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

[Test]
public void CalculationHistory_IsNotReturnedWhenDisabled()
{
_settings.EnableHistory = true;
_plugin.Query(new Plugin.Query { Search = "1+1" });
WaitForHistoryDebounce();

_settings.EnableHistory = false;
var results = _plugin.Query(new Plugin.Query { Search = "2+2" });

ClassicAssert.AreEqual(1, results.Count);
ClassicAssert.AreEqual("4", results[0].CopyText);
}

[Test]
public void CalculationHistory_ShowsQueryAndResultWithChangeQueryAction()
{
_settings.EnableHistory = true;
_settings.HistoryCreationMode = HistoryCreationMode.OnQuery;

_plugin.Query(new Plugin.Query { Search = "1+1" });
WaitForHistoryDebounce();

var results = _plugin.Query(new Plugin.Query { Search = "5+5" });

var historyResult = results.FirstOrDefault(x => x.ShowBadge);
ClassicAssert.IsNotNull(historyResult);
ClassicAssert.AreEqual("1+1 = 2", historyResult!.Title);
ClassicAssert.AreEqual("2", historyResult.CopyText);
ClassicAssert.IsNotNull(historyResult.Action);
}
Comment thread
Jack251970 marked this conversation as resolved.

// Basic operations
[TestCase(@"1+1", "2")]
[TestCase(@"2-1", "1")]
Expand Down Expand Up @@ -128,7 +186,38 @@ private string GetCalculationResult(string expression)
{
Search = expression
});
return results.Count > 0 ? results[0].Title : string.Empty;
return results.Count > 0 ? results[0].CopyText : string.Empty;
}

private void WaitForHistoryDebounce()
{
var flushPendingItemMethod = typeof(History).GetMethod("FlushPendingItem", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.That(flushPendingItemMethod, Is.Not.Null,
"Could not find method 'FlushPendingItem' on Flow.Launcher.Plugin.Calculator.Storage.History");

flushPendingItemMethod!.Invoke(GetHistory(), null);
}

private History GetHistory()
{
var historyProperty = typeof(Main).GetProperty("History", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.That(historyProperty, Is.Not.Null,
"Could not find property 'History' on Flow.Launcher.Plugin.Calculator.Main");

var history = historyProperty!.GetValue(_plugin) as History;
Assert.That(history, Is.Not.Null,
"Property 'History' on Flow.Launcher.Plugin.Calculator.Main' was not a calculator History instance");

return history!;
}

private void SetHistory(History history)
{
var historyProperty = typeof(Main).GetProperty("History", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.That(historyProperty, Is.Not.Null,
"Could not find property 'History' on Flow.Launcher.Plugin.Calculator.Main");

historyProperty!.SetValue(_plugin, history);
}
}
}
23 changes: 23 additions & 0 deletions Plugins/Flow.Launcher.Plugin.Calculator/HistoryCreationMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Flow.Launcher.Localization.Attributes;

namespace Flow.Launcher.Plugin.Calculator
{
/// <summary>
/// Represents the different modes for saving calculator calculations into the history.
/// </summary>
[EnumLocalize]
public enum HistoryCreationMode
{
/// <summary>
/// Saves calculations into the history automatically as the query is typed.
/// </summary>
[EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_calculator_history_creation_mode_on_query))]
OnQuery,

/// <summary>
/// Saves calculations into the history only when the action is executed (e.g. Enter is pressed).
/// </summary>
[EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_calculator_history_creation_mode_on_enter))]
OnEnter
}
}
75 changes: 75 additions & 0 deletions Plugins/Flow.Launcher.Plugin.Calculator/HistoryHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System;
using Flow.Launcher.Plugin;

namespace Flow.Launcher.Plugin.Calculator
{
/// <summary>
/// Helper class providing utility methods for formatting relative time strings.
/// </summary>
internal static class HistoryHelper
{
/// <summary>
/// Formats a DateTime value into a localized relative time string (e.g. "just now", "5 minutes ago").
/// </summary>
/// <param name="context">The plugin init context used to retrieve localized strings.</param>
/// <param name="calculatedAt">The time when the calculation was recorded.</param>
/// <returns>A localized relative time delta string.</returns>
public static string GetTimeDeltaString(PluginInitContext context, DateTime calculatedAt)
{
var now = DateTime.Now;
var timeSpan = now - calculatedAt;

if (timeSpan.TotalSeconds < 0)
{
timeSpan = TimeSpan.Zero;
}

if (timeSpan.TotalSeconds < 60)
{
return context == null ? "just now" : Localize.flowlauncher_plugin_calculator_time_just_now();
}
if (timeSpan.TotalMinutes < 60)
{
var minutes = (int)timeSpan.TotalMinutes;
if (minutes == 1)
{
return context == null ? "1 minute ago" : Localize.flowlauncher_plugin_calculator_time_minute_ago();
}
return context == null ? $"{minutes} minutes ago" : Localize.flowlauncher_plugin_calculator_time_minutes_ago(minutes);
}
if (timeSpan.TotalHours < 24)
{
var hours = (int)timeSpan.TotalHours;
if (hours == 1)
{
return context == null ? "1 hour ago" : Localize.flowlauncher_plugin_calculator_time_hour_ago();
}
return context == null ? $"{hours} hours ago" : Localize.flowlauncher_plugin_calculator_time_hours_ago(hours);
}
if (timeSpan.TotalDays < 30)
{
var days = (int)timeSpan.TotalDays;
if (days == 1)
{
return context == null ? "1 day ago" : Localize.flowlauncher_plugin_calculator_time_day_ago();
}
return context == null ? $"{days} days ago" : Localize.flowlauncher_plugin_calculator_time_days_ago(days);
}
if (timeSpan.TotalDays < 365)
{
var months = (int)(timeSpan.TotalDays / 30);
if (months == 1)
{
return context == null ? "1 month ago" : Localize.flowlauncher_plugin_calculator_time_month_ago();
}
return context == null ? $"{months} months ago" : Localize.flowlauncher_plugin_calculator_time_months_ago(months);
}
var years = (int)(timeSpan.TotalDays / 365);
if (years == 1)
{
return context == null ? "1 year ago" : Localize.flowlauncher_plugin_calculator_time_year_ago();
}
return context == null ? $"{years} years ago" : Localize.flowlauncher_plugin_calculator_time_years_ago(years);
}
}
}
Comment thread
DavidGBrett marked this conversation as resolved.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 19 additions & 1 deletion Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,22 @@
<system:String x:Key="flowlauncher_plugin_calculator_use_thousands_separator">Show thousands separator in results</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_failed_to_copy">Copy failed, please try later</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_show_error_message">Show error message when calculation fails</system:String>
</ResourceDictionary>
<system:String x:Key="flowlauncher_plugin_calculator_enable_history">Enable History</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_history_subtitle">{0}</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_just_now">just now</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_minute_ago">1 minute ago</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_minutes_ago">{0} minutes ago</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_hour_ago">1 hour ago</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_hours_ago">{0} hours ago</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_day_ago">1 day ago</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_days_ago">{0} days ago</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_month_ago">1 month ago</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_months_ago">{0} months ago</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_year_ago">1 year ago</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_years_ago">{0} years ago</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_history_creation_mode">History save mode</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_history_creation_mode_on_query">Save on query</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_history_creation_mode_on_enter">Save on Enter</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_history_creation_mode_warning">Warning: Due to the plugin's architecture, saving on query may register incomplete calculations in the history while you type.</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_copy_result_to_clipboard">Copy the result of this expression to the clipboard</system:String>
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
</ResourceDictionary>
Loading
Loading