GeezerVison is showing github.com — feat: clipboard history, taskbar widget & configurable modules by devohmycode · Pull Request #8 · devohmycode/NoteUI
Conversation
devohmycode
commented
Mar 29, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
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.
…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
- 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
Bot
commented
Mar 29, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
📝 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
Comment @coderabbitai help to get the list of available commands and usage tips.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Review Summary by Qodo
Clipboard history, taskbar widget, and configurable widget modules
✨ Enhancement 🧪 Tests

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

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


|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Code Review by Qodo
🐞 Bugs (6) 📘 Rule violations (0) 📎 Requirement gaps (0)


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

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

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


|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
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);
|
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.

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.
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));
|
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.

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.
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;
|
|
|
}
|
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.

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.
Reviewers
1 more reviewer
Reviewers whose approvals may not affect merge requirements
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
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.
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.
|