Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
120 commits
Select commit Hold shift + click to select a range
bc85033
Initial plan
Copilot Jan 31, 2026
10d9f08
Add Windows context menu integration for grabbing text from images
Copilot Jan 31, 2026
11d0d89
Improve error handling and user feedback for context menu registration
Copilot Jan 31, 2026
62d4e34
Address code review feedback: improve error handling and test quality
Copilot Jan 31, 2026
f332799
Final improvements: consistent error handling for removal and proper …
Copilot Jan 31, 2026
bdc3476
Clarify MessageBoxButton enum usage in MessageBox.Show
TheJoeFin Jan 31, 2026
f93ea39
Add Windows 11 modern context menu support for packaged apps and Open…
Copilot Jan 31, 2026
dc96e4e
Address code review feedback: improve argument parsing and path valid…
Copilot Jan 31, 2026
1dc80d3
Improve path validation with GetFullPath and user error messages
Copilot Jan 31, 2026
d5cac6a
Add 'Paste in Grab Frame' to NotifyIcon context menu
TheJoeFin Feb 12, 2026
e273780
Add "Open With" & Share Target support for image files
TheJoeFin Feb 12, 2026
42d79cf
Start redraw timer after image drop in GrabFrame
TheJoeFin Feb 13, 2026
83cafd3
Improve handling of loaded vs. captured images in GrabFrame
TheJoeFin Feb 24, 2026
7147cd2
Code formatting
TheJoeFin Feb 26, 2026
d24a9e6
Merge pull request #616 from TheJoeFin/copilot/add-context-menu-integ…
TheJoeFin Feb 26, 2026
cc979fa
Add date/time math support to calculator and tests
TheJoeFin Feb 26, 2026
f9258d2
Add support for combined duration segments in date math
TheJoeFin Feb 26, 2026
70888bc
Enhance DateTime Math Evaluation and Subtraction Support
TheJoeFin Feb 26, 2026
8da968c
Add unit conversion functionality and corresponding unit tests
TheJoeFin Feb 27, 2026
1f7da7c
Add support for "C" and "F" temp unit abbreviations
TheJoeFin Feb 28, 2026
d8ed478
Initial plan
Copilot Feb 28, 2026
a0fc61e
Apply all 5 PR review comment fixes
Copilot Feb 28, 2026
8ad4b1e
Merge pull request #619 from TheJoeFin/copilot/sub-pr-618
TheJoeFin Feb 28, 2026
c3113ab
Merge pull request #618 from TheJoeFin/units-math
TheJoeFin Feb 28, 2026
da044ec
Add template-based capture system with region support
TheJoeFin Mar 1, 2026
2dc268c
Add file-based GrabTemplate storage and executor
TheJoeFin Mar 1, 2026
ae3aa3d
Add Grab Template support as post-grab actions
TheJoeFin Mar 1, 2026
376b0fa
Add new user settings for templates and context menu options
TheJoeFin Mar 1, 2026
6fb2a4b
Handle ApplicationDataContainer size limit exception
TheJoeFin Mar 1, 2026
58b7ba3
Add Grab Templates management to Post-Grab Actions editor
TheJoeFin Mar 1, 2026
61af2a0
Add Grab Template creation and overlay support
TheJoeFin Mar 1, 2026
66b7856
Enhance Grab Template editing and region visualization
TheJoeFin Mar 1, 2026
092bf5d
Add inline chip picker control for RichTextBox
TheJoeFin Mar 2, 2026
0a4df29
Add chip picker for template output editing in GrabFrame
TheJoeFin Mar 2, 2026
64875df
Remove unused OutputTemplateBox; enhance TemplateOutputBox
TheJoeFin Mar 2, 2026
dc563f3
Update packages
TheJoeFin Mar 2, 2026
d4b1101
Code style
TheJoeFin Mar 2, 2026
4411659
Add Microsoft.WindowsAppSDK.WinUI package reference
TheJoeFin Mar 2, 2026
7c8c891
Improve GrabFrame content area calculation and DPI accuracy
TheJoeFin Mar 2, 2026
3cd3001
Improve GrabFrame overlay scaling and image cropping accuracy
TheJoeFin Mar 3, 2026
bfe084b
Highlight unused template regions in GrabFrame/preview
TheJoeFin Mar 4, 2026
cdca429
Add Grab Template support to lookup and grab workflows
TheJoeFin Mar 4, 2026
e411b46
Fix ListView scrollbar, improve Post-Grab Actions UI
TheJoeFin Mar 4, 2026
aacf37e
Add regex pattern matching support to Grab Templates
TheJoeFin Mar 4, 2026
20bc475
Fix NullReferenceException in PatternMatchModeDialog
TheJoeFin Mar 4, 2026
6802c6a
Fix ArgumentNullException in CommitSelection when dialog steals focus
TheJoeFin Mar 4, 2026
209ca5f
Skip region overlays in FSG for pattern-only templates
TheJoeFin Mar 4, 2026
5205db2
Refactor GrabTemplate, improve regex usage, cleanup code
TheJoeFin Mar 5, 2026
78c3762
Sync overlay canvas to DIP; use [GeneratedRegex] patterns
TheJoeFin Mar 5, 2026
87e0c38
Initial plan
Copilot Mar 5, 2026
90a8704
fix: address review feedback on docs, error messages, and unused vari…
Copilot Mar 5, 2026
d62d7eb
Merge pull request #627 from TheJoeFin/copilot/sub-pr-620
TheJoeFin Mar 5, 2026
8bf9482
Refactor context menu for post-grab actions and templates
TheJoeFin Mar 5, 2026
d606fec
Synchronize post-grab actions across FullscreenGrab windows
TheJoeFin Mar 6, 2026
2b7bfce
Add fullscreen grab selection styles
TheJoeFin Mar 6, 2026
9c50c38
Harden barcode bitmap handling
TheJoeFin Mar 6, 2026
2df0c51
Improve selection cropping for zoom/pan and add tests
TheJoeFin Mar 6, 2026
d28f1b4
Initial plan
Copilot Mar 7, 2026
28a021d
Fix 5 review comments: shortcut cap, ItemsDictionary clear, null Item…
Copilot Mar 7, 2026
27df35c
Merge pull request #629 from TheJoeFin/copilot/sub-pr-620
TheJoeFin Mar 7, 2026
6952739
Switch pattern match mode selection to RadioButtons
TheJoeFin Mar 7, 2026
68504b6
Merge pull request #620 from TheJoeFin/grab-templates
TheJoeFin Mar 7, 2026
db483a6
Add UiAutomation language support and options
TheJoeFin Mar 7, 2026
2a4d5d5
Add UI Automation as OCR language option and utilities
TheJoeFin Mar 7, 2026
582a76b
Add UI Automation text extraction settings and UI
TheJoeFin Mar 7, 2026
b06e333
Add UI Automation OCR mode and refactor language handling
TheJoeFin Mar 7, 2026
e07062f
Add unit tests for language and UI automation utilities
TheJoeFin Mar 7, 2026
918474c
Add UIA overlay models & snapshot extraction, improve logic
TheJoeFin Mar 7, 2026
e81c3c5
Add comprehensive unit tests for capture and UI automation
TheJoeFin Mar 7, 2026
ca7cefa
Add UI Automation overlay support to GrabFrame window
TheJoeFin Mar 7, 2026
eff7643
Rename "UI Automation" language option to "Direct Text"
TheJoeFin Mar 8, 2026
a1f9a28
Initial plan
Copilot Mar 8, 2026
06c4f51
Fix bitmap disposal, isDrawing reset, and indentation per review comm…
Copilot Mar 8, 2026
1fcc08a
Remove using statement for selectionBitmap in table OCR
TheJoeFin Mar 8, 2026
fa135cb
Merge pull request #631 from TheJoeFin/copilot/sub-pr-630
TheJoeFin Mar 8, 2026
4036c0d
Improve language picker to use real and keyboard languages
TheJoeFin Mar 9, 2026
76ff856
Merge pull request #630 from TheJoeFin/ui-automation
TheJoeFin Mar 9, 2026
c22c595
Refactor history and settings to use sidecar JSON files
TheJoeFin Mar 10, 2026
870a287
Refactor settings management and history import/export
TheJoeFin Mar 10, 2026
c6b26ed
Refactor regex pattern storage to use settings service
TheJoeFin Mar 10, 2026
3138d89
Add tests for HistoryService and SettingsService migration
TheJoeFin Mar 10, 2026
6bee41d
add signing
TheJoeFin Mar 10, 2026
1e6d362
fix selfcontained flags
TheJoeFin Mar 10, 2026
72088ed
Initial plan
Copilot Mar 10, 2026
3132696
fix: sanitize path traversal and add exception handling in HistorySer…
Copilot Mar 10, 2026
7007d11
Merge pull request #636 from TheJoeFin/copilot/sub-pr-634
TheJoeFin Mar 10, 2026
a0d362e
Add tests for managed JSON settings import/export
TheJoeFin Mar 13, 2026
8f035f4
feat: add EnableFileBackedManagedSettings and refactor SettingsServic…
TheJoeFin Mar 16, 2026
d9305e1
fix: normalize UiAutomation language to its OCR fallback on persist
TheJoeFin Mar 16, 2026
e9708c6
refactor: GrabTemplateManager to dual-store with resolve/sync and exp…
TheJoeFin Mar 16, 2026
664dbc1
feat: include managed settings folder and grab templates in import/ex…
TheJoeFin Mar 16, 2026
6c4fe81
feat: add experimental file-backed settings toggle to Danger Settings
TheJoeFin Mar 16, 2026
b50c726
feat: move Direct Text section to bottom of Language Settings and mar…
TheJoeFin Mar 16, 2026
0ac9a12
test: add settings isolation collection and expand coverage for dual-…
TheJoeFin Mar 16, 2026
7bbbd02
Merge pull request #634 from TheJoeFin/better-mem
TheJoeFin Mar 16, 2026
5879d95
add regex mgmt to etw menu
TheJoeFin Mar 19, 2026
f5536a8
fix: guard against COM exception in TryCreateTextRangeOverlayItem
TheJoeFin Mar 19, 2026
9253c36
refactor: replace System.Windows.MessageBox with Wpf.Ui.Controls.Mess…
TheJoeFin Mar 19, 2026
f521c5a
feat: add text-only grab template support
TheJoeFin Mar 19, 2026
6d2866c
feat: apply grab templates to folder image OCR
TheJoeFin Mar 19, 2026
b910692
fix: allow grab templates to appear in user-defined order in post gra…
TheJoeFin Mar 19, 2026
67914da
Add template selection & overlay to GrabFrame UI
TheJoeFin Mar 19, 2026
868bf70
Update NuGet package versions in main and test projects
TheJoeFin Mar 19, 2026
7946148
fix: use stored image for OCR when GrabFrame is frozen
TheJoeFin Mar 20, 2026
cbead0a
feat: add ZoomWhenFrozen scroll mode and fix PresentationSource crash…
TheJoeFin Mar 20, 2026
0371720
fix: pan teal selection border with region and draw it outside captur…
TheJoeFin Mar 20, 2026
cfeb1a3
feat: add Reset View, Show Word Borders, and Overlay Opacity to View …
TheJoeFin Mar 20, 2026
cbeec18
fix: sync TemplateRegionOverlayCanvas sizing with RectanglesCanvas
TheJoeFin Mar 23, 2026
fde1dea
main merge
TheJoeFin Mar 23, 2026
456f2fe
make versions 4.13
TheJoeFin Mar 23, 2026
70e9e19
Catch lang name issue
TheJoeFin Mar 23, 2026
07123ed
Read multiple barcodes
TheJoeFin Mar 23, 2026
daf47f1
Clean up the FSG Settings page a bit
TheJoeFin Mar 23, 2026
0b3bfaf
Add settings pages for Grab Frame, Quick Lookup, and Edit Text Window
TheJoeFin Mar 24, 2026
f313a21
Fix minor issues in FSG description, icon, canvas margin, and unused …
TheJoeFin Mar 24, 2026
01becf2
Update NuGet package versions in project files
TheJoeFin Mar 27, 2026
d0e0ada
Disable WindowsAppSDKSelfContained by default, enable for self-contai…
TheJoeFin Mar 27, 2026
66c0cbf
Switch to OCR fallback if UI Automation on static images
TheJoeFin Apr 3, 2026
7f81bb8
Sync rectangles canvas size when using image coordinates
TheJoeFin Apr 3, 2026
8643518
Redact PII from diagnostics; expand bug report details
TheJoeFin Apr 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .claude/settings.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"Bash(dotnet build:*)",
"Bash(dotnet test:*)",
"Bash(gh pr list:*)",
"Bash(/mnt/c/Program Files/dotnet/dotnet.exe:*)"
"Bash(/mnt/c/Program Files/dotnet/dotnet.exe:*)",
"WebFetch(domain:github.com)"
],
"deny": []
}
Expand Down
79 changes: 75 additions & 4 deletions .github/workflows/Release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ on:

permissions:
contents: write
id-token: write

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
Expand All @@ -27,6 +28,9 @@ env:
BUILD_X64_SC: 'bld/x64/Text-Grab-Self-Contained'
BUILD_ARM64: 'bld/arm64'
BUILD_ARM64_SC: 'bld/arm64/Text-Grab-Self-Contained'
ARTIFACT_SIGNING_ENDPOINT: 'https://eus.codesigning.azure.net/'
ARTIFACT_SIGNING_ACCOUNT_NAME: 'JoeFinAppsSigningCerts'
ARTIFACT_SIGNING_CERTIFICATE_PROFILE_NAME: 'JoeFinApps'

jobs:
build:
Expand Down Expand Up @@ -77,7 +81,7 @@ jobs:
run: >-
dotnet publish ${{ env.PROJECT_PATH }}
--runtime win-x64
--self-contained false
--no-self-contained
-c Release
-v minimal
-o ${{ env.BUILD_X64 }}
Expand All @@ -91,7 +95,7 @@ jobs:
run: >-
dotnet publish ${{ env.PROJECT_PATH }}
--runtime win-x64
--self-contained true
--self-contained
-c Release
-v minimal
-o ${{ env.BUILD_X64_SC }}
Expand All @@ -105,7 +109,7 @@ jobs:
run: >-
dotnet publish ${{ env.PROJECT_PATH }}
--runtime win-arm64
--self-contained false
--no-self-contained
-c Release
-v minimal
-o ${{ env.BUILD_ARM64 }}
Expand All @@ -118,7 +122,7 @@ jobs:
run: >-
dotnet publish ${{ env.PROJECT_PATH }}
--runtime win-arm64
--self-contained true
--self-contained
-c Release
-v minimal
-o ${{ env.BUILD_ARM64_SC }}
Expand All @@ -137,6 +141,73 @@ jobs:
Rename-Item "${{ env.BUILD_ARM64_SC }}/${{ env.PROJECT }}.exe" 'Text-Grab-arm64.exe'
}

- name: Validate Azure Trusted Signing configuration
shell: pwsh
env:
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
run: |
$requiredSecrets = @{
AZURE_TENANT_ID = $env:AZURE_TENANT_ID
AZURE_CLIENT_ID = $env:AZURE_CLIENT_ID
AZURE_SUBSCRIPTION_ID = $env:AZURE_SUBSCRIPTION_ID
}

$missingSecrets = @(
$requiredSecrets.GetEnumerator() |
Where-Object { [string]::IsNullOrWhiteSpace($_.Value) } |
ForEach-Object { $_.Key }
)

if ($missingSecrets.Count -gt 0) {
throw "Configure these repository secrets before running the release workflow: $($missingSecrets -join ', ')"
}

$signingConfig = @{
ARTIFACT_SIGNING_ENDPOINT = '${{ env.ARTIFACT_SIGNING_ENDPOINT }}'
ARTIFACT_SIGNING_ACCOUNT_NAME = '${{ env.ARTIFACT_SIGNING_ACCOUNT_NAME }}'
ARTIFACT_SIGNING_CERTIFICATE_PROFILE_NAME = '${{ env.ARTIFACT_SIGNING_CERTIFICATE_PROFILE_NAME }}'
}

$missing = @(
$signingConfig.GetEnumerator() |
Where-Object {
[string]::IsNullOrWhiteSpace($_.Value) -or
$_.Value.StartsWith('REPLACE_WITH_') -or
$_.Value.Contains('REPLACE_WITH_')
} |
ForEach-Object { $_.Key }
)

if ($missing.Count -gt 0) {
throw "Update the Azure Trusted Signing placeholders in .github/workflows/Release.yml before running the release workflow: $($missing -join ', ')"
}

- name: Azure login for Trusted Signing
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

- name: Sign release executables
uses: azure/artifact-signing-action@v1
with:
endpoint: ${{ env.ARTIFACT_SIGNING_ENDPOINT }}
signing-account-name: ${{ env.ARTIFACT_SIGNING_ACCOUNT_NAME }}
certificate-profile-name: ${{ env.ARTIFACT_SIGNING_CERTIFICATE_PROFILE_NAME }}
files: |
${{ github.workspace }}\${{ env.BUILD_X64 }}\${{ env.PROJECT }}.exe
${{ github.workspace }}\${{ env.BUILD_X64_SC }}\${{ env.PROJECT }}.exe
${{ github.workspace }}\${{ env.BUILD_ARM64 }}\Text-Grab-arm64.exe
${{ github.workspace }}\${{ env.BUILD_ARM64_SC }}\Text-Grab-arm64.exe
file-digest: SHA256
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
description: Text Grab
description-url: https://github.com/TheJoeFin/Text-Grab

- name: Create self-contained archives
shell: pwsh
run: |
Expand Down
82 changes: 82 additions & 0 deletions Tests/BarcodeUtilitiesTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using Text_Grab;
using Text_Grab.Models;
using Text_Grab.Utilities;
using UnitsNet;
using Windows.Storage.Streams;
using static System.Net.Mime.MediaTypeNames;

namespace Tests;

public class BarcodeUtilitiesTests
{
[Fact]
public void TryToReadBarcodes_WithDisposedBitmap_ReturnsEmptyList()
{
Bitmap disposedBitmap = new(8, 8);
disposedBitmap.Dispose();

List<OcrOutput> results = BarcodeUtilities.TryToReadBarcodes(disposedBitmap);

Assert.Empty(results);
}

[Fact]
public void TryToReadBarcodes_WithTwoQrCodes_ReturnsTwoResults()
{
// Build a side-by-side bitmap containing two different QR codes
using Bitmap qr1 = BarcodeUtilities.GetQrCodeForText("https://example.com", ZXing.QrCode.Internal.ErrorCorrectionLevel.M);
using Bitmap qr2 = BarcodeUtilities.GetQrCodeForText("https://example.org", ZXing.QrCode.Internal.ErrorCorrectionLevel.M);

using Bitmap combined = new(qr1.Width + qr2.Width, Math.Max(qr1.Height, qr2.Height));
using (Graphics g = Graphics.FromImage(combined))
{
g.Clear(Color.White);
g.DrawImage(qr1, 0, 0);
g.DrawImage(qr2, qr1.Width, 0);
}

List<OcrOutput> results = BarcodeUtilities.TryToReadBarcodes(combined);

Assert.Equal(2, results.Count);
Assert.All(results, r => Assert.Equal(OcrOutputKind.Barcode, r.Kind));
Assert.Contains(results, r => r.RawOutput == "https://example.com");
Assert.Contains(results, r => r.RawOutput == "https://example.org");
}

[WpfFact]
public void ReadTestSingleQRCode()
{
string expectedOutput = "This is a test of the QR Code system";
string testFilePath = FileUtilities.GetPathToLocalFile(@".\Images\QrCodeTestImage.png");

Bitmap testBmp = new(testFilePath);

List<OcrOutput> result = BarcodeUtilities.TryToReadBarcodes(testBmp);

Assert.Single(result);
Assert.Equal(expectedOutput, result[0].RawOutput);
}

[Fact]
public async Task GetBitmapFromIRandomAccessStream_ReturnsBitmapIndependentOfSourceStream()
{
using Bitmap sourceBitmap = new(8, 8);
sourceBitmap.SetPixel(0, 0, Color.Red);

using MemoryStream memoryStream = new();
sourceBitmap.Save(memoryStream, ImageFormat.Png);

using InMemoryRandomAccessStream randomAccessStream = new();
_ = await randomAccessStream.WriteAsync(memoryStream.ToArray().AsBuffer());

Bitmap clonedBitmap = ImageMethods.GetBitmapFromIRandomAccessStream(randomAccessStream);

Assert.Equal(8, clonedBitmap.Width);
Assert.Equal(8, clonedBitmap.Height);
Assert.Equal(Color.Red.ToArgb(), clonedBitmap.GetPixel(0, 0).ToArgb());
}
}
Loading
Loading