Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*.xaml text eol=crlf
*.xml text eol=crlf
*.ps1 text eol=crlf
*.nsi text eol=crlf
*.md text eol=lf
*.json text eol=lf
*.yml text eol=lf
Expand Down
36 changes: 36 additions & 0 deletions .github/releases/v0.3.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# TextRecast v0.3.0

TextRecast 0.3.0 introduces a guided Windows installer and an explicit review-before-replacement workflow.

## Highlights

- Installs per user through one self-contained Windows setup executable; .NET 10 does not need to be installed separately.
- Presents the Apache-2.0 license, privacy notice, destination, and four compatible local-model choices before installation.
- Downloads only the model selected by the user, with resumable transfer and exact size and SHA-256 verification.
- Keeps source text unchanged while the user generates, reviews, edits, copies, or regenerates a revision.
- Revalidates the captured source and replaces it only after **Replace selection** is chosen.
- Closes the editor after a clean replacement; failures and clipboard-restoration warnings remain visible with the revision preserved.
- Uses a compact Windows-controlled light/dark interface for setup, the floating launcher, rewrite editor, and information dialogs.
- Lets the floating launcher toggle the editor open and closed without recapturing an already open selection.
- Offers default-checked Launch, Desktop shortcut, and Start at sign-in choices on the completion page.
- Removes application files and owned shortcuts during uninstall while preserving downloaded models and setup state for reinstall.

## Included model choices

| Profile | Model | Download |
| --- | --- | ---: |
| Fast/default | Qwen 2.5 1.5B Q4_K_M | 1.04 GiB |
| Balanced | Qwen 3.5 2B Q5_K_M | 1.34 GiB |
| Best quality | Qwen 3.5 4B Q5_K_M | 2.93 GiB |
| Alternative | Granite 4.1 3B Q5_K_M | 2.27 GiB |

All choices currently target English rewriting. Local models can change meaning or details, so review every revision before replacement.

## Install

1. Download `TextRecast-v0.3.0-win-x64-setup.exe` and its `.exe.sha256` file.
2. Optionally verify the SHA-256 checksum.
3. Close any running TextRecast instance, run Setup, and choose one compatible model.
4. Keep or clear the completion-page Desktop and Start at sign-in options, then choose **Finish**.

The installer contains no GGUF model. An internet connection is required when the selected model is not already installed. The current Microsoft Visual C++ x64 Redistributable may also be required.
95 changes: 58 additions & 37 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ permissions:
jobs:
build:
runs-on: windows-latest
timeout-minutes: 20
timeout-minutes: 40

steps:
- name: Check out repository
Expand Down Expand Up @@ -51,7 +51,7 @@ jobs:
}

"version=$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
"package_name=TextRecast-v$version-win-x64" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
"setup_name=TextRecast-v$version-win-x64-setup" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append

- name: Restore dependencies
run: dotnet restore TextRecast.slnx
Expand All @@ -65,6 +65,26 @@ jobs:
- name: Build release
run: dotnet build TextRecast.slnx -c Release --no-restore

- name: Verify setup dependency boundary
shell: pwsh
run: |
[xml] $setupProject = Get-Content -Raw src/TextRecast.Setup/TextRecast.Setup.csproj
[xml] $deploymentProject = Get-Content -Raw src/TextRecast.Deployment/TextRecast.Deployment.csproj

$setupReferences = @($setupProject.SelectNodes("//ProjectReference") | ForEach-Object { $_.Include })
if ($setupReferences.Count -ne 1 -or
$setupReferences[0] -ne "..\TextRecast.Deployment\TextRecast.Deployment.csproj") {
throw "The setup host must reference only TextRecast.Deployment."
}

if (@($deploymentProject.SelectNodes("//PackageReference")).Count -ne 0) {
throw "TextRecast.Deployment must remain package-free."
}

if (@($setupProject.SelectNodes("//PackageReference")).Count -ne 0) {
throw "The setup host must remain package-free."
}

- name: Publish self-contained application
run: >-
dotnet publish src/TextRecast.App/TextRecast.App.csproj
Expand All @@ -75,47 +95,48 @@ jobs:
-p:Version=${{ steps.version.outputs.version }}
-o artifacts/publish/TextRecast-win-x64

- name: Create versioned release package
- name: Prepare controlled upgrade fixture
shell: pwsh
env:
PACKAGE_NAME: ${{ steps.version.outputs.package_name }}
run: |
$publishDirectory = (Resolve-Path "artifacts/publish/TextRecast-win-x64").Path
$releaseDirectory = Join-Path $PWD "artifacts/release"
New-Item -ItemType Directory -Path $releaseDirectory -Force | Out-Null

$requiredFiles = @(
"TextRecast.exe",
"LICENSE",
"NOTICE",
"THIRD-PARTY-NOTICES.md",
"DOTNET-LICENSE.txt",
"DOTNET-THIRD-PARTY-NOTICES.txt",
"WPF-LICENSE.txt"
)
foreach ($file in $requiredFiles) {
if (-not (Test-Path -LiteralPath (Join-Path $publishDirectory $file) -PathType Leaf)) {
throw "Published application is missing required file: $file"
}
}
$source = (Resolve-Path "artifacts/publish/TextRecast-win-x64").Path
$fixture = Join-Path $PWD "artifacts/smoke/previous-publish"
New-Item -ItemType Directory -Path $fixture -Force | Out-Null
Copy-Item -Path (Join-Path $source "*") -Destination $fixture -Recurse -Force
Set-Content -LiteralPath (Join-Path $fixture "obsolete-v0.1-fixture.txt") -Value "upgrade fixture" -NoNewline

$modelFiles = @(Get-ChildItem -Path $publishDirectory -Filter "*.gguf" -File -Recurse)
if ($modelFiles.Count -ne 0) {
throw "Release packages must not contain GGUF model files."
}

$archiveFileName = "$env:PACKAGE_NAME.zip"
$archivePath = Join-Path $releaseDirectory $archiveFileName
Compress-Archive -Path (Join-Path $publishDirectory "*") -DestinationPath $archivePath -Force
- name: Build controlled upgrade installer fixture
shell: pwsh
run: >-
./tools/Build-WindowsInstaller.ps1
-Version 0.1.0
-PublishDirectory artifacts/smoke/previous-publish
-OutputDirectory artifacts/smoke/previous
-NoRestore

$checksum = (Get-FileHash -LiteralPath $archivePath -Algorithm SHA256).Hash.ToLowerInvariant()
Set-Content -LiteralPath "$archivePath.sha256" -Value "$checksum $archiveFileName" -NoNewline
- name: Build Windows setup
shell: pwsh
run: >-
./tools/Build-WindowsInstaller.ps1
-Version ${{ steps.version.outputs.version }}
-PublishDirectory artifacts/publish/TextRecast-win-x64
-OutputDirectory artifacts/release
-NoRestore

- name: Upload Windows release package
- name: Smoke test install, launch, payload replacement, uninstall, and reinstall
shell: pwsh
run: >-
./tools/Test-WindowsInstaller.ps1
-SetupPath artifacts/release/${{ steps.version.outputs.setup_name }}.exe
-ExpectedVersion ${{ steps.version.outputs.version }}
-PreviousSetupPath artifacts/smoke/previous/TextRecast-v0.1.0-win-x64-setup.exe
-PreviousVersion 0.1.0
-UpgradeFixtureFileName obsolete-v0.1-fixture.txt

- name: Upload Windows setup
uses: actions/upload-artifact@v7
with:
name: ${{ steps.version.outputs.package_name }}
name: ${{ steps.version.outputs.setup_name }}
path: |
artifacts/release/${{ steps.version.outputs.package_name }}.zip
artifacts/release/${{ steps.version.outputs.package_name }}.zip.sha256
artifacts/release/${{ steps.version.outputs.setup_name }}.exe
artifacts/release/${{ steps.version.outputs.setup_name }}.exe.sha256
if-no-files-found: error
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ Desktop.ini
# Large local model binaries are downloaded separately.
Models/*.gguf
Models/*.partial
Models/*.partial.metadata.json

# Local documentation drafts and planning notes
docs/
/design-qa.md
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>0.2.0</Version>
<Version>0.3.0</Version>
<Copyright>Copyright 2026 snss10</Copyright>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
Expand Down
10 changes: 5 additions & 5 deletions Models/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Supported local models

TextRecast runs one cataloged GGUF model at a time. During setup, the user reviews the available choices and confirms the exact model before any download starts. Release archives do not contain model binaries.
TextRecast runs one cataloged GGUF model at a time. During setup, the user reviews the available choices and confirms the exact model before any download starts. Normal release payloads do not contain model binaries.

All current profiles are for English rewriting. The three newer choices are experimental, and every replacement should be reviewed in the source application.

Expand Down Expand Up @@ -39,10 +39,10 @@ An interrupted download uses two temporary files beside its final destination:

TextRecast uses HTTP Range and If-Range to resume compatible partial downloads. It restarts when the server ignores the range, returns inconsistent metadata, or exposes a changed validator. The final file is installed only after its exact size and SHA-256 pass verification.

## Offline or portable builds
## Offline builds

Place only exact cataloged GGUF files in this directory before publishing. The App project links `Models/*.gguf` into its output, and setup marks matching files as installed. Do not rename a model file or substitute another quantization without adding and testing a separate catalog profile.
Place only exact cataloged GGUF files in this directory before an intentional offline publish. Model inclusion is disabled by default so normal builds and installers cannot accidentally carry a local model. Pass `-p:IncludeBundledModels=true` when publishing an offline installer input; setup then recognizes an exact catalog match as installed. Do not rename a model file or substitute another quantization without adding and testing a separate catalog profile.

The normal GitHub Actions release intentionally fails if a GGUF file enters the self-contained ZIP. This keeps the public application download small and leaves the model choice with the user.
The normal GitHub Actions build intentionally fails if a GGUF or partial-download file enters either the self-contained application payload or the Windows setup input. This keeps the public installer model-free and leaves the model choice with the user.

Catalog metadata is defined in [`SlmModelCatalog.cs`](../src/TextRecast.Infrastructure/SLM/SlmModelCatalog.cs).
Catalog metadata is defined in [`SlmModelCatalog.cs`](../src/TextRecast.Deployment/SLM/SlmModelCatalog.cs).
40 changes: 40 additions & 0 deletions PRIVACY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# TextRecast Privacy Notice

Effective: August 8, 2026

TextRecast is a local Windows writing assistant. This notice describes the data handled by the application and its installer.

## Text processing

Selected text, generated text, prompts, and model output are processed on the current computer by the local language model. TextRecast does not send that content to a hosted text-processing service, and it does not save a writing history.

TextRecast does not currently include an account system, advertising, analytics, telemetry, or automatic crash reporting.

## Clipboard and Windows access

TextRecast uses Windows UI Automation to read and replace a selection when the source application supports it. It can temporarily use the Windows clipboard as a fallback. The application attempts to restore the previous clipboard contents and reports when restoration cannot be verified.

Windows clipboard history, clipboard synchronization, accessibility software, or third-party clipboard managers operate outside TextRecast and may retain content placed on the clipboard. Disable those features before handling sensitive text if that behavior is not acceptable.

## Network access

TextRecast connects to the pinned model source only when a selected model is not already installed and the user confirms its download. The request identifies the model file and necessarily exposes ordinary connection data, such as the IP address and HTTP request metadata, to the model host. Selected text and generated text are not included in that request.

After a verified model is installed, normal text formatting is local and does not require a text-processing service.

## Data stored on the computer

TextRecast can store the following data for the current Windows user under `%LOCALAPPDATA%\TextRecast`:

- the selected local model and its verified model metadata;
- model-selection and setup state;
- a partial model download so an interrupted download can resume; and
- the downloaded model file.

The installer stores application files under the selected per-user installation directory and adds normal Windows uninstall information and a Start Menu shortcut. On the completion page, it offers a desktop shortcut and a shortcut in the current user's Windows Startup folder; both choices are enabled by default and can be cleared before choosing Finish. The Startup shortcut launches the floating TextRecast button when that Windows user signs in. Uninstall removes these application shortcuts but preserves the user model and setup data so reinstalling does not require another model download. That data can be removed manually by deleting `%LOCALAPPDATA%\TextRecast` after TextRecast is closed.

## Questions and changes

Privacy-related questions can be raised through the repository's issue tracker. Do not include private text, clipboard contents, credentials, or other sensitive data in an issue.

Material changes to TextRecast's data handling will be reflected in this notice and its effective date.
Loading
Loading