GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — feat: clipboard history, taskbar widget & configurable modules by devohmycode · Pull Request #8 · devohmycode/NoteUI

Conversation

@devohmycode

@devohmycode devohmycode commented Mar 29, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Owner

Summary

  • Clipboard history & widget: New ClipboardWidget and ClipboardHistoryManager to manage clipboard history with categories and favorites
  • Configurable Taskbar widget: TaskbarWidget with modules that can be enabled/disabled via AppSettings, automatic hiding in fullscreen mode
  • UX Improvements: Fixed UX flyouts, expanded ActionPanel, i18n support (Lang.cs), default sorting in the installer

Notable Changes

  • 15 modified files, ~2,500 lines added
  • New files: ClipboardWidget.xaml/.cs, TaskbarWidget.xaml/.cs, ClipboardHistoryManager.cs, AppSettings.cs, Lang.cs

Test plan

  • Check taskbar widget (show, hide full screen, modules)
  • Test clipboard history (copy, favorites, categories)
  • Validate settings in AppSettings
  • Test updated installer

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
devohmycode added 2 commits March 28, 2026 16:19
@devohmycode
feat: clipboard history, taskbar widget, snippet categories and favor…
a4a11b9
…ites

- Clipboard history: capture text, RTF, images and files from Explorer, persist to JSON, max 50 entries with auto-cleanup, deduplication, pinning
- Clipboard view in main window with search, drag-and-drop support, clear all button, context menu (pin, save as note, snippet, delete)
- Taskbar widget docked next to system tray with clipboard and favorites flyout menus, paste-to-active-window on click, persistent enabled state
- Floating clipboard widget with opacity slider, compact mode, resize grip, drag to move, all settings persisted
- Snippet categories: required category field in creation flyout, editable ComboBox with existing categories
- Favorites in note context menu (add/remove)
- Settings: icons on all options, widget toggle button, quit moved from quick access to settings
- Start minimized only applies on Windows startup (not manual launch)
- Bump version to 0.4.0
@devohmycode
feat: configurable widget modules, fullscreen hide, flyout UX fixes
b656e66
- Widget modules configurable via Settings > Widget sub-panel with toggle switches
- 5 modules: Clipboard, Notes, Favorites, Folders, Snippets
- Dynamic button generation based on enabled modules
- Widget hides when fullscreen app is active (monitor detection)
- Settings flyout stays open when toggling switches or enable/disable
- Widget created without stealing focus (AppWindow.Show activateWindow:false)
- Module changes update widget in-place via BuildButtons() without recreating
- Add favorites toggle to note context menu
@coderabbitai

coderabbitai Bot commented Mar 29, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR introduces a comprehensive clipboard widget feature including clipboard history tracking and storage, implements categorized snippets, extends UI controls with optional glyphs, adds a taskbar widget for quick access, and updates application settings and hotkey management. The version is bumped from 0.3.1 to 0.4.0.

Changes

Cohort / File(s) Summary
Clipboard History & Storage
ClipboardHistoryManager.cs, ClipboardMonitor.cs
New clipboard history tracking with persistent storage (max 50 entries), entry metadata (source, timestamp, pin state), and duplicate detection. ClipboardMonitor now captures clipboard changes asynchronously, detects images/storage items, and emits ClipboardHistoryEntry instances via EntryAdded event.
Clipboard Widget UI
ClipboardWidget.xaml, ClipboardWidget.xaml.cs
New floating window displaying clipboard history, snippets, and favorites with drag/resize interactions, opacity slider, compact mode toggle, and keyboard-driven paste mechanism using Win32 APIs and keybd_event.
Taskbar Widget
TaskbarWidget.xaml, TaskbarWidget.xaml.cs
New taskbar-integrated widget with dynamic repositioning tied to shell taskbar location, fullscreen app detection, right-click context menus, flyouts for clipboard/notes/folders/snippets, and Win32 subclass hook for Z-order management and backdrop behavior.
Widget Settings & Persistence
AppSettings.cs, HotkeyService.cs
Added methods to persist/load widget state (modules, enabled, position, size, opacity, compact mode) via JSON in settings.json. New hotkey constant HOTKEY_CLIPBOARD_WIDGET = 4 (Ctrl+Shift+V) with default configuration.
UI Control Enhancements
ActionPanel.cs
Extended CreateSettings(...) with optional onShowWidget and onQuit callbacks; updated CreateCascadeButton(...) and CreateNavigateButton(...) to accept optional glyph parameter for leading icons; added new ShowWidgetSubPanel(...) method to build module toggle lists.
Application Integration
MainWindow.xaml.cs, App.xaml.cs
MainWindow now manages clipboard history, displays clipboard view mode, integrates clipboard cards with context menus (pin/unpin, save as note, create snippet, delete), and manages taskbar widget lifecycle. App.xaml.cs updated startup minimization logic to be conditional on two settings and gated by 5-minute uptime threshold.
Snippet Management
SnippetManager.cs
Extended SnippetEntry with Category property; updated AddSnippet(...) to accept optional category parameter; added GetAllCategories() method to retrieve distinct categories for UI grouping.
Localization & Version
Lang.cs, UpdateService.cs, installer.iss
Added localization entries for clipboard widget and snippet category UI labels across English and French. Updated app version from 0.3.1 to 0.4.0 in UpdateService and installer configuration.

Sequence Diagram(s)

sequenceDiagram
    participant ClipMon as ClipboardMonitor
    participant CBEntry as ClipboardHistoryEntry
    participant CBMgr as ClipboardHistoryManager
    participant MainWin as MainWindow
    participant CBWidget as ClipboardWidget

    ClipMon->>ClipMon: Clipboard content changed
    ClipMon->>CBEntry: Parse content<br/>(text/RTF/image)
    CBEntry->>CBMgr: EntryAdded event<br/>(new entry)
    CBMgr->>CBMgr: Add + evict old<br/>non-pinned entries
    CBMgr->>CBMgr: Save to<br/>clipboard_history.json
    MainWin->>MainWin: Receive EntryAdded<br/>update if<br/>Clipboard view active
    MainWin->>MainWin: Refresh display<br/>with new entry
    CBWidget->>CBMgr: GetSorted() on<br/>Refresh/Reopen
    CBMgr->>CBWidget: Return entries<br/>(pinned first)
    CBWidget->>CBWidget: Render history<br/>cards
    CBWidget->>MainWin: User copies entry<br/>→ paste to<br/>previously focused
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • feat: v0.2.0 — Firebase sync, window attachment & note security #5: Modifies ActionPanel.CreateSettings(...) API and adds new settings subpanel behavior with callbacks, directly overlapping with signature and subpanel changes in this PR.
  • feat: AI integration, custom prompts, global hotkeys, reminder UI #2: Modifies ActionPanel.cs — changes to CreateSettings(...) signature and new sub-panel entry points align with similar UI integration patterns.
  • feat: fonts, note styles, Groq voice & Firebase sync #4: Modifies ActionPanel.cs methods including CreateSettings, CreateCascadeButton, and CreateNavigateButton with glyph/icon parameters, sharing overlapping UI control enhancements.

Poem

🐰✨ Clipboard hops into the widget's nest,
History pinned, oh what a quest!
Snippets sorted, categories shine,
Taskbar magic, dragging by design,
From 0.3 to 0.4, this feature's divine! 📋🎯

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title directly and accurately summarizes the three main features added: clipboard history, taskbar widget, and configurable modules.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 0.4.0

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
  • X
  • Mastodon
  • Reddit
  • LinkedIn

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

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@qodo-code-review

qodo-code-review Bot commented Mar 29, 2026

Copy link
Copy Markdown

Review Summary by Qodo

Clipboard history, taskbar widget, and configurable widget modules

✨ Enhancement 🧪 Tests

Grey Divider

Walkthroughs

Description
• Clipboard history manager with capture, persistence, pinning, and deduplication
• Taskbar widget with configurable modules (clipboard, notes, favorites, folders, snippets)
• Floating clipboard widget with opacity slider, compact mode, and drag-to-resize
• Widget module toggles in settings with dynamic button generation and fullscreen detection
• Snippet categories support with editable ComboBox in creation flyout
• Clipboard history view in main window with search, drag-and-drop, and context menu
• Internationalization (i18n) support for clipboard and widget strings (English/French)
Diagram
flowchart LR
  A["ClipboardMonitor"] -->|captures text/RTF/images| B["ClipboardHistoryManager"]
  B -->|persists to JSON| C["clipboard_history.json"]
  B -->|provides entries| D["TaskbarWidget"]
  B -->|provides entries| E["ClipboardWidget"]
  B -->|provides entries| F["MainWindow Clipboard View"]
  D -->|flyouts for| G["Clipboard/Notes/Favorites/Folders/Snippets"]
  H["AppSettings"] -->|loads/saves| I["Widget Modules Config"]
  H -->|loads/saves| J["Widget Position/Size/Opacity"]
  K["SnippetManager"] -->|categories| L["Snippet Flyout"]
  M["MainWindow"] -->|toggles| D
  M -->|toggles| E
Loading

Grey Divider

File Changes

1. TaskbarWidget.xaml.cs ✨ Enhancement +935/-0

Taskbar widget with module-based flyouts

TaskbarWidget.xaml.cs


2. TaskbarWidget.xaml ✨ Enhancement +11/-0

Minimal taskbar widget UI layout

TaskbarWidget.xaml


3. ClipboardWidget.xaml.cs ✨ Enhancement +439/-0

Floating clipboard widget with opacity and compact mode

ClipboardWidget.xaml.cs


View more (12)
4. ClipboardWidget.xaml ✨ Enhancement +60/-0

Floating widget UI with drag bar and resize grip

ClipboardWidget.xaml


5. ClipboardHistoryManager.cs ✨ Enhancement +133/-0

Clipboard history persistence and management

ClipboardHistoryManager.cs


6. ClipboardMonitor.cs ✨ Enhancement +118/-3

Enhanced clipboard monitoring with image/RTF/file capture

ClipboardMonitor.cs


7. MainWindow.xaml.cs ✨ Enhancement +375/-3

Clipboard history view, widget toggle, and integration

MainWindow.xaml.cs


8. ActionPanel.cs ✨ Enhancement +166/-24

Widget settings sub-panel and icon additions to menu buttons

ActionPanel.cs


9. SnippetManager.cs ✨ Enhancement +13/-1

Snippet categories support and category retrieval

SnippetManager.cs


10. AppSettings.cs ⚙️ Configuration changes +145/-0

Widget configuration persistence (modules, position, size, opacity)

AppSettings.cs


11. Lang.cs 📝 Documentation +50/-0

Clipboard history and widget localization strings

Lang.cs


12. HotkeyService.cs ✨ Enhancement +2/-0

Clipboard widget hotkey registration (Ctrl+Shift+V)

HotkeyService.cs


13. App.xaml.cs 🐞 Bug fix +8/-3

Minimize only on Windows auto-start, not manual launch

App.xaml.cs


14. UpdateService.cs ⚙️ Configuration changes +1/-1

Version bump to 0.4.0

UpdateService.cs


15. installer.iss ⚙️ Configuration changes +1/-1

Installer version update to 0.4.0

installer.iss


Grey Divider

Qodo Logo

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@qodo-code-review

qodo-code-review Bot commented Mar 29, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (6) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Wrong buffer size types 🐞 Bug ✓ Correctness
Description
ClipboardMonitor allocates byte arrays using WinRT size properties (e.g., IBuffer.Length and
stream.Size) that are not int-sized, which can break compilation or truncate/overflow when sizes
exceed int. This affects clipboard image/file capture paths and can prevent building or produce
corrupted reads.
Code

ClipboardMonitor.cs[R65-132]

+                                var buffer = await FileIO.ReadBufferAsync(file);
+                                var bytes = new byte[buffer.Length];
+                                using var reader = Windows.Storage.Streams.DataReader.FromBuffer(buffer);
+                                reader.ReadBytes(bytes);
+
+                                var imgEntry = new ClipboardHistoryEntry
+                                {
+                                    ContentType = "image",
+                                    ImageData = bytes,
+                                    SourceExePath = SourceExePath,
+                                    SourceTitle = file.Name,
+                                    CapturedAt = DateTime.Now
+                                };
+                                EntryAdded?.Invoke(imgEntry);
+                            }
+                            return;
+                        }
+
+                        var paths = items.Select(i => i.Path).Where(p => !string.IsNullOrEmpty(p)).ToList();
+                        if (paths.Count > 0)
+                        {
+                            var names = items.Select(i => i.Name).ToList();
+                            var fileEntry = new ClipboardHistoryEntry
+                            {
+                                ContentType = "text",
+                                TextContent = string.Join("\n", paths),
+                                SourceExePath = SourceExePath,
+                                SourceTitle = string.Join(", ", names),
+                                CapturedAt = DateTime.Now
+                            };
+                            EntryAdded?.Invoke(fileEntry);
+                            return;
+                        }
+                    }
+                }
+                catch { }
+            }
+
+            string? text = null;
+            string? rtf = null;
+            byte[]? imageData = null;
+
+            if (content.Contains(StandardDataFormats.Text))
+                text = await content.GetTextAsync();
+            if (content.Contains(StandardDataFormats.Rtf))
+            {
+                try { rtf = await content.GetRtfAsync(); } catch { }
+            }
+            if (content.Contains(StandardDataFormats.Bitmap))
+            {
+                try
+                {
+                    var reference = await content.GetBitmapAsync();
+                    using var stream = await reference.OpenReadAsync();
+                    var decoder = await BitmapDecoder.CreateAsync(stream);
+                    using var outStream = new InMemoryRandomAccessStream();
+                    var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, outStream);
+                    var pixelData = await decoder.GetPixelDataAsync();
+                    encoder.SetPixelData(decoder.BitmapPixelFormat, decoder.BitmapAlphaMode,
+                        decoder.PixelWidth, decoder.PixelHeight,
+                        decoder.DpiX, decoder.DpiY,
+                        pixelData.DetachPixelData());
+                    await encoder.FlushAsync();
+                    outStream.Seek(0);
+                    imageData = new byte[outStream.Size];
+                    var reader = new DataReader(outStream);
+                    await reader.LoadAsync((uint)imageData.Length);
+                    reader.ReadBytes(imageData);
Evidence
The code allocates arrays directly from WinRT buffer/stream size properties, then reads into them;
this relies on array-length compatibility and safe sizing, which is not guaranteed for these APIs
without explicit checked casts and bounds checks.

ClipboardMonitor.cs[54-135]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`ClipboardMonitor` allocates arrays with WinRT size properties (`buffer.Length`, `outStream.Size`) without explicit safe conversion/bounds checking. This can fail to compile depending on the projected types, and even if casted later, it can overflow/truncate for large buffers.

### Issue Context
This occurs when reading image files (`FileIO.ReadBufferAsync`) and when converting clipboard bitmaps into PNG bytes (`InMemoryRandomAccessStream.Size`).

### Fix Focus Areas
- ClipboardMonitor.cs[54-135]

### Suggested fix
- Use explicit, checked casts when converting WinRT sizes to array lengths, e.g.:
 - `var len = checked((int)buffer.Length); var bytes = new byte[len];`
 - `var len = checked((int)outStream.Size); imageData = new byte[len];`
- Guard against sizes larger than `int.MaxValue` (skip, cap, or stream-copy in chunks).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Blocking clipboard image writes 🐞 Bug ⛯ Reliability
Description
Clipboard image copy paths synchronously block on async WinRT writes (Wait/GetResults) while running
in UI event handlers. This can freeze the UI (and can deadlock depending on apartment/continuation
scheduling) when copying large images.
Code

ClipboardWidget.xaml.cs[R264-272]

+    private static void CopyEntryToClipboard(ClipboardHistoryEntry entry)
+    {
+        var dp = new DataPackage();
+        if (entry.ContentType == "image" && entry.ImageData != null)
+        {
+            var stream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
+            stream.WriteAsync(entry.ImageData.AsBuffer()).AsTask().Wait();
+            stream.Seek(0);
+            dp.SetBitmap(Windows.Storage.Streams.RandomAccessStreamReference.CreateFromStream(stream));
Evidence
ClipboardWidget and TaskbarWidget both call .AsTask().Wait() while preparing an image for the
clipboard, and MainWindow uses .GetResults() in a tap handler; these are synchronous waits inside
UI interactions and will block responsiveness during the async write.

ClipboardWidget.xaml.cs[247-280]
TaskbarWidget.xaml.cs[830-846]
MainWindow.xaml.cs[3284-3305]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Several UI-triggered code paths block synchronously on WinRT async operations (`WriteAsync(...).AsTask().Wait()` / `.GetResults()`) when building clipboard bitmap content. This can freeze the UI.

### Issue Context
Occurs when copying/pasting clipboard history images from widgets and when clicking clipboard history cards.

### Fix Focus Areas
- ClipboardWidget.xaml.cs[247-280]
- TaskbarWidget.xaml.cs[830-846]
- MainWindow.xaml.cs[3284-3305]

### Suggested fix
- Convert helpers to `async Task` and `await stream.WriteAsync(...)`.
- Convert event handlers to `async` (e.g., `Tapped += async ...`) and await the clipboard preparation before continuing.
- Avoid `.Wait()`/`.GetResults()` entirely in UI code paths.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Uncategorized snippets hidden 🐞 Bug ✓ Correctness
Description
ClipboardWidget shows "No snippets" whenever there are no snippet categories, even if snippets exist
but have an empty Category (the default for existing saved snippets). This makes existing snippets
inaccessible in the ClipboardWidget after upgrade.
Code

ClipboardWidget.xaml.cs[R150-160]

+    private void BuildSnippetSection()
+    {
+        AddDivider();
+        AddSectionHeader(Lang.T("snippet"));
+
+        var categories = _snippetManager.GetAllCategories();
+        if (categories.Count == 0)
+        {
+            AddEmptyLabel(Lang.T("no_snippets"));
+            return;
+        }
Evidence
SnippetEntry.Category defaults to empty string and GetAllCategories() filters out empty categories;
ClipboardWidget returns early when categories are empty and never renders uncategorized snippets,
unlike TaskbarWidget which does handle them.

ClipboardWidget.xaml.cs[150-160]
SnippetManager.cs[42-52]
SnippetManager.cs[113-125]
TaskbarWidget.xaml.cs[619-693]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`ClipboardWidget.BuildSnippetSection()` exits early when `GetAllCategories()` returns 0, which hides existing snippets that have an empty `Category` (default for pre-existing snippets).

### Issue Context
After introducing snippet categories, old saved snippets will deserialize with `Category == ""`. `GetAllCategories()` excludes them.

### Fix Focus Areas
- ClipboardWidget.xaml.cs[150-198]
- SnippetManager.cs[42-52]

### Suggested fix
- Mirror `TaskbarWidget.BuildSnippetsFlyout()` behavior:
 - Show categorized snippets by category.
 - Additionally show an "Uncategorized" section for `string.IsNullOrEmpty(s.Category)`.
- Alternatively, migrate existing snippets on load by assigning a default category (e.g., "General").

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Temp drag files never deleted 🐞 Bug ⛯ Reliability
Description
Drag-and-drop for clipboard images writes PNG files into the system temp directory but never removes
them. Repeated drags will accumulate orphaned files and waste disk space over time.
Code

MainWindow.xaml.cs[R3255-3259]

+                // Write image to temp file for Explorer drop
+                var tempPath = Path.Combine(Path.GetTempPath(), $"NoteUI_clip_{entry.Id[..8]}.png");
+                await File.WriteAllBytesAsync(tempPath, entry.ImageData);
+                var file = await Windows.Storage.StorageFile.GetFileFromPathAsync(tempPath);
+                args.Data.SetStorageItems(new[] { file });
Evidence
Both MainWindow’s clipboard card DragStarting and TaskbarWidget’s image flyout DragStarting persist
images to a temp path and pass a StorageFile reference, but there is no cleanup path after the drag
completes.

MainWindow.xaml.cs[3248-3264]
TaskbarWidget.xaml.cs[770-784]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Clipboard image drag/drop writes PNGs into `%TEMP%` and never deletes them.

### Issue Context
Files are created in DragStarting and handed off via `SetStorageItems`, but no later deletion occurs.

### Fix Focus Areas
- MainWindow.xaml.cs[3248-3264]
- TaskbarWidget.xaml.cs[770-784]

### Suggested fix
- Use an app-owned temp directory (e.g., `%LOCALAPPDATA%\NoteUI\Temp`) and periodically purge old files on startup.
- Or track created temp paths and delete them after a delay / when the app closes.
- Ensure deletion is resilient (best-effort try/catch).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Static subclass delegate retained 🐞 Bug ⛯ Reliability
Description
TaskbarWidget stores the window subclass proc delegate in a static field, which retains the
TaskbarWidget instance even after Close, and can cause cross-instance confusion if recreated. This
is unnecessary because the delegate can be safely held as an instance field for the native subclass
lifetime.
Code

TaskbarWidget.xaml.cs[R58-97]

+    private delegate IntPtr SubclassProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam, IntPtr uIdSubclass, IntPtr dwRefData);
+
+    // prevent GC of the delegate
+    private static SubclassProc? _subclassProc;
+
+    public TaskbarWidget(ClipboardHistoryManager clipboardHistory, SnippetManager snippetManager, NotesManager? notesManager = null)
+    {
+        this.InitializeComponent();
+        _clipboardHistory = clipboardHistory;
+        _snippetManager = snippetManager;
+        _notesManager = notesManager;
+        _modules = AppSettings.LoadWidgetModules();
+
+        BuildButtons();
+
+        _hWnd = WindowNative.GetWindowHandle(this);
+
+        // Transparent title bar
+        ExtendsContentIntoTitleBar = false;
+        var transparent = new Windows.UI.Color { A = 0, R = 0, G = 0, B = 0 };
+        AppWindow.TitleBar.BackgroundColor = transparent;
+        AppWindow.TitleBar.InactiveBackgroundColor = transparent;
+        AppWindow.TitleBar.ButtonBackgroundColor = transparent;
+        AppWindow.TitleBar.ButtonInactiveBackgroundColor = transparent;
+
+        // Presenter setup
+        var presenter = GetOverlappedPresenter();
+        presenter.SetBorderAndTitleBar(false, false);
+        presenter.IsResizable = false;
+        presenter.IsAlwaysOnTop = true;
+
+        // Win32 styling
+        RemoveWindowBorder();
+        DisableRoundedCorners();
+        SetWindowStyles();
+
+        // Subclass to keep TOPMOST and handle activation
+        _subclassProc = SubclassWndProc;
+        SetWindowSubclass(_hWnd, _subclassProc, IntPtr.Zero, IntPtr.Zero);
+
Evidence
The subclass delegate is assigned to a static field and later removed using that static field; the
static reference will keep the instance method target alive unless explicitly cleared, and makes
behavior depend on global state.

TaskbarWidget.xaml.cs[58-97]
TaskbarWidget.xaml.cs[850-856]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`TaskbarWidget` keeps the subclass proc delegate in a `static` field, which can retain the window instance and introduces global shared state.

### Issue Context
The code comment suggests this was done to prevent GC of the delegate for native calls, but an instance field achieves that without leaking the instance.

### Fix Focus Areas
- TaskbarWidget.xaml.cs[58-97]
- TaskbarWidget.xaml.cs[850-856]

### Suggested fix
- Change `private static SubclassProc? _subclassProc;` to `private SubclassProc? _subclassProc;`.
- Assign `_subclassProc = SubclassWndProc;` per instance.
- In `OnClosed`, call `RemoveWindowSubclass` and then set `_subclassProc = null;`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Heavy clipboard history persistence 🐞 Bug ➹ Performance
Description
ClipboardHistoryManager serializes and writes the entire history (including ImageData byte arrays)
to JSON on every add/remove/pin/clear. This can cause noticeable I/O and CPU spikes when copying
frequently or when images are large.
Code

ClipboardHistoryManager.cs[R56-79]

+    public void Save()
+    {
+        try
+        {
+            Directory.CreateDirectory(Path.GetDirectoryName(_savePath)!);
+            var json = JsonSerializer.Serialize(_entries, new JsonSerializerOptions { WriteIndented = true });
+            File.WriteAllText(_savePath, json);
+        }
+        catch { }
+    }
+
+    public void AddEntry(ClipboardHistoryEntry entry)
+    {
+        _entries.Insert(0, entry);
+        while (_entries.Count > MaxEntries)
+        {
+            var oldest = _entries.LastOrDefault(e => !e.IsPinned);
+            if (oldest != null)
+                _entries.Remove(oldest);
+            else
+                break;
+        }
+        Save();
+    }
Evidence
Save() serializes the full in-memory list and writes it to disk, and Save() is invoked on every
mutation method; entries may include large ImageData and RtfContent payloads.

ClipboardHistoryManager.cs[5-22]
ClipboardHistoryManager.cs[56-109]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Clipboard history is persisted by serializing the entire `_entries` list to JSON on every modification, which can be expensive, especially with image data.

### Issue Context
This happens for `AddEntry`, `RemoveEntry`, `TogglePin`, and `Clear`.

### Fix Focus Areas
- ClipboardHistoryManager.cs[56-109]
- ClipboardHistoryManager.cs[5-22]

### Suggested fix
- Debounce saves (e.g., schedule a save 250–500ms after last change) and coalesce multiple updates.
- Consider limiting image size stored (downscale/compress thumbnails) and/or storing images as separate files with references in JSON.
- Optionally save only metadata in JSON and keep large blobs out-of-band.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@devohmycode
devohmycode merged commit 14bfd34 into main Mar 29, 2026
1 check was pending

Uh oh!

There was an error while loading. Please reload this page.

qodo-code-review[bot]
qodo-code-review Bot reviewed Mar 29, 2026
View reviewed changes
Comment thread ClipboardMonitor.cs
Comment on lines +65 to +132
var buffer = await FileIO.ReadBufferAsync(file);
var bytes = new byte[buffer.Length];
using var reader = Windows.Storage.Streams.DataReader.FromBuffer(buffer);
reader.ReadBytes(bytes);

var imgEntry = new ClipboardHistoryEntry
{
ContentType = "image",
ImageData = bytes,
SourceExePath = SourceExePath,
SourceTitle = file.Name,
CapturedAt = DateTime.Now
};
EntryAdded?.Invoke(imgEntry);
}
return;
}

var paths = items.Select(i => i.Path).Where(p => !string.IsNullOrEmpty(p)).ToList();
if (paths.Count > 0)
{
var names = items.Select(i => i.Name).ToList();
var fileEntry = new ClipboardHistoryEntry
{
ContentType = "text",
TextContent = string.Join("\n", paths),
SourceExePath = SourceExePath,
SourceTitle = string.Join(", ", names),
CapturedAt = DateTime.Now
};
EntryAdded?.Invoke(fileEntry);
return;
}
}
}
catch { }
}

string? text = null;
string? rtf = null;
byte[]? imageData = null;

if (content.Contains(StandardDataFormats.Text))
text = await content.GetTextAsync();
if (content.Contains(StandardDataFormats.Rtf))
{
try { rtf = await content.GetRtfAsync(); } catch { }
}
if (content.Contains(StandardDataFormats.Bitmap))
{
try
{
var reference = await content.GetBitmapAsync();
using var stream = await reference.OpenReadAsync();
var decoder = await BitmapDecoder.CreateAsync(stream);
using var outStream = new InMemoryRandomAccessStream();
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, outStream);
var pixelData = await decoder.GetPixelDataAsync();
encoder.SetPixelData(decoder.BitmapPixelFormat, decoder.BitmapAlphaMode,
decoder.PixelWidth, decoder.PixelHeight,
decoder.DpiX, decoder.DpiY,
pixelData.DetachPixelData());
await encoder.FlushAsync();
outStream.Seek(0);
imageData = new byte[outStream.Size];
var reader = new DataReader(outStream);
await reader.LoadAsync((uint)imageData.Length);
reader.ReadBytes(imageData);

@qodo-code-review qodo-code-review Bot Mar 29, 2026

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Action required

1. Wrong buffer size types 🐞 Bug ✓ Correctness

ClipboardMonitor allocates byte arrays using WinRT size properties (e.g., IBuffer.Length and
stream.Size) that are not int-sized, which can break compilation or truncate/overflow when sizes
exceed int. This affects clipboard image/file capture paths and can prevent building or produce
corrupted reads.
Agent Prompt
### Issue description
`ClipboardMonitor` allocates arrays with WinRT size properties (`buffer.Length`, `outStream.Size`) without explicit safe conversion/bounds checking. This can fail to compile depending on the projected types, and even if casted later, it can overflow/truncate for large buffers.

### Issue Context
This occurs when reading image files (`FileIO.ReadBufferAsync`) and when converting clipboard bitmaps into PNG bytes (`InMemoryRandomAccessStream.Size`).

### Fix Focus Areas
- ClipboardMonitor.cs[54-135]

### Suggested fix
- Use explicit, checked casts when converting WinRT sizes to array lengths, e.g.:
  - `var len = checked((int)buffer.Length); var bytes = new byte[len];`
  - `var len = checked((int)outStream.Size); imageData = new byte[len];`
- Guard against sizes larger than `int.MaxValue` (skip, cap, or stream-copy in chunks).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread ClipboardWidget.xaml.cs
Comment on lines +264 to +272
private static void CopyEntryToClipboard(ClipboardHistoryEntry entry)
{
var dp = new DataPackage();
if (entry.ContentType == "image" && entry.ImageData != null)
{
var stream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
stream.WriteAsync(entry.ImageData.AsBuffer()).AsTask().Wait();
stream.Seek(0);
dp.SetBitmap(Windows.Storage.Streams.RandomAccessStreamReference.CreateFromStream(stream));

@qodo-code-review qodo-code-review Bot Mar 29, 2026

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Action required

2. Blocking clipboard image writes 🐞 Bug ⛯ Reliability

Clipboard image copy paths synchronously block on async WinRT writes (Wait/GetResults) while running
in UI event handlers. This can freeze the UI (and can deadlock depending on apartment/continuation
scheduling) when copying large images.
Agent Prompt
### Issue description
Several UI-triggered code paths block synchronously on WinRT async operations (`WriteAsync(...).AsTask().Wait()` / `.GetResults()`) when building clipboard bitmap content. This can freeze the UI.

### Issue Context
Occurs when copying/pasting clipboard history images from widgets and when clicking clipboard history cards.

### Fix Focus Areas
- ClipboardWidget.xaml.cs[247-280]
- TaskbarWidget.xaml.cs[830-846]
- MainWindow.xaml.cs[3284-3305]

### Suggested fix
- Convert helpers to `async Task` and `await stream.WriteAsync(...)`.
- Convert event handlers to `async` (e.g., `Tapped += async ...`) and await the clipboard preparation before continuing.
- Avoid `.Wait()`/`.GetResults()` entirely in UI code paths.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread ClipboardWidget.xaml.cs
Comment on lines +150 to +160
private void BuildSnippetSection()
{
AddDivider();
AddSectionHeader(Lang.T("snippet"));

var categories = _snippetManager.GetAllCategories();
if (categories.Count == 0)
{
AddEmptyLabel(Lang.T("no_snippets"));
return;
}

@qodo-code-review qodo-code-review Bot Mar 29, 2026

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Action required

3. Uncategorized snippets hidden 🐞 Bug ✓ Correctness

ClipboardWidget shows "No snippets" whenever there are no snippet categories, even if snippets exist
but have an empty Category (the default for existing saved snippets). This makes existing snippets
inaccessible in the ClipboardWidget after upgrade.
Agent Prompt
### Issue description
`ClipboardWidget.BuildSnippetSection()` exits early when `GetAllCategories()` returns 0, which hides existing snippets that have an empty `Category` (default for pre-existing snippets).

### Issue Context
After introducing snippet categories, old saved snippets will deserialize with `Category == ""`. `GetAllCategories()` excludes them.

### Fix Focus Areas
- ClipboardWidget.xaml.cs[150-198]
- SnippetManager.cs[42-52]

### Suggested fix
- Mirror `TaskbarWidget.BuildSnippetsFlyout()` behavior:
  - Show categorized snippets by category.
  - Additionally show an "Uncategorized" section for `string.IsNullOrEmpty(s.Category)`.
- Alternatively, migrate existing snippets on load by assigning a default category (e.g., "General").

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

1 more reviewer
@qodo-code-review qodo-code-review[bot] qodo-code-review[bot] left review comments
Reviewers whose approvals may not affect merge requirements

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

1 participant

@devohmycode
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.