Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
288f6d1
Scaffold Text-Grab.Core, Text-Grab.Core.Windows, and Tests.Core projects
TheJoeFin Aug 27, 2026
c48db75
Move StringMethods.cs into Text-Grab.Core
TheJoeFin Aug 27, 2026
0fb1265
Move CharacterUtilities.cs and TextSearchUtilities.cs into Text-Grab.…
TheJoeFin Aug 27, 2026
700cab2
Move RecognizerExecutor.cs and BuiltInRecognizer.cs into Text-Grab.Core
TheJoeFin Aug 27, 2026
e677b54
Move StoredRegex.cs and split PatternItem.cs into Text-Grab.Core
TheJoeFin Aug 27, 2026
c110cd1
Move ILanguage.cs and OcrLinesWords.cs into Text-Grab.Core.Windows
TheJoeFin Aug 27, 2026
edefeaa
Move the OCR engine implementations into Text-Grab.Core.Windows
TheJoeFin Aug 27, 2026
30af90f
Establish RectangleF as the Core tier's geometry currency
TheJoeFin Aug 27, 2026
8398af2
Give Core a settings seam via ITextGrabSettings
TheJoeFin Aug 27, 2026
d7edfca
Scaffold Tests.Core.Windows, guard the tier boundaries, wire CI
TheJoeFin Aug 27, 2026
cd24749
Rebuild the Core split plan from five reconnaissance passes
TheJoeFin Aug 27, 2026
d251324
Move Singleton, StreamWrapper, NullAsyncResult and Json into Text-Gra…
TheJoeFin Aug 27, 2026
bf171f7
Extract packaging identity and move StorageFileExtensions into Text-G…
TheJoeFin Aug 27, 2026
9c4d2e5
Read the app version from the entry assembly, not the executing one
TheJoeFin Aug 27, 2026
ce24f0b
Merge Enums.cs into Text-Grab.Core and move GetOpenContentKindForPath
TheJoeFin Aug 27, 2026
35f9b14
Move ten Models files into Text-Grab.Core and finish the FileOpenUtil…
TheJoeFin Aug 27, 2026
f6a1cd0
Move seven Utilities/Extensions/Interfaces files into Core, split two…
TheJoeFin Aug 27, 2026
f05be11
Convert WordBorderInfo/TemplateRegion to RectangleF, move both plus G…
TheJoeFin Aug 27, 2026
147ece4
Move CalculationService into Text-Grab.Core
TheJoeFin Aug 27, 2026
0000bc6
Split MarkdownDocumentUtilities: pure AST/regex helpers into Core
TheJoeFin Aug 27, 2026
7e539c0
Move eight zero-blocker files into Text-Grab.Core.Windows
TheJoeFin Aug 27, 2026
966a497
Move SettingsStorageExtensions and LimitedAccessFeatureUtilities into…
TheJoeFin Aug 27, 2026
4711c5e
Move the Windows AI chain into Text-Grab.Core.Windows
TheJoeFin Aug 27, 2026
dff9aa4
Split Extensions/LanguageExtensions.cs into Core.Windows and an app-s…
TheJoeFin Aug 27, 2026
90af6b8
Record the RTL blocker batch 3d created for 4c
TheJoeFin Aug 27, 2026
93c1729
Move the automation settings provider and profile into Text-Grab.Core
TheJoeFin Aug 27, 2026
0d3b485
Move OcrOutput and BarcodeUtilities into Text-Grab.Core.Windows
TheJoeFin Aug 30, 2026
cc9c581
Move TesseractHelper into Text-Grab.Core.Windows, split out the downl…
TheJoeFin Aug 30, 2026
bd84507
Delete dead ResultTable code, move the clustering algorithm to Core
TheJoeFin Aug 30, 2026
3b0bf02
Split OcrUtilities, move the OCR text assembly into Core.Windows
TheJoeFin Aug 30, 2026
9ef3171
Move HDR tone-mapping and imaging leaves into Core/Core.Windows
TheJoeFin Aug 30, 2026
bbf601b
Split ImageMethods.cs, move the headless bitmap helpers to Core.Windows
TheJoeFin Aug 30, 2026
60c7b21
Split ClipboardUtilities.cs, move the CF_HTML table parser to plain Core
TheJoeFin Aug 30, 2026
bb5484c
Split FreeformCaptureUtilities.cs, move CreateMaskedBitmap to Core.Wi…
TheJoeFin Aug 30, 2026
5ad4968
Drop the dead ToSourceAsync and the WinUI package it pulled in
TheJoeFin Aug 30, 2026
f156b63
Move HdrScreenCapture into Core.Windows behind a UI-thread hook
TheJoeFin Aug 30, 2026
f103294
Move the Windows AI and language chain into Core.Windows
TheJoeFin Aug 30, 2026
08d6a8b
Move the text-to-speech chain into Text-Grab.Core / Core.Windows
TheJoeFin Aug 30, 2026
cd9a7c1
Move AudioTranscriptionUtilities into Text-Grab.Core.Windows wholesale
TheJoeFin Aug 30, 2026
baa98f0
Split WebSearchUrlModel into a pure record and an app-side catalog
TheJoeFin Aug 30, 2026
3952d92
Deferred-ledger sweep: move FileUtilities/ContextMenuUtilities, prune…
TheJoeFin Aug 30, 2026
a8591aa
Move HistoryInfo to Core.Windows, retiring its System.Windows.Rect
TheJoeFin Aug 30, 2026
212234a
6e: split the history persistence pipeline out of HistoryService
TheJoeFin Aug 30, 2026
9f78694
Plan: record 6e and the HistoryInfo move; reopen two section 7 rows
TheJoeFin Aug 30, 2026
a3643e6
7a: move seven pure model/utility test suites to Tests.Core
TheJoeFin Aug 30, 2026
873ea58
7a: move five headless-Windows test suites to Tests.Core.Windows
TheJoeFin Aug 30, 2026
5585199
7a: split five test suites across the tier boundary
TheJoeFin Aug 30, 2026
fe4fd59
7a: delete ScaffoldingSmokeTests, its job is done
TheJoeFin Aug 30, 2026
695671f
7a follow-up: restore CRLF and attribute spacing
TheJoeFin Aug 30, 2026
6497dd3
7b: remove the dead Table-Complex.png content item
TheJoeFin Aug 30, 2026
3f4b222
7b: move GrabFrameFileUtilities and FileAssociationUtilities to Core.…
TheJoeFin Aug 30, 2026
688f100
Plan: re-derive the deferred ledger and never-move list against B2
TheJoeFin Aug 30, 2026
0e43f83
Plan: record 7a's test-placement decisions and add the final layer map
TheJoeFin Aug 30, 2026
1f0d921
7a fix: map Tests.Core.Windows to its real solution platforms
TheJoeFin Aug 30, 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
10 changes: 10 additions & 0 deletions .github/workflows/Release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ env:
PROJECT: 'Text-Grab'
PROJECT_PATH: 'Text-Grab/Text-Grab.csproj'
TEST_PATH: 'Tests/Tests.csproj'
TEST_CORE_PATH: 'Tests.Core/Tests.Core.csproj'
TEST_CORE_WINDOWS_PATH: 'Tests.Core.Windows/Tests.Core.Windows.csproj'
BUILD_X64: 'bld/x64'
BUILD_X64_SC: 'bld/x64/Text-Grab-Self-Contained'
BUILD_ARM64: 'bld/arm64'
Expand All @@ -50,6 +52,14 @@ jobs:
- name: Install dependencies
run: dotnet restore ${{ env.PROJECT_PATH }}

# The pure tier runs first: it needs no display and finishes in about a second, so a
# logic regression fails the job before the slow WPF/STA suite starts.
- name: Run Core tests
run: dotnet test --project ${{ env.TEST_CORE_PATH }}

- name: Run Core.Windows tests
run: dotnet test --project ${{ env.TEST_CORE_WINDOWS_PATH }} -r win-x64

- name: Run tests
run: dotnet test --project ${{ env.TEST_PATH }} -r win-x64

Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/buildDev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ concurrency:
env:
PROJECT_PATH: "Text-Grab/Text-Grab.csproj"
TEST_PATH: "Tests/Tests.csproj"
TEST_CORE_PATH: "Tests.Core/Tests.Core.csproj"
TEST_CORE_WINDOWS_PATH: "Tests.Core.Windows/Tests.Core.Windows.csproj"
# Unlock token for the Windows AI language model (a Limited Access Feature). Absent secrets
# leave the properties empty, which just builds without on-device text AI rather than failing.
LAF_TOKEN: ${{ secrets.LAF_TOKEN }}
Expand All @@ -30,6 +32,12 @@ jobs:
run: dotnet restore ${{ env.PROJECT_PATH }}
- name: Build
run: dotnet build ${{ env.PROJECT_PATH }} -p:EnableMsixTooling=true
# The pure tier runs first: it needs no display and finishes in about a second, so a
# logic regression fails the job before the slow WPF/STA suite starts.
- name: Test Core
run: dotnet test --project ${{ env.TEST_CORE_PATH }}
- name: Test Core.Windows
run: dotnet test --project ${{ env.TEST_CORE_WINDOWS_PATH }} -r win-x64
- name: Test
run: dotnet test --project ${{ env.TEST_PATH }} -r win-x64

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
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;
namespace Text_Grab.Tests.Core.Windows;

// Headless half of the original Tests/BarcodeUtilitiesTests.cs (batch 7a). ReadTestSingleQRCode
// stayed behind as Tests/BarcodeUtilitiesImageTests.cs: it is [WpfFact]-tagged, and Xunit.StaFact
// cannot be referenced here (it pulls in WindowsBase, which TierBoundaryTests bans). This half has
// 3 methods against that one's 1, so it kept the original name.
public class BarcodeUtilitiesTests
{
[Fact]
Expand Down Expand Up @@ -47,20 +49,6 @@ public void TryToReadBarcodes_WithTwoQrCodes_ReturnsTwoResults()
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()
{
Expand All @@ -73,7 +61,7 @@ public async Task GetBitmapFromIRandomAccessStream_ReturnsBitmapIndependentOfSou
using InMemoryRandomAccessStream randomAccessStream = new();
_ = await randomAccessStream.WriteAsync(memoryStream.ToArray().AsBuffer());

Bitmap clonedBitmap = ImageMethods.GetBitmapFromIRandomAccessStream(randomAccessStream);
Bitmap clonedBitmap = BitmapUtilities.GetBitmapFromIRandomAccessStream(randomAccessStream);

Assert.Equal(8, clonedBitmap.Width);
Assert.Equal(8, clonedBitmap.Height);
Expand Down
49 changes: 49 additions & 0 deletions Tests.Core.Windows/FakeTextGrabSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System.Runtime.CompilerServices;
using Text_Grab.Interfaces;
using Text_Grab.Services;

namespace Text_Grab.Tests.Core.Windows;

/// <summary>
/// Registers a <see cref="SettingsAccess"/> resolver for this test host. Tests.Core.Windows has
/// no app assembly to supply one via a <c>[ModuleInitializer]</c> the way Tests does (see
/// SettingsAccess.Current's remarks), so any moved test whose production code path reads settings
/// - here, a handful of OcrTests methods that call into OcrUtilities.BuildTextFromOcrLines, which
/// reads ParagraphDetection/RemoveFurigana/CorrectErrors/CorrectToLatin internally - would throw
/// InvalidOperationException without one installed.
///
/// Every default below is copied from Text-Grab/Properties/Settings.settings's "(Default)"
/// profile, so a moved test that depends on a default value (e.g. RemoveFurigana=true dropping
/// furigana words) sees exactly what it saw running inside the app-hosted Tests project.
/// </summary>
internal static class TestSettingsInitializer
{
[ModuleInitializer]
internal static void Register() => SettingsAccess.SetResolver(() => new FakeTextGrabSettings());
}

/// <summary>Minimal ITextGrabSettings double seeded with Settings.settings's shipped defaults.</summary>
internal sealed class FakeTextGrabSettings : ITextGrabSettings
{
public bool CorrectErrors { get; set; } = true;
public bool CorrectToLatin { get; set; } = true;
public bool OverrideAiArchCheck { get; set; }
public bool ParagraphDetection { get; set; } = true;
public bool RemoveFurigana { get; set; } = true;
public bool TryToReadBarcodes { get; set; } = true;
public bool HdrCaptureCorrection { get; set; }
public bool HdrBorderlessGranted { get; set; }
public bool UiAutomationEnabled { get; set; }
public bool WindowsAiDescriptionEnabled { get; set; }
public bool UiAutomationFallbackToOcr { get; set; } = true;
public bool UseTesseract { get; set; }
public string TesseractPath { get; set; } = string.Empty;
public string LastUsedLang { get; set; } = string.Empty;
public int TtsSpeakWordLimit { get; set; } = 100;
public string TtsVoiceName { get; set; } = string.Empty;
public double TtsSpeakingRate { get; set; } = 1;
public string AudioTranscriptionModel { get; set; } = "BaseMultilingual";
public bool EnableFileBackedManagedSettings { get; set; }

public void Save() { }
}
36 changes: 36 additions & 0 deletions Tests.Core.Windows/FileUtilitiesTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Text_Grab.Utilities;

namespace Text_Grab.Tests.Core.Windows;

// Pure half of the original Tests/FilesIoTests.cs (batch 7a): FileUtilities.GetVisualDocumentFilter
// is Core.Windows-only and needs no app type. The rest of that file needed WPF or app-side members
// and kept the FilesIoTests name in Tests; the IoUtilities-only tests moved separately to
// Tests.Core/IoUtilitiesTests.cs.
public class FileUtilitiesTests
{
[Fact]
public void GetVisualDocumentFilter_IncludesPdfSupport()
{
string filter = FileUtilities.GetVisualDocumentFilter();

Assert.Contains("Image and PDF files|", filter);
Assert.Contains("PDF files|*.pdf", filter);
Assert.Contains("Image files|", filter);
}

// Joined FileUtilities in 7b once GrabFrameFileUtilities followed HistoryInfo to
// Core.Windows and GetOpenDocumentFilter() no longer needed the app-side
// OpenDocumentFilterUtilities split.
[Fact]
public void GetOpenDocumentFilter_IncludesVisualAndTextOptions()
{
string filter = FileUtilities.GetOpenDocumentFilter();

Assert.Contains("Supported documents|", filter);
Assert.Contains("Image and PDF files|", filter);
Assert.Contains("Spreadsheet documents|*.csv;*.tsv;*.tab", filter);
Assert.Contains("Markdown documents|*.md;*.markdown", filter);
Assert.Contains("Text documents (*.txt)|*.txt", filter);
Assert.Contains("All files (*.*)|*.*", filter);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
using System.IO;
using System.IO.Compression;
using System.Text.Json;
using System.Windows;
using Text_Grab;
using Text_Grab.Models;
using Text_Grab.Utilities;

namespace Tests;
namespace Text_Grab.Tests.Core.Windows;

// Moved wholesale in 7b: GrabFrameFileUtilities followed HistoryInfo to Core.Windows once the
// HistoryInfo blocker cleared (a8591aa), and every assertion here is against that headless pair
// (GrabFrameFileUtilities, HistoryInfo/WordBorderInfo) with no WPF type in sight - the file had
// an unused `using System.Windows;` from before that move, since dropped.
public class GrabFrameFileTests
{
[Fact]
Expand All @@ -21,13 +24,13 @@ public async Task SaveAndLoad_RoundTripsMetadataWordBordersAndImage()
new()
{
Word = "Hello",
BorderRect = new Rect(1, 2, 30, 12),
BorderRect = new RectangleF(1, 2, 30, 12),
LineNumber = 0,
},
new()
{
Word = "World",
BorderRect = new Rect(35, 2, 32, 12),
BorderRect = new RectangleF(35, 2, 32, 12),
LineNumber = 0,
},
];
Expand All @@ -40,7 +43,7 @@ public async Task SaveAndLoad_RoundTripsMetadataWordBordersAndImage()
IsTable = true,
LanguageTag = "en-US",
LanguageKind = LanguageKind.Global,
PositionRect = new Rect(100, 120, 400, 300),
PositionRect = new RectangleF(100, 120, 400, 300),
WordBorderInfoJson = JsonSerializer.Serialize(wordBorders),
ImageContent = new Bitmap(64, 48),
};
Expand All @@ -60,7 +63,7 @@ public async Task SaveAndLoad_RoundTripsMetadataWordBordersAndImage()
Assert.True(loaded.IsTable);
Assert.Equal("en-US", loaded.LanguageTag);
Assert.Equal(LanguageKind.Global, loaded.LanguageKind);
Assert.Equal(new Rect(100, 120, 400, 300), loaded.PositionRect);
Assert.Equal(new RectangleF(100, 120, 400, 300), loaded.PositionRect);

Assert.NotNull(loaded.ImageContent);
Assert.Equal(64, loaded.ImageContent!.Width);
Expand Down Expand Up @@ -91,7 +94,7 @@ public async Task SaveGrabFrameFileAsync_DoesNotMutateSuppliedInfo()

string originalWordBordersJson = JsonSerializer.Serialize(new List<WordBorderInfo>
{
new() { Word = "Hello", BorderRect = new Rect(1, 2, 30, 12), LineNumber = 0 },
new() { Word = "Hello", BorderRect = new RectangleF(1, 2, 30, 12), LineNumber = 0 },
});
Bitmap originalImage = new(64, 48);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Drawing;
using Text_Grab.Utilities.Hdr;

namespace Tests;
namespace Text_Grab.Tests.Core.Windows;

public class HdrScreenCaptureTests
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Drawing;
using Text_Grab.Utilities;

namespace Tests;
namespace Text_Grab.Tests.Core.Windows;

public class ImageChangeDetectorTests
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using Text_Grab;
using Text_Grab.Models;

namespace Tests;
namespace Text_Grab.Tests.Core.Windows;

public class LanguageTests
{
Expand Down
Loading