GeezerVison is showing github.com — Release 0.3.0 by devohmycode · Pull Request #6 · devohmycode/NoteUI
Conversation
devohmycode
commented
Mar 26, 2026
•
edited by coderabbitai
Bot
Loading
Uh oh!
There was an error while loading. Please reload this page.
Summary
- Startup options: start with Windows, start minimized to tray
- Sorting & compact cards: sort notes by recent/created/alpha/color/size, compact card view toggle
- Clipboard monitor: background clipboard monitoring
- About panel: app info, developer credits, GitHub and Buy Me a Coffee links
- Icon helper: extract and cache application icons from executables
- Auto-update system: check for updates via GitHub Releases with CPU/CUDA variant detection, in-app download and install
Test plan
- Verify startup options (start with Windows, start minimized)
- Test sorting modes and compact cards toggle
- Open About panel and verify links open correctly
- Test update check from About panel
- Verify clipboard monitor functionality
Summary by CodeRabbit
Release Notes
-
New Features
- Startup options: launch with Windows and start minimized.
- Customizable note sorting: recent, created, alphabetical, color, or size.
- Compact card view toggle.
- Quick paste hotkey (Ctrl+Alt+V) to create notes from clipboard.
- Folder-based note organization.
- Inline note expansion and editing.
- Note linking with
[[noteTitle]] syntax.
- Automatic update checking with download capability.
- Clipboard source tracking displays application source on notes.
-
Bug Fixes
- Drag-and-drop support added to note editors.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Review Summary by Qodo
Release 0.3.0 - Inline editing, folders, note linking, and auto-updates
✨ Enhancement

Walkthroughs
Description
• **Inline note editing**: Expand and edit notes directly in the main list without opening separate
windows
• **Folder/category system**: Organize notes into folders with dedicated view modes and management
UI
• **Sorting options**: Sort notes by recent, created date, alphabetical order, color, or size with
persistent preferences
• **Compact card view**: Toggle between full and compact note card layouts
• **Note linking**: Create links between notes using [[note name]] syntax with Ctrl+Click
navigation
• **Clipboard monitoring**: Track source application and window title when notes are created
• **Auto-update system**: Check for updates via GitHub Releases with CPU/CUDA variant detection and
in-app download/install
• **Startup options**: Start application with Windows and optionally minimize to system tray on
launch
• **About panel**: Display app information, version, update checker, and links to GitHub and Buy Me
a Coffee
• **Icon extraction utility**: Extract and cache application icons from executables for clipboard
source tracking
• **Export functionality**: Export notes as Markdown or plain text with file save and clipboard
options
• **Drag-and-drop images**: Insert images into notes by dragging from files or browser
• **Global hotkey**: Added Ctrl+Alt+V shortcut for paste-as-note feature
• **Localization**: Added 60+ new strings in English and French for all new features
• **Installer updates**: Version bump to 0.3.0 with application restart support during installation
Diagram
flowchart LR
A["Clipboard Monitor"] -->|"Track source app"| B["Note Entry"]
B -->|"Organize"| C["Folder System"]
C -->|"Display"| D["Main Window"]
D -->|"Sort by"| E["Recent/Created/Alpha/Color/Size"]
D -->|"Edit inline"| F["Rich Text Editor"]
F -->|"Link notes"| G["Note Linking"]
D -->|"Toggle"| H["Compact View"]
I["Update Service"] -->|"Check GitHub"| J["Auto-Update"]
J -->|"Download & Install"| K["New Version"]
L["Startup Options"] -->|"Configure"| M["App Launch"]
M -->|"Minimize to tray"| N["System Tray"]
O["Icon Helper"] -->|"Extract icons"| P["Source App Display"]
Loading

File Changes
1. MainWindow.xaml.cs
✨ Enhancement +983/-6
Major feature release with inline editing, folders, and auto-updates
• Added clipboard monitoring to track source application when notes are created
• Implemented inline note expansion with rich text editing directly in the main list
• Added auto-update system with background checking and in-app download/install
• Implemented folder/category system for organizing notes with dedicated view modes
• Added sorting preferences (recent, created, alphabetical, color, size) and compact card view
toggle
• Added note linking feature with [[note name]] syntax and Ctrl+Click navigation
• Integrated startup options (start with Windows, start minimized to tray)
MainWindow.xaml.cs
2. NoteWindow.xaml.cs
✨ Enhancement +649/-0
Note linking, export, and folder management features
• Added note linking detection and insertion with hidden ID encoding
• Implemented Ctrl+Click handler to navigate between linked notes
• Added export functionality (Markdown and plain text) with file save and clipboard options
• Added folder management menu for organizing notes into categories
• Implemented drag-and-drop image insertion from files and browser
• Added ReloadFromDisk() method to sync with inline editor changes
NoteWindow.xaml.cs
3. ActionPanel.cs
✨ Enhancement +367/-1
Settings UI enhancements for sorting, startup, and about panel
• Added sort mode selector with five sorting options (recent, created, alphabetical, color, size)
• Added compact cards toggle to switch between full and compact note card layouts
• Added startup options sub-panel for Windows startup and minimized launch settings
• Added About panel with app info, version, update checker, and external links (GitHub, Buy Me a
Coffee)
• Extended CreateSettings() method signature with new parameters for startup and sorting options
ActionPanel.cs
View more (11)
4. Lang.cs
📝 Documentation +84/-0
Localization strings for new features in English and French
• Added 60+ new localization strings for folders, sorting, export, startup, updates, and about
features
• Provided translations in both English and French for all new UI elements
• Added strings for note linking, clipboard operations, and update notifications
Lang.cs
5. IconHelper.cs
✨ Enhancement +211/-0
Icon extraction and caching utility for clipboard source tracking
• New utility class to extract and cache application icons from executables
• Implements memory and disk caching to avoid repeated extraction
• Uses P/Invoke to extract icon pixels from .exe files and encode as PNG
• Provides LoadIconAsync() method to load icons into XAML Image controls
IconHelper.cs
6. UpdateService.cs
✨ Enhancement +136/-0
Auto-update system with GitHub release integration
• New service for checking GitHub releases and downloading updates
• Detects CPU vs CUDA build variant and downloads appropriate installer
• Implements progress reporting during download and silent installer launch
• Compares semantic versions to determine if update is available
UpdateService.cs
7. ClipboardMonitor.cs
✨ Enhancement +105/-0
Clipboard monitoring to track note source application
ClipboardMonitor.cs
8. NotesManager.cs
✨ Enhancement +42/-3
Note manager enhancements for folders, sorting, and clipboard source tracking
• Added GetByTitle() and SearchByTitle() methods for note lookup and search
• Added GetAllFolders() to retrieve distinct folder names from notes
• Added UpdateNoteFolder() to assign or update note folder assignment
• Enhanced GetSorted() to support multiple sort modes (recent, created, alphabetical, color, size)
• Added SourceExePath, SourceTitle, and Folder properties to NoteEntry class
NotesManager.cs
9. AppSettings.cs
✨ Enhancement +102/-0
Settings persistence for startup, sorting, and compact view options
• Added startup options persistence (start with Windows via registry, start minimized)
• Added compact cards preference storage
• Added sort mode preference storage
• Implemented registry integration for Windows startup configuration
AppSettings.cs
10. HotkeyService.cs
✨ Enhancement +2/-0
Global hotkey support for paste-as-note feature
• Added HOTKEY_PASTE_NOTE constant for new global hotkey
• Added default shortcut entry for paste-as-note (Ctrl+Alt+V)
HotkeyService.cs
11. App.xaml.cs
✨ Enhancement +6/-0
Start minimized to tray support
• Added logic to hide main window on startup if startMinimized setting is enabled
• Allows app to launch directly to system tray
App.xaml.cs
12. installer.iss
⚙️ Configuration changes +4/-1
Installer configuration for version 0.3.0 release
• Updated version from 0.2.0 to 0.3.0
• Added CloseApplications directives to close running NoteUI instances during installation
• Added RestartApplications to relaunch app after installation
installer.iss
13. MainWindow.xaml
✨ Enhancement +19/-2
XAML UI updates for update banner and inline note collapse
• Added update banner UI element with download progress display
• Added PointerPressed event handler to collapse expanded notes when scrolling
• Set transparent background on scroll viewer and notes list for consistency
MainWindow.xaml
14. NoteWindow.xaml ✨ Enhancement +6/-0
Enable drag-and-drop functionality for note editors
• Added drag-and-drop support to the main note editor RichEditBox with AllowDrop="True" and
event handlers for DragOver and Drop
• Added identical drag-and-drop support to the task note editor RichEditBox in the compact view
• Both editors now handle drag-over and drop events through NoteEditor_DragOver and
NoteEditor_Drop handlers
NoteWindow.xaml


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


1. Icon cache compile error 🐞 Bug ✓ Correctness
Description
IconHelper.EncodePngAsync allocates new byte[stream.Size], but stream.Size is not an int, so
this code does not compile and blocks the build.
Code
IconHelper.cs[R131-142]
+ using var stream = new InMemoryRandomAccessStream();
+ var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
+ encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied,
+ (uint)w, (uint)h, 96, 96, bgra);
+ await encoder.FlushAsync();
+
+ stream.Seek(0);
+ var bytes = new byte[stream.Size];
+ var reader = new DataReader(stream);
+ await reader.LoadAsync((uint)bytes.Length);
+ reader.ReadBytes(bytes);
+ return bytes;
Evidence
EncodePngAsync uses InMemoryRandomAccessStream.Size directly as an array length, but C# array
lengths must be int-convertible; this is a compile-time error.
IconHelper.cs[127-145]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
### Issue description
`EncodePngAsync` uses `new byte[stream.Size]`, which fails to compile because the array length must be an `int`.
### Issue Context
This prevents the entire project from building.
### Fix Focus Areas
- IconHelper.cs[127-145]
### Suggested fix
- Convert `stream.Size` to an `int` safely (e.g., `var len = checked((int)stream.Size); var bytes = new byte[len];`).
- Prefer `using var reader = new DataReader(stream.GetInputStreamAt(0));` and dispose/detach reader if needed.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
2. Update banner z-order overlap 🐞 Bug ✓ Correctness
Description
UpdateBanner and NotesScroll are both placed in Grid.Row="3", and NotesScroll is declared after
the banner, so it will render on top and intercept input, making the banner effectively
hidden/un-clickable when shown.
Code
MainWindow.xaml[R97-118]
+ <!-- Update banner -->
+ <Button x:Name="UpdateBanner" Grid.Row="3" Visibility="Collapsed"
+ HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
+ Background="{ThemeResource AccentFillColorDefaultBrush}"
+ Foreground="{ThemeResource TextOnAccentFillColorPrimaryBrush}"
+ BorderThickness="0" CornerRadius="6" Margin="12,0,12,8"
+ Padding="8,6" MinHeight="0" FontSize="12"
+ Click="UpdateBanner_Click">
+ <StackPanel Orientation="Horizontal" Spacing="6">
+ <FontIcon Glyph="" FontSize="12"
+ Foreground="{ThemeResource TextOnAccentFillColorPrimaryBrush}"/>
+ <TextBlock x:Name="UpdateBannerText" Text="" VerticalAlignment="Center"
+ Foreground="{ThemeResource TextOnAccentFillColorPrimaryBrush}"/>
+ </StackPanel>
+ </Button>
+
<!-- Row 3: Notes list -->
<ScrollViewer x:Name="NotesScroll" Grid.Row="3" Padding="12,0,12,12"
- VerticalScrollBarVisibility="Auto">
- <StackPanel x:Name="NotesList" Spacing="8"/>
+ VerticalScrollBarVisibility="Auto"
+ Background="Transparent" PointerPressed="NotesScroll_PointerPressed">
+ <StackPanel x:Name="NotesList" Spacing="8" Background="Transparent"/>
</ScrollViewer>
Evidence
The XAML shows both controls in the same Grid cell (row 3). In a Grid, later-declared elements are
rendered above earlier ones, so the ScrollViewer will cover the banner when the banner is made
visible.
MainWindow.xaml[97-118]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
### Issue description
`UpdateBanner` shares the same `Grid.Row` as `NotesScroll` and is declared before it, so it will be covered by the ScrollViewer.
### Issue Context
This likely prevents users from seeing/clicking the update prompt.
### Fix Focus Areas
- MainWindow.xaml[97-118]
### Suggested fix
Choose one:
1) Add a dedicated grid row for the banner and move `NotesScroll` to the next row.
2) Keep same row but declare `UpdateBanner` after `NotesScroll` and/or set `Canvas.ZIndex` (or equivalent) so the banner is on top, and add top margin/padding to `NotesScroll` to avoid content being obscured.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
3. Updater runs unverified EXE 🐞 Bug ⛨ Security
Description
The auto-update flow downloads an installer EXE and executes it without verifying Authenticode
signature or a known checksum, so a compromised download could lead to arbitrary code execution.
Code
UpdateService.cs[R80-127]
+ /// <summary>Downloads the installer to a temp file and returns the path.</summary>
+ public static async Task<string?> DownloadInstallerAsync(string downloadUrl, IProgress<double>? progress = null)
+ {
+ try
+ {
+ using var response = await Http.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead);
+ if (!response.IsSuccessStatusCode)
+ return null;
+
+ var totalBytes = response.Content.Headers.ContentLength ?? -1;
+ var tempPath = Path.Combine(Path.GetTempPath(), $"NoteUI-Setup-{Guid.NewGuid():N}.exe");
+
+ await using var contentStream = await response.Content.ReadAsStreamAsync();
+ await using var fileStream = new FileStream(tempPath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true);
+
+ var buffer = new byte[81920];
+ long totalRead = 0;
+ int bytesRead;
+
+ while ((bytesRead = await contentStream.ReadAsync(buffer)) > 0)
+ {
+ await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead));
+ totalRead += bytesRead;
+ if (totalBytes > 0)
+ progress?.Report((double)totalRead / totalBytes);
+ }
+
+ progress?.Report(1.0);
+ return tempPath;
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ /// <summary>Launches the downloaded installer with /SILENT and exits the app.</summary>
+ public static void LaunchInstallerAndExit(string installerPath)
+ {
+ Process.Start(new ProcessStartInfo
+ {
+ FileName = installerPath,
+ Arguments = "/SILENT",
+ UseShellExecute = true
+ });
+
+ Environment.Exit(0);
+ }
Evidence
UpdateService downloads an EXE to a temp path and LaunchInstallerAndExit runs it directly; there is
no signature or checksum verification step anywhere in UpdateService.
UpdateService.cs[80-127]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
### Issue description
Downloaded update installers are executed without integrity/authenticity verification.
### Issue Context
The download URL comes from GitHub API, but defense-in-depth still requires verifying the downloaded binary before executing.
### Fix Focus Areas
- UpdateService.cs[80-127]
### Suggested fix
Implement at least one verification layer before `Process.Start`:
- Verify Authenticode signature matches an expected publisher certificate.
- And/or ship an expected SHA-256 checksum (or fetch checksums from release assets) and validate the downloaded file.
- Refuse to execute and surface an error state if verification fails.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

4. Synchronous save per keystroke 🐞 Bug ➹ Performance
Description
The inline expanded RichEditBox calls SaveInlineEditor on every TextChanged, and SaveInlineEditor
calls _notes.Save(localOnly: true) which writes notes.json synchronously, causing heavy disk I/O
and typing lag.
Code
MainWindow.xaml.cs[R1586-1594]
+ editor.TextChanged += (_, _) =>
+ {
+ if (_suppressInlineTextChanged) return;
+ _inlineEditorDirty = true;
+ SaveInlineEditor();
+ // Live sync with open NoteWindow
+ var openWin = _openNoteWindows.FirstOrDefault(w => w.NoteId == capturedNoteId);
+ openWin?.ReloadFromDisk();
+ };
Evidence
MainWindow’s inline editor TextChanged handler triggers SaveInlineEditor for every edit, and
SaveInlineEditor calls NotesManager.Save; NotesManager.Save uses File.WriteAllText synchronously.
MainWindow.xaml.cs[1254-1269]
MainWindow.xaml.cs[1540-1594]
NotesManager.cs[49-66]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
### Issue description
Inline expansion currently writes `notes.json` on every keystroke via a synchronous `File.WriteAllText`, which can cause UI stalls and excessive disk writes.
### Issue Context
The problem is the combination of per-keystroke `TextChanged` + synchronous `Save()`.
### Fix Focus Areas
- MainWindow.xaml.cs[1540-1594]
- MainWindow.xaml.cs[1254-1269]
- NotesManager.cs[49-66]
### Suggested fix
- Implement a debounce (e.g., DispatcherTimer ~300–750ms) so saves happen after the user pauses typing.
- Save immediately on collapse/losing focus, but not on every keystroke.
- Optionally make NotesManager.Save async (File.WriteAllTextAsync) or move serialization+write off the UI thread.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
5. Hidden IDs leak in export 🐞 Bug ✓ Correctness
Description
Note links embed hidden note IDs into the document text, but export/copy uses TextGetOptions.None
(which this code comments includes hidden text), so exported Markdown/plain text can contain
GUIDs/zero-width characters.
Code
NoteWindow.xaml.cs[R1793-1804]
+ private string GetPlainText()
+ {
+ NoteEditor.Document.GetText(TextGetOptions.None, out var text);
+ return text.TrimEnd('\r', '\n');
+ }
+
+ private string ConvertNoteToMarkdown()
+ {
+ var doc = NoteEditor.Document;
+ doc.GetText(TextGetOptions.None, out var fullText);
+ fullText = fullText.TrimEnd('\r', '\n');
+ if (string.IsNullOrEmpty(fullText)) return "";
Evidence
InsertNoteLink appends \u200B + target.Id + \u200B and marks it Hidden; later, the code explicitly
states TextGetOptions.None includes hidden, and export routines use
GetText(TextGetOptions.None), so hidden IDs are included in exports/copy.
NoteWindow.xaml.cs[1687-1719]
NoteWindow.xaml.cs[1763-1767]
NoteWindow.xaml.cs[1793-1804]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
### Issue description
Export/copy paths include hidden note IDs inserted for note links, causing leaked GUIDs/zero-width characters in exported output.
### Issue Context
The note-link feature uses hidden text for IDs, but export uses `TextGetOptions.None`.
### Fix Focus Areas
- NoteWindow.xaml.cs[1687-1719]
- NoteWindow.xaml.cs[1763-1767]
- NoteWindow.xaml.cs[1793-1804]
### Suggested fix
- Use a text extraction mode that excludes hidden text for export/copy (align with the code comment that `IncludeNumbering` skips hidden).
- Alternatively, post-process extracted text to remove hidden-ID segments (e.g., strip `\u200B{guid}\u200B`).
- Ensure Markdown conversion also uses the hidden-excluding extraction so it doesn’t emit IDs.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
6. Full-res images still embedded 🐞 Bug ➹ Performance
Description
InsertImageFromStream encodes the full-resolution bitmap into a PNG stream before applying the
maxWidth resize, so large images are still stored at full size (only the displayed dimensions
change).
Code
NoteWindow.xaml.cs[R2000-2030]
+ private async Task InsertImageFromStream(RichEditBox editor, Windows.Storage.Streams.IRandomAccessStream sourceStream)
+ {
+ var decoder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(sourceStream);
+ var w = (int)decoder.PixelWidth;
+ var h = (int)decoder.PixelHeight;
+
+ var softBitmap = await decoder.GetSoftwareBitmapAsync(
+ Windows.Graphics.Imaging.BitmapPixelFormat.Bgra8,
+ Windows.Graphics.Imaging.BitmapAlphaMode.Premultiplied);
+
+ var pngStream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
+ var encoder = await Windows.Graphics.Imaging.BitmapEncoder.CreateAsync(
+ Windows.Graphics.Imaging.BitmapEncoder.PngEncoderId, pngStream);
+ encoder.SetSoftwareBitmap(softBitmap);
+ await encoder.FlushAsync();
+
+ const int maxWidth = 340;
+ if (w > maxWidth)
+ {
+ h = (int)(h * ((double)maxWidth / w));
+ w = maxWidth;
+ }
+
+ pngStream.Seek(0);
+ editor.Document.Selection.InsertImage(
+ w, h, 0,
+ Microsoft.UI.Text.VerticalCharacterAlignment.Baseline,
+ "image",
+ pngStream);
+ // pngStream NOT disposed — RichEditBox holds reference for rendering
+
Evidence
The code flushes the PNG encoder using the original SoftwareBitmap, then later adjusts w/h only
for InsertImage(...), meaning storage/memory footprint remains full-res.
NoteWindow.xaml.cs[2000-2038]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
### Issue description
Dropped images are encoded at original resolution; the later `maxWidth` logic only affects display size.
### Issue Context
This can bloat note storage and slow loading/sync.
### Fix Focus Areas
- NoteWindow.xaml.cs[2000-2038]
### Suggested fix
- Apply a real downscale before encoding (e.g., decode with `BitmapTransform.ScaledWidth/ScaledHeight`, or resize the SoftwareBitmap) so the PNG data is actually smaller.
- Then call `InsertImage` with the scaled dimensions matching the encoded bitmap.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
View more (1)
7. Settings JSON parsed repeatedly 🐞 Bug ➹ Performance
Description
AppSettings.LoadStartMinimized/LoadCompactCards/LoadSortPreference parse JSON via
JsonDocument.Parse without disposing the JsonDocument, and these are called in hot UI paths,
causing avoidable allocations and file reads.
Code
AppSettings.cs[R214-274]
+ public static bool LoadStartMinimized()
+ {
+ try
+ {
+ if (File.Exists(SettingsPath))
+ {
+ var json = File.ReadAllText(SettingsPath);
+ var doc = JsonDocument.Parse(json);
+ if (doc.RootElement.TryGetProperty("startMinimized", out var prop))
+ return prop.GetBoolean();
+ }
+ }
+ catch { }
+ return false;
+ }
+
+ public static void SaveStartMinimized(bool enabled)
+ {
+ MergeAndSaveSettings(new Dictionary<string, object> { ["startMinimized"] = enabled });
+ }
+
+ // ── Compact cards ────────────────────────────────────────
+
+ public static bool LoadCompactCards()
+ {
+ try
+ {
+ if (File.Exists(SettingsPath))
+ {
+ var json = File.ReadAllText(SettingsPath);
+ var doc = JsonDocument.Parse(json);
+ if (doc.RootElement.TryGetProperty("compactCards", out var prop))
+ return prop.GetBoolean();
+ }
+ }
+ catch { }
+ return false;
+ }
+
+ public static void SaveCompactCards(bool enabled)
+ {
+ MergeAndSaveSettings(new Dictionary<string, object> { ["compactCards"] = enabled });
+ }
+
+ // ── Sort preference ────────────────────────────────────────
+
+ public static string LoadSortPreference()
+ {
+ try
+ {
+ if (File.Exists(SettingsPath))
+ {
+ var json = File.ReadAllText(SettingsPath);
+ var doc = JsonDocument.Parse(json);
+ if (doc.RootElement.TryGetProperty("sortMode", out var prop))
+ return prop.GetString() ?? "recent";
+ }
+ }
+ catch { }
+ return "recent";
+ }
Evidence
Each Load* method reads SettingsPath and creates a JsonDocument but never wraps it in using, so
disposal doesn’t occur; these getters are invoked frequently (e.g., compact-card selection during
card creation).
AppSettings.cs[214-274]
MainWindow.xaml.cs[441-446]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
### Issue description
Settings getters repeatedly read/parse the JSON file and leak JsonDocument resources by not disposing.
### Issue Context
These methods can be called many times during UI rendering, amplifying the overhead.
### Fix Focus Areas
- AppSettings.cs[214-274]
- MainWindow.xaml.cs[441-446]
### Suggested fix
- Change `var doc = JsonDocument.Parse(json);` to `using var doc = JsonDocument.Parse(json);` in the new Load* methods.
- Consider caching settings in memory (load once at startup + invalidate on save) so UI card creation doesn’t hit disk/JSON parsing repeatedly.
ⓘ 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.
coderabbitai
Bot
commented
Mar 26, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
Caution
Review failed
The pull request is closed.
ℹ️ Recent review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8e3ade7d-bf31-4e5c-8466-5ab71bd1805f
📥 Commits
Reviewing files that changed from the base of the PR and between 5272f2b and 6cd8f4d.
📒 Files selected for processing (14)
ActionPanel.cs
App.xaml.cs
AppSettings.cs
ClipboardMonitor.cs
HotkeyService.cs
IconHelper.cs
Lang.cs
MainWindow.xaml
MainWindow.xaml.cs
NoteWindow.xaml
NoteWindow.xaml.cs
NotesManager.cs
UpdateService.cs
installer.iss
📝 Walkthrough
Walkthrough
This PR introduces clipboard source tracking, automatic update detection with installer downloads, note linking via [[]] syntax, folder-based note organization, configurable sorting/startup/compact preferences, an About panel with expanded settings, executable icon caching, and drag-and-drop support for notes.
Changes
| Cohort / File(s) |
Summary |
Settings & Configuration Expansion ActionPanel.cs, AppSettings.cs, HotkeyService.cs, Lang.cs |
Added new optional parameters to CreateSettings() for startup toggles, sort selection, and compact cards. Introduced 8 new AppSettings methods for persisting startup preferences (registry-based), minimized state, compact cards, and sort mode (JSON-based). Added HOTKEY_PASTE_NOTE constant and 84 new localization strings for folders, shortcuts, export, startup, about, and update UI. |
Update Service & Installation UpdateService.cs, installer.iss |
Created new UpdateService class for GitHub release API polling, version comparison, and installer download streaming with progress reporting. Added LaunchInstallerAndExit() to trigger silent installation. Updated installer script to version 0.3.0 and enabled auto-close/restart of target app during setup. |
Clipboard & Icon Management ClipboardMonitor.cs, IconHelper.cs |
Implemented ClipboardMonitor class to track clipboard changes, resolve foreground window/process metadata via Win32, and clean browser-specific title suffixes. Added IconHelper with async icon extraction from executables, PNG encoding, and dual-tier caching (memory and disk). |
Note Data Model & Organization NotesManager.cs |
Extended NoteEntry with SourceExePath, SourceTitle, and Folder properties. Refactored GetSorted() to accept a sortMode parameter (recent/created/alpha/color/size). Added GetByTitle(), SearchByTitle(), GetAllFolders(), and UpdateNoteFolder() for folder support and lookup. |
Note Linking & Folder Support NoteWindow.xaml.cs, NoteWindow.xaml |
Implemented [[ pattern detection and flyout-based note-link suggestion/insertion with hidden note ID encoding. Added Ctrl+Click note navigation and NoteLinkClicked event. Extended note menu with folder management (set/clear/create). Added ReloadFromDisk() method. Enabled drag-and-drop on both RichEditBox controls. |
Main Window UI, Startup & Features MainWindow.xaml.cs, MainWindow.xaml, App.xaml.cs |
Extended view modes with Folders and FolderFilter for folder browsing. Added inline note expansion/editing with expandable card UI and format flyout. Integrated clipboard source display and clipboard monitoring on note creation. Implemented update banner with background version check and installer download/launch. Added paste hotkey triggering PasteAsNewNote(). Integrated LoadSortPreference() and LoadCompactCards() into note list rendering and card creation. Enhanced window startup to conditionally hide based on LoadStartMinimized(). |
Sequence Diagram(s)
sequenceDiagram
participant Clipboard as Windows<br/>Clipboard API
participant Monitor as ClipboardMonitor
participant Win32 as Win32 API
participant App as MainWindow
participant Notes as NotesManager
participant IconHelper as IconHelper
Clipboard->>Monitor: ContentChanged event
Monitor->>Win32: GetForegroundWindow()
Win32-->>Monitor: window handle
Monitor->>Win32: QueryFullProcessImageName()
Win32-->>Monitor: exePath, processID
Monitor->>Win32: GetWindowText()
Win32-->>Monitor: title
Monitor->>Monitor: CleanTitle(title, exePath)
Monitor-->>Monitor: sourceExePath,<br/>sourceTitle stored
App->>Monitor: Read SourceExePath/<br/>SourceTitle
App->>Notes: AddNote() with source
Notes-->>Notes: NoteEntry with<br/>clipboard metadata
App->>IconHelper: LoadIconAsync(exePath)
IconHelper->>IconHelper: Check memory cache
alt Cache miss
IconHelper->>Win32: ExtractIcon,<br/>GetIconInfo, etc.
Win32-->>IconHelper: Icon pixel data
IconHelper->>IconHelper: Encode PNG,<br/>write to disk
end
IconHelper-->>App: Update Image.Source
Loading
sequenceDiagram
participant MainWindow as MainWindow
participant UpdateService as UpdateService
participant GitHub as GitHub API
participant Installer as Installer
MainWindow->>UpdateService: CheckForUpdateAsync()
UpdateService->>GitHub: GET /releases/latest
GitHub-->>UpdateService: JSON (tag_name, assets)
UpdateService->>UpdateService: Compare CurrentVersion<br/>vs tag_name
alt Update available
UpdateService-->>UpdateService: Select asset URL<br/>(CUDA or CPU)
UpdateService-->>MainWindow: UpdateInfo record
MainWindow->>MainWindow: Show UpdateBanner
Note over MainWindow: User clicks banner
MainWindow->>UpdateService: DownloadInstallerAsync()
UpdateService->>UpdateService: Stream download<br/>to temp .exe,<br/>report progress
UpdateService-->>MainWindow: installerPath
MainWindow->>UpdateService: LaunchInstallerAndExit(path)
UpdateService->>Installer: Start /SILENT
UpdateService->>UpdateService: Environment.Exit(0)
else No update
UpdateService-->>MainWindow: null
end
Loading
sequenceDiagram
participant User as User
participant NoteWindow as NoteWindow Editor
participant Flyout as Note Link Flyout
participant NotesManager as NotesManager
participant App as MainWindow
User->>NoteWindow: Type [[
NoteWindow->>NoteWindow: Detect [[ pattern
NoteWindow->>NotesManager: SearchByTitle(query)
NotesManager-->>NoteWindow: List of notes (up to 10)
NoteWindow->>Flyout: Show suggestions
User->>Flyout: Select note
Flyout->>NoteWindow: Insert note link
NoteWindow->>NoteWindow: Format: visible title +<br/>hidden noteID<br/>(zero-width chars)
NoteWindow->>NoteWindow: Save note
Note over User: Later...
User->>NoteWindow: Ctrl+Click on link
NoteWindow->>NoteWindow: Scan adjacent green text
NoteWindow->>NoteWindow: Extract hidden noteID
NoteWindow->>NoteWindow: Raise NoteLinkClicked(noteId)
NoteWindow-->>App: NoteLinkClicked event
App->>App: OpenNote(noteId)
Loading
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~75 minutes
Possibly related PRs
- PR
#3: Modifies AppSettings and App.xaml.cs startup behavior for minimized window state, directly overlapping with startup-related changes in this PR.
- PR
#2: Extends ActionPanel.CreateSettings() signature with additional optional parameters for new settings UI sections, sharing the same method expansion pattern.
- PR
#5: Adds further optional parameters to ActionPanel.CreateSettings() for settings panel customization, related through the same settings UI construction pathway.
Poem
🐰 Hops through clipboards, tracks the source,
Icons cached—a swift, swift course!
Notes link up with [[ syntax grand,
Folders organize, updates on hand,
Inline editing makes hearts sing,
What a feature-packed spring! ✨
✨ 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.3.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.
Comment on lines
+131
to
+142
|
|
using var stream = new InMemoryRandomAccessStream();
|
|
|
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
|
|
|
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied,
|
|
|
(uint)w, (uint)h, 96, 96, bgra);
|
|
|
await encoder.FlushAsync();
|
|
|
|
|
|
stream.Seek(0);
|
|
|
var bytes = new byte[stream.Size];
|
|
|
var reader = new DataReader(stream);
|
|
|
await reader.LoadAsync((uint)bytes.Length);
|
|
|
reader.ReadBytes(bytes);
|
|
|
return bytes;
|
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. Icon cache compile error 🐞 Bug ✓ Correctness
IconHelper.EncodePngAsync allocates new byte[stream.Size], but stream.Size is not an int, so
this code does not compile and blocks the build.
Agent Prompt
### Issue description
`EncodePngAsync` uses `new byte[stream.Size]`, which fails to compile because the array length must be an `int`.
### Issue Context
This prevents the entire project from building.
### Fix Focus Areas
- IconHelper.cs[127-145]
### Suggested fix
- Convert `stream.Size` to an `int` safely (e.g., `var len = checked((int)stream.Size); var bytes = new byte[len];`).
- Prefer `using var reader = new DataReader(stream.GetInputStreamAt(0));` and dispose/detach reader if needed.
ⓘ 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
+97
to
118
|
|
<!-- Update banner -->
|
|
|
<Button x:Name="UpdateBanner" Grid.Row="3" Visibility="Collapsed"
|
|
|
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
|
|
|
Background="{ThemeResource AccentFillColorDefaultBrush}"
|
|
|
Foreground="{ThemeResource TextOnAccentFillColorPrimaryBrush}"
|
|
|
BorderThickness="0" CornerRadius="6" Margin="12,0,12,8"
|
|
|
Padding="8,6" MinHeight="0" FontSize="12"
|
|
|
Click="UpdateBanner_Click">
|
|
|
<StackPanel Orientation="Horizontal" Spacing="6">
|
|
|
<FontIcon Glyph="" FontSize="12"
|
|
|
Foreground="{ThemeResource TextOnAccentFillColorPrimaryBrush}"/>
|
|
|
<TextBlock x:Name="UpdateBannerText" Text="" VerticalAlignment="Center"
|
|
|
Foreground="{ThemeResource TextOnAccentFillColorPrimaryBrush}"/>
|
|
|
</StackPanel>
|
|
|
</Button>
|
|
|
|
|
|
<!-- Row 3: Notes list -->
|
|
|
<ScrollViewer x:Name="NotesScroll" Grid.Row="3" Padding="12,0,12,12"
|
|
|
VerticalScrollBarVisibility="Auto">
|
|
|
<StackPanel x:Name="NotesList" Spacing="8"/>
|
|
|
VerticalScrollBarVisibility="Auto"
|
|
|
Background="Transparent" PointerPressed="NotesScroll_PointerPressed">
|
|
|
<StackPanel x:Name="NotesList" Spacing="8" Background="Transparent"/>
|
|
|
</ScrollViewer>
|
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. Update banner z-order overlap 🐞 Bug ✓ Correctness
UpdateBanner and NotesScroll are both placed in Grid.Row="3", and NotesScroll is declared after
the banner, so it will render on top and intercept input, making the banner effectively
hidden/un-clickable when shown.
Agent Prompt
### Issue description
`UpdateBanner` shares the same `Grid.Row` as `NotesScroll` and is declared before it, so it will be covered by the ScrollViewer.
### Issue Context
This likely prevents users from seeing/clicking the update prompt.
### Fix Focus Areas
- MainWindow.xaml[97-118]
### Suggested fix
Choose one:
1) Add a dedicated grid row for the banner and move `NotesScroll` to the next row.
2) Keep same row but declare `UpdateBanner` after `NotesScroll` and/or set `Canvas.ZIndex` (or equivalent) so the banner is on top, and add top margin/padding to `NotesScroll` to avoid content being obscured.
ⓘ 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
+80
to
+127
|
|
/// <summary>Downloads the installer to a temp file and returns the path.</summary>
|
|
|
public static async Task<string?> DownloadInstallerAsync(string downloadUrl, IProgress<double>? progress = null)
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
using var response = await Http.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead);
|
|
|
if (!response.IsSuccessStatusCode)
|
|
|
return null;
|
|
|
|
|
|
var totalBytes = response.Content.Headers.ContentLength ?? -1;
|
|
|
var tempPath = Path.Combine(Path.GetTempPath(), $"NoteUI-Setup-{Guid.NewGuid():N}.exe");
|
|
|
|
|
|
await using var contentStream = await response.Content.ReadAsStreamAsync();
|
|
|
await using var fileStream = new FileStream(tempPath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true);
|
|
|
|
|
|
var buffer = new byte[81920];
|
|
|
long totalRead = 0;
|
|
|
int bytesRead;
|
|
|
|
|
|
while ((bytesRead = await contentStream.ReadAsync(buffer)) > 0)
|
|
|
{
|
|
|
await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead));
|
|
|
totalRead += bytesRead;
|
|
|
if (totalBytes > 0)
|
|
|
progress?.Report((double)totalRead / totalBytes);
|
|
|
}
|
|
|
|
|
|
progress?.Report(1.0);
|
|
|
return tempPath;
|
|
|
}
|
|
|
catch
|
|
|
{
|
|
|
return null;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>Launches the downloaded installer with /SILENT and exits the app.</summary>
|
|
|
public static void LaunchInstallerAndExit(string installerPath)
|
|
|
{
|
|
|
Process.Start(new ProcessStartInfo
|
|
|
{
|
|
|
FileName = installerPath,
|
|
|
Arguments = "/SILENT",
|
|
|
UseShellExecute = true
|
|
|
});
|
|
|
|
|
|
Environment.Exit(0);
|
|
|
}
|
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. Updater runs unverified exe 🐞 Bug ⛨ Security
The auto-update flow downloads an installer EXE and executes it without verifying Authenticode
signature or a known checksum, so a compromised download could lead to arbitrary code execution.
Agent Prompt
### Issue description
Downloaded update installers are executed without integrity/authenticity verification.
### Issue Context
The download URL comes from GitHub API, but defense-in-depth still requires verifying the downloaded binary before executing.
### Fix Focus Areas
- UpdateService.cs[80-127]
### Suggested fix
Implement at least one verification layer before `Process.Start`:
- Verify Authenticode signature matches an expected publisher certificate.
- And/or ship an expected SHA-256 checksum (or fetch checksums from release assets) and validate the downloaded file.
- Refuse to execute and surface an error state if verification fails.
ⓘ 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.
|