Skip to content

FEATURE: Calculator Plugin History#4454

Open
01Dri wants to merge 17 commits into
Flow-Launcher:devfrom
01Dri:feature/history-calc-plugin
Open

FEATURE: Calculator Plugin History#4454
01Dri wants to merge 17 commits into
Flow-Launcher:devfrom
01Dri:feature/history-calc-plugin

Conversation

@01Dri

@01Dri 01Dri commented May 10, 2026

Copy link
Copy Markdown
Contributor

Changes

  • Added History and HistoryItem objects to persist calculator results
  • Added a plugin setting to enable or disable history
  • Implemented a debounce strategy to reduce unnecessary history entries

Debounce Strategy

Currently, the calculator plugin returns results even when the Enter key is not pressed. Because of this behavior, every partial expression can be added to the history while the user is typing.
For example, while the user is building the expression:

1+2+3+4+5

Without a debounce mechanism, each intermediate expression is stored in history, generating multiple unnecessary entries.
With the debounce strategy, the plugin waits for a short delay before saving the result. This ensures that only the final or more complete expression is added to history.

Without debounce

  • 1
  • 1+2
  • 1+2+3
  • 1+2+3+4
  • 1+2+3+4+5

With debounce

  • 1+2+3+4+5

This keeps the history cleaner and improves the overall user experience.


Note

I'm not fully satisfied with the result title and subtitles yet, so suggestions are welcome!

Technical Implementation Details

Time-Ago Formatting

We implemented custom, localized relative time strings (e.g., "just now", "5 minutes ago"). This was needed because there is no built-in datetime formatting helper in the Flow Launcher codebase. The implementation uses the plugin's own local translation keys (in en.xaml) to keep everything localized correctly.

Debounce vs Immediate Modes

  • On Query Mode (Debounced): Uses a PendingHistoryItem together with an 800ms debounce timer to prevent saving incomplete expressions while the user is typing.
  • On Enter Mode (Immediate): When execution occurs (e.g., when the user presses Enter), the item is added or updated directly using a standard HistoryItem without any debounce.

Code Quality & Refactorings

  • Constructor Chaining: Chained the constructors in HistoryItem.cs so that the PendingHistoryItem constructor delegates to the primary one, avoiding initialization duplication.
  • Unified Action Delegation: Structured CreateClipboardActionWithHistory in Main.cs to delegate directly to CreateClipboardAction, avoiding duplicated copy and error handling logic.
  • Unified Update Logic: Created AddOrUpdateInternal(HistoryItem) in History.cs to standardize the update path for both modes.
  • Main Thread Execution: Removed unnecessary locks (lock (_syncRoot)) on the normal HistoryItem addition path since it is triggered from the main thread.

Screenshot 2026-07-04 201306
2026-07-04.19-49-34.mp4

Summary by cubic

Adds an optional calculation history to the calculator plugin, showing up to 5 recent calculations beneath the current result. Supports debounced save-on-query and save-on-enter modes with relative time and a history badge.

  • Summary of changes

    • Changed: Query now returns the main result plus recent history when EnableHistory is on, excluding the active expression. Main result uses a clear “copy to clipboard” subtitle. Clipboard is handled via shared actions; in OnEnter mode history saves only after a successful copy. History items render with a badge and a relative “time ago.”
    • Added: Settings EnableHistory (default off) and HistoryCreationMode (OnQuery with 800ms debounce, OnEnter). Storage-backed History with a 5-item cap (drops oldest). HistoryItem, PendingHistoryItem, and HistoryHelper for pending items and localized time-ago strings. History icon, English strings, and settings UI with mode selector and an OnQuery warning. Unit tests for storing when enabled, suppressing when disabled, and saved fields; tests flush debounce and reset history.
    • Removed: Inline copy-to-clipboard logic from Query in favor of centralized helpers.
    • Memory impact: Low. At most 5 stored items, one pending item, and one debounce timer in settings storage.
    • Security risks: Low. History is local and off by default. OnQuery may capture partial expressions; UI warns; nothing is sent externally.
    • Unit tests: Added coverage for history enable/disable behavior and stored data; includes debounce flush and per-test history reset.
  • Release Note

    • You can now enable a calculator history that remembers up to 5 recent calculations, saved as you type or only when you press Enter.

Written for commit a7efe5d. Summary will update on new commits.

Review in cubic

@github-actions github-actions Bot added this to the 2.2.0 milestone May 10, 2026
@coderabbitai coderabbitai Bot added the enhancement New feature or request label May 10, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 8 files

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread Flow.Launcher.Test/Plugins/CalculatorTest.cs
Comment thread Plugins/Flow.Launcher.Plugin.Calculator/Main.cs Outdated
Comment thread Plugins/Flow.Launcher.Plugin.Calculator/Storage/History.cs Outdated
@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds calculator history persistence and retrieval, with new storage models, query-flow wiring, history-related settings/UI, localized strings, and reflection-based tests.

Changes

Calculator History Feature

Layer / File(s) Summary
Storage and contracts
Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs, Plugins/Flow.Launcher.Plugin.Calculator/HistoryCreationMode.cs, Plugins/Flow.Launcher.Plugin.Calculator/Storage/*
Adds EnableHistory, the history creation mode enum, and the History, HistoryItem, and PendingHistoryItem storage types with debounced update and capped list behavior.
History formatting helpers
Plugins/Flow.Launcher.Plugin.Calculator/HistoryHelper.cs
Formats relative time strings and builds pending history entries with localized subtitle text and copy instructions.
Main query flow
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs
Loads persisted history, gates history recording on valid results, creates pending history entries, and merges current results with stored history entries.
Settings UI and localization
Plugins/Flow.Launcher.Plugin.Calculator/ViewModels/SettingsViewModel.cs, Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml, Plugins/Flow.Launcher.Plugin.Calculator/Languages/*.xaml
Adds history settings state, UI controls, warnings, and supporting English and Portuguese resource strings.
Tests and validation
Flow.Launcher.Test/Plugins/CalculatorTest.cs
Adds history-focused tests and reflection helpers to set, read, and flush the plugin history during test runs.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

Possibly related PRs

  • Flow-Launcher/Flow.Launcher#3859: Shares changes to Plugins/Flow.Launcher.Plugin.Calculator/Main.cs and the query/result flow, though for different calculator behavior.
  • Flow-Launcher/Flow.Launcher#3971: Also changes the calculator query/result-building path, including handling of invalid results and copy/action construction.

Suggested reviewers: Jack251970, VictoriousRaptor, jjw24

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: adding history support to the calculator plugin.
Description check ✅ Passed The description matches the implemented history storage, debounce behavior, settings, and UI updates.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (1)

156-159: 💤 Low value

History stores normalized expression, not original user input.

The expression variable passed to History.AddOrUpdate (line 158) is the normalized form after number formatting and function rewrites (pow/log/ln workarounds). This means users who type "1,234+5" will see "1234+5" in their history, and users who type "pow(2,3)" will see "(2^3)".

This is likely acceptable for most users, but consider whether showing the original query.Search instead would provide a better experience. The normalized form is what was actually computed, so there's a tradeoff.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Plugins/Flow.Launcher.Plugin.Calculator/Main.cs` around lines 156 - 159, The
history entry is being stored using the normalized expression variable (used
after number formatting and function rewrites) which loses the original user
input; change the argument passed to History.AddOrUpdate so it uses the original
query string (query.Search) instead of expression when creating the new
History.PendingHistoryItem, or choose to store both by passing query.Search as
the display/original value and expression as the computed/normalized value to
PendingHistoryItem if that type supports it (update PendingHistoryItem
constructor if needed) so history shows what the user typed while still
preserving the actual computed expression.
Plugins/Flow.Launcher.Plugin.Calculator/Storage/HistoryItem.cs (1)

11-12: ⚡ Quick win

Remove [JsonIgnore] from const field.

Constants are not serialized by System.Text.Json (or most serializers) by default. Adding [JsonIgnore] to a const field is misleading because it suggests the field might be serialized otherwise, which is never the case.

♻️ Proposed fix
-    [JsonIgnore]
     private const string BadgeIconPath = "Images/history.png";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Plugins/Flow.Launcher.Plugin.Calculator/Storage/HistoryItem.cs` around lines
11 - 12, Remove the misleading [JsonIgnore] attribute applied to the const field
BadgeIconPath in the HistoryItem class: locate the private const string
BadgeIconPath = "Images/history.png" declaration and delete the [JsonIgnore]
attribute so the const remains attribute-free; no other code changes required.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Flow.Launcher.Test/Plugins/CalculatorTest.cs`:
- Around line 102-110: The test CalculationHistory_IsNotStoredWhenDisabled
checks history immediately after calling _plugin.Query without waiting for the
debounce flush used by the plugin (same as
CalculationHistory_IsStoredWhenEnabled), so update the test to wait for the
debounce to complete or call the same helper that forces a flush used by the
other test before asserting; specifically, after _plugin.Query(new Plugin.Query
{ Search = "1+1" }) add the same wait/flush step used in
CalculationHistory_IsStoredWhenEnabled (e.g., await Task.Delay(debounceMs +
margin) or call the test helper that flushes history) then assert
GetHistory().Items.Count is 0, keeping references to _settings.EnableHistory,
_plugin.Query, GetHistory(), and the other test name for locating the helper.
- Around line 91-100: The test fails to account for History's 800ms debounce so
_plugin.Query(...) is called but GetHistory().Items.Count is checked before the
debounce flush; fix by either waiting for the debounce to elapse (e.g.,
Thread.Sleep(900) or an async wait after calling _plugin.Query) or add a
test-only synchronous flush on History (add an internal FlushNow() in History
that stops the _debounceTimer and calls FlushPendingItem()) and call that from
the test before asserting; reference History.FlushNow, History.FlushPendingItem,
_plugin.Query, and GetHistory() to locate the code to change.
- Around line 163-174: GetHistory() is using reflection for a static field but
Main declares a private instance property History; update the reflection to
retrieve the non-public instance property and read it from the plugin instance
used in SetUp() (e.g. use typeof(Main).GetProperty("History",
BindingFlags.NonPublic | BindingFlags.Instance) and call GetValue(_plugin)),
then assert the property exists and returns a History instance; alternatively,
if static behavior was intended change Main.History to a static field, but the
safer fix in the test is to use GetProperty with BindingFlags.Instance and the
_plugin instance.

In `@Plugins/Flow.Launcher.Plugin.Calculator/Main.cs`:
- Line 31: The History property is declared as an instance property but tests
access it via reflection with BindingFlags.Static; change the declaration of
History in Main.cs to a static property (e.g., private static History History {
get; set; } = null!;) so the reflection in CalculatorTest (which expects a
static member) will find it; ensure any usages of History in methods are updated
to reference the static property if needed.

In `@Plugins/Flow.Launcher.Plugin.Calculator/Storage/History.cs`:
- Around line 72-75: The eviction currently removes Items.RemoveAt(0) which uses
list order; change it to remove the item with the oldest CalculatedAt timestamp
so refreshed entries (via Refresh) aren’t evicted; in History.cs locate the
block checking Items.Count >= MaxItems and replace the RemoveAt(0) call with
logic that finds the item with the minimum CalculatedAt (e.g., via
Items.OrderBy(i => i.CalculatedAt).First() or a manual scan) and removes that
item from Items, ensuring you handle any null/zero timestamps consistently.

---

Nitpick comments:
In `@Plugins/Flow.Launcher.Plugin.Calculator/Main.cs`:
- Around line 156-159: The history entry is being stored using the normalized
expression variable (used after number formatting and function rewrites) which
loses the original user input; change the argument passed to History.AddOrUpdate
so it uses the original query string (query.Search) instead of expression when
creating the new History.PendingHistoryItem, or choose to store both by passing
query.Search as the display/original value and expression as the
computed/normalized value to PendingHistoryItem if that type supports it (update
PendingHistoryItem constructor if needed) so history shows what the user typed
while still preserving the actual computed expression.

In `@Plugins/Flow.Launcher.Plugin.Calculator/Storage/HistoryItem.cs`:
- Around line 11-12: Remove the misleading [JsonIgnore] attribute applied to the
const field BadgeIconPath in the HistoryItem class: locate the private const
string BadgeIconPath = "Images/history.png" declaration and delete the
[JsonIgnore] attribute so the const remains attribute-free; no other code
changes required.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f00f3981-70fd-4e0a-bb48-4b468541be79

📥 Commits

Reviewing files that changed from the base of the PR and between 5779818 and 4fc3016.

⛔ Files ignored due to path filters (1)
  • Plugins/Flow.Launcher.Plugin.Calculator/Images/history.png is excluded by !**/*.png
📒 Files selected for processing (7)
  • Flow.Launcher.Test/Plugins/CalculatorTest.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml
  • Plugins/Flow.Launcher.Plugin.Calculator/Main.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/Storage/History.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/Storage/HistoryItem.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml

Comment thread Flow.Launcher.Test/Plugins/CalculatorTest.cs
Comment thread Flow.Launcher.Test/Plugins/CalculatorTest.cs
Comment thread Flow.Launcher.Test/Plugins/CalculatorTest.cs Outdated
Comment thread Plugins/Flow.Launcher.Plugin.Calculator/Main.cs
Comment thread Plugins/Flow.Launcher.Plugin.Calculator/Storage/History.cs Outdated
@01Dri 01Dri closed this May 10, 2026
@01Dri 01Dri reopened this May 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
Plugins/Flow.Launcher.Plugin.Calculator/Storage/History.cs (1)

71-74: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Fix eviction to remove oldest by timestamp, not insertion order.

When capacity is reached, RemoveAt(0) evicts based on list position. However, when an existing item is refreshed (line 65), its CalculatedAt is updated but it remains at the same index. This means a recently refreshed item at index 0 will be evicted even though newer (by timestamp) items exist at higher indices.

🐛 Proposed fix to remove oldest by timestamp
                 if (Items.Count >= MaxItems)
                 {
-                    Items.RemoveAt(0);
+                    var oldestItem = Items.OrderBy(x => x.CalculatedAt).First();
+                    Items.Remove(oldestItem);
                 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Plugins/Flow.Launcher.Plugin.Calculator/Storage/History.cs` around lines 71 -
74, The eviction currently uses Items.RemoveAt(0) which removes by list
position; change it to remove the item with the oldest CalculatedAt timestamp so
refreshes aren't evicted; in the eviction block in History (the method
updating/adding items that uses Items, MaxItems and updates CalculatedAt)
compute the item with the minimum CalculatedAt (e.g., via Items.MinBy(i =>
i.CalculatedAt) or Items.OrderBy(i => i.CalculatedAt).First()) and call
Items.Remove(oldest) instead of RemoveAt(0).
🧹 Nitpick comments (2)
Plugins/Flow.Launcher.Plugin.Calculator/Storage/HistoryItem.cs (2)

22-22: 💤 Low value

Consider extracting magic number to a named constant.

The hardcoded score 300 would be more maintainable as a named constant (e.g., private const int HistoryItemScore = 300) to clarify its purpose and make it easier to adjust if needed.

♻️ Proposed fix
+    private const int HistoryItemScore = 300;
+
     public HistoryItem(PendingHistoryItem item)
     {
         CalculatedAt = item.CalculatedAt;
         Title = item.Expression;
         SubTitle = item.SubTitle;
-        Score = 300;
+        Score = HistoryItemScore;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Plugins/Flow.Launcher.Plugin.Calculator/Storage/HistoryItem.cs` at line 22,
The hardcoded assignment Score = 300 in HistoryItem should be replaced with a
named constant to remove the magic number; add a private const int (e.g.,
HistoryItemScore = 300) in the HistoryItem class and use that constant when
setting the Score property (refer to the Score assignment in HistoryItem and the
class declaration to locate the change).

11-11: 💤 Low value

Remove redundant [JsonIgnore] attribute.

The [JsonIgnore] attribute on a private const field is redundant. Private constants are not serialized by System.Text.Json by default, and const fields are implicitly static (which are also excluded from serialization).

♻️ Proposed fix
-    [JsonIgnore] private const string BadgeIconPath = "Images/history.png";
+    private const string BadgeIconPath = "Images/history.png";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Plugins/Flow.Launcher.Plugin.Calculator/Storage/HistoryItem.cs` at line 11,
Remove the redundant [JsonIgnore] attribute applied to the private const field
BadgeIconPath in HistoryItem (the line "[JsonIgnore] private const string
BadgeIconPath = \"Images/history.png\""); simply delete the [JsonIgnore] token
so the field is declared as "private const string BadgeIconPath =
\"Images/history.png\"". If that was the only usage of
System.Text.Json.Serialization in the file, also remove the now-unused using for
cleanliness.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@Plugins/Flow.Launcher.Plugin.Calculator/Storage/History.cs`:
- Around line 71-74: The eviction currently uses Items.RemoveAt(0) which removes
by list position; change it to remove the item with the oldest CalculatedAt
timestamp so refreshes aren't evicted; in the eviction block in History (the
method updating/adding items that uses Items, MaxItems and updates CalculatedAt)
compute the item with the minimum CalculatedAt (e.g., via Items.MinBy(i =>
i.CalculatedAt) or Items.OrderBy(i => i.CalculatedAt).First()) and call
Items.Remove(oldest) instead of RemoveAt(0).

---

Nitpick comments:
In `@Plugins/Flow.Launcher.Plugin.Calculator/Storage/HistoryItem.cs`:
- Line 22: The hardcoded assignment Score = 300 in HistoryItem should be
replaced with a named constant to remove the magic number; add a private const
int (e.g., HistoryItemScore = 300) in the HistoryItem class and use that
constant when setting the Score property (refer to the Score assignment in
HistoryItem and the class declaration to locate the change).
- Line 11: Remove the redundant [JsonIgnore] attribute applied to the private
const field BadgeIconPath in HistoryItem (the line "[JsonIgnore] private const
string BadgeIconPath = \"Images/history.png\""); simply delete the [JsonIgnore]
token so the field is declared as "private const string BadgeIconPath =
\"Images/history.png\"". If that was the only usage of
System.Text.Json.Serialization in the file, also remove the now-unused using for
cleanliness.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 16e2d2a3-7a8a-46ab-a47a-40f9fe95bcb0

📥 Commits

Reviewing files that changed from the base of the PR and between 4fc3016 and 6d93ce0.

📒 Files selected for processing (5)
  • Flow.Launcher.Test/Plugins/CalculatorTest.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/Main.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/Storage/History.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/Storage/HistoryItem.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/Storage/PendingHistoryItem.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • Plugins/Flow.Launcher.Plugin.Calculator/Main.cs

@DavidGBrett DavidGBrett left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently my result icons aren't getting that clock (history.png) layered on top of them like yours are in the screenshots provded - not sure why
My mistake I didn't realise I needed the results badges to be enabled in the settings

One design concern is that if the user is searching for a numerical query, e.g. a date,
then there would be a lot of history results in the way of what they actually want.
Its a difficult problem to solve since as a query a date and a calculation can look identical

It's a little confusing that the title is not whats being copied to the clipboard
Perhaps it could be query = result?

Another approach that you could consider is switching the current query to that historical query, making it work more like a rewind button (that's what I was used to on my old physical calculator).
They can still use the existing result to copy it after switching - a context menu copy is also an option but perhaps unnecessary.

Instead of a timestamp, I think a time delta would be more helpful.
No need for the Calculated at part either I think, too verbose.
e.g. 2 minutes ago

image

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can confirm this is from icons8 which we already give attribution to so is safe to use
https://icons8.com/icon/QDgOnr6UAOmg/time-machine

My concern is that at a small scale its not too readable

I found another alternative, which might be slightly more clear - though still not ideal
https://icons8.com/icons/set/history--os-windows--multi--technique-filled

Image

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 11 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread Plugins/Flow.Launcher.Plugin.Calculator/Storage/History.cs Outdated
Comment thread Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-br.xaml
Comment thread Plugins/Flow.Launcher.Plugin.Calculator/Storage/HistoryItem.cs
Comment thread Plugins/Flow.Launcher.Plugin.Calculator/Main.cs Outdated
01Dri and others added 2 commits July 4, 2026 19:54
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
Plugins/Flow.Launcher.Plugin.Calculator/Storage/History.cs (1)

113-122: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Eviction removes the newest item instead of the oldest.

OrderByDescending(x => x.CalculatedAt) sorts newest-first, so Items[0] after this sort is the most recently calculated item. RemoveAt(0) therefore evicts the newest entry and keeps the oldest ones — the opposite of the intended "cap at MaxItems by dropping the oldest" behavior discussed in the prior review thread.

🐛 Proposed fix
     private void Add(HistoryItem item)
     {
         if (Items.Count >= MaxItems)
         {
-
-            Items = Items.OrderByDescending(x => x.CalculatedAt).ToList();
-            Items.RemoveAt(0);
+            var oldestItem = Items.OrderBy(x => x.CalculatedAt).First();
+            Items.Remove(oldestItem);
         }
         Items.Add(item);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Plugins/Flow.Launcher.Plugin.Calculator/Storage/History.cs` around lines 113
- 122, The Add method in History is evicting the wrong entry because it sorts
newest-first with OrderByDescending(x => x.CalculatedAt) and then removes index
0, which drops the newest item instead of the oldest. Update the eviction logic
in Add so it identifies and removes the oldest HistoryItem by CalculatedAt
before appending the new item, while keeping the MaxItems cap behavior intact.
🧹 Nitpick comments (1)
Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml (1)

100-107: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Disabled TextBlock won't visually dim like the adjacent ComboBox.

TextBlock is not a Control, so it has no built-in template trigger that reacts to IsEnabled (unlike the ComboBox at Line 108-120, which will visibly dim via the default theme when disabled). Setting IsEnabled="{Binding EnableHistory}" here will disable interaction/hit-testing but the label text likely won't appear grayed out, leaving an inconsistent visual cue when history is disabled.

💡 Suggested fix: bind Opacity in addition to IsEnabled
         <TextBlock
             Grid.Row="5"
             Grid.Column="0"
             Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
             VerticalAlignment="Center"
             FontSize="14"
             Text="{DynamicResource flowlauncher_plugin_calculator_history_creation_mode}"
-            IsEnabled="{Binding EnableHistory}" />
+            IsEnabled="{Binding EnableHistory}"
+            Opacity="{Binding EnableHistory, Converter={StaticResource BoolToOpacityConverter}}" />

(Requires a BoolToOpacityConverter or equivalent, or apply a Style.Trigger on IsEnabled for TextBlock.)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml` around
lines 100 - 107, The history label TextBlock in CalculatorSettings.xaml is being
disabled via IsEnabled, but because TextBlock is not a Control it will not
visually dim like the adjacent ComboBox. Update the label binding so the text
also reflects the disabled state, either by adding an Opacity binding with a
BoolToOpacityConverter or by applying a Style trigger on TextBlock tied to
EnableHistory, and keep the existing IsEnabled binding for behavior consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@Plugins/Flow.Launcher.Plugin.Calculator/Storage/History.cs`:
- Around line 113-122: The Add method in History is evicting the wrong entry
because it sorts newest-first with OrderByDescending(x => x.CalculatedAt) and
then removes index 0, which drops the newest item instead of the oldest. Update
the eviction logic in Add so it identifies and removes the oldest HistoryItem by
CalculatedAt before appending the new item, while keeping the MaxItems cap
behavior intact.

---

Nitpick comments:
In `@Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml`:
- Around line 100-107: The history label TextBlock in CalculatorSettings.xaml is
being disabled via IsEnabled, but because TextBlock is not a Control it will not
visually dim like the adjacent ComboBox. Update the label binding so the text
also reflects the disabled state, either by adding an Opacity binding with a
BoolToOpacityConverter or by applying a Style trigger on TextBlock tied to
EnableHistory, and keep the existing IsEnabled binding for behavior consistency.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f0b96d0c-bf32-42eb-9084-07fc70ac4ad8

📥 Commits

Reviewing files that changed from the base of the PR and between 6d93ce0 and 0d1b2bb.

📒 Files selected for processing (11)
  • Plugins/Flow.Launcher.Plugin.Calculator/HistoryCreationMode.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/HistoryHelper.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml
  • Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-br.xaml
  • Plugins/Flow.Launcher.Plugin.Calculator/Main.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/Storage/History.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/Storage/HistoryItem.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/Storage/PendingHistoryItem.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/ViewModels/SettingsViewModel.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml
✅ Files skipped from review due to trivial changes (1)
  • Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-br.xaml
🚧 Files skipped from review as they are similar to previous changes (3)
  • Plugins/Flow.Launcher.Plugin.Calculator/Storage/PendingHistoryItem.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/Main.cs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (3)

123-137: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

NaN/Function paths still fall through to Convert.ToDecimal
At Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:123-137, both branches replace result with a localized string, but execution still continues into Convert.ToDecimal(result). That throws on the non-numeric text, so the message never surfaces. isValidResultToAddHistory is also never read, so the intended history guard is ineffective. Return those cases early (or skip the numeric formatting block) and remove the dead flag.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Plugins/Flow.Launcher.Plugin.Calculator/Main.cs` around lines 123 - 137, The
NaN and Function handling in Main.cs still continues into the numeric formatting
path, causing Convert.ToDecimal to run on localized strings. Update the
result-processing logic in the Main method to return early or otherwise skip the
rounding/formatting block when result is NaN or a Function, using the existing
Localize.flowlauncher_plugin_calculator_not_a_number and
Localize.flowlauncher_plugin_calculator_expression_not_complete branches. Remove
the unused isValidResultToAddHistory flag if it is no longer needed, since it
currently has no effect.

155-162: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep the original query in history entries
HistoryItem is built from the normalized expression, so history shows (log10(100)) instead of the user's log(100). Store query.Search for Query/Title and keep the rewritten string only for evaluation/dedupe.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Plugins/Flow.Launcher.Plugin.Calculator/Main.cs` around lines 155 - 162, The
history entry is using the normalized expression instead of the user’s original
query, so update the history creation path in Main.cs to preserve query.Search
for HistoryItem Query and Title while still using the rewritten expression only
for evaluation and deduplication. In the flow around
HistoryHelper.CreatePendingHistoryItem and History.AddOrUpdate, make sure the
stored history text reflects the original user input, and keep the normalized
expression only for computation-related logic.

41-41: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Restore the history action on reload. Persisted HistoryItems lose their Action, so restored calculator history entries become non-clickable after restart. Rebuild the delegate when materializing history items instead of copying it through storage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Plugins/Flow.Launcher.Plugin.Calculator/Main.cs` at line 41, The calculator
history reload path is restoring HistoryItem data without rebuilding its Action,
so loaded entries become non-clickable after restart. Update the history
materialization logic in Main and the History/HistoryItem handling so the
delegate is recreated when loading persisted items instead of being copied from
storage. Use the History and HistoryItem symbols to locate the restore flow and
ensure each restored item gets a fresh clickable action.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@Plugins/Flow.Launcher.Plugin.Calculator/Main.cs`:
- Around line 123-137: The NaN and Function handling in Main.cs still continues
into the numeric formatting path, causing Convert.ToDecimal to run on localized
strings. Update the result-processing logic in the Main method to return early
or otherwise skip the rounding/formatting block when result is NaN or a
Function, using the existing
Localize.flowlauncher_plugin_calculator_not_a_number and
Localize.flowlauncher_plugin_calculator_expression_not_complete branches. Remove
the unused isValidResultToAddHistory flag if it is no longer needed, since it
currently has no effect.
- Around line 155-162: The history entry is using the normalized expression
instead of the user’s original query, so update the history creation path in
Main.cs to preserve query.Search for HistoryItem Query and Title while still
using the rewritten expression only for evaluation and deduplication. In the
flow around HistoryHelper.CreatePendingHistoryItem and History.AddOrUpdate, make
sure the stored history text reflects the original user input, and keep the
normalized expression only for computation-related logic.
- Line 41: The calculator history reload path is restoring HistoryItem data
without rebuilding its Action, so loaded entries become non-clickable after
restart. Update the history materialization logic in Main and the
History/HistoryItem handling so the delegate is recreated when loading persisted
items instead of being copied from storage. Use the History and HistoryItem
symbols to locate the restore flow and ensure each restored item gets a fresh
clickable action.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 63fad677-58d4-4ccb-bbde-8c4fc9e72923

📥 Commits

Reviewing files that changed from the base of the PR and between 2f7f2bb and a745873.

📒 Files selected for processing (2)
  • Plugins/Flow.Launcher.Plugin.Calculator/Main.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/Storage/HistoryItem.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • Plugins/Flow.Launcher.Plugin.Calculator/Storage/HistoryItem.cs

@01Dri

01Dri commented Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

@DavidGBrett
@Jack251970

I made some adjustments. Could you test it again and let me know if everything looks good? Thanks!

@01Dri 01Dri requested a review from DavidGBrett July 4, 2026 23:18

@Jack251970 Jack251970 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert changes in pt-br.xaml.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, could you please revert changes in this file?

@Jack251970

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a74587366d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Plugins/Flow.Launcher.Plugin.Calculator/Main.cs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants