Skip to content
Draft
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
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## V1 / RD3 - modern voice overlay candidate

- Replaced the old rectangular listening/transcribing presentation with a modern oval/pill activity surface at build time.
- Added smooth rounded waveform bars with distinct listening and local-transcription motion.
- Listening uses the blue SignalFlow treatment; transcription uses a graphite/blue-grey treatment.
- Preserved the visible state words: Listening, Transcribing locally, Pasted, Copied to clipboard, and No speech detected.
- Preserved F8 push-to-talk, microphone capture, local whisper.cpp transcription, clipboard recovery, guarded paste, focus protection, and the existing timing path.
- Kept this public candidate sanitized; no owner-specific ReoFlow identity or private control material is introduced.
- The V1/RD2 public source is preserved on the rollback branch `rollback/signalflow-mini-v1-rd2-public-2026-09-10`.

## Public cleanup release

- Prepared SignalFlow Mini as a free Apache 2.0 giveaway.
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
SIGNALFLOW-MINI-V1-RD2-PUBLIC
SIGNALFLOW-MINI-V1-RD3-PUBLIC
20 changes: 12 additions & 8 deletions build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,31 @@ New-Item -ItemType Directory -Force $out | Out-Null

if (!(Test-Path -LiteralPath $src)) {
if (!(Test-Path -LiteralPath $srcGz)) { throw 'Neither src\Program.cs nor src\Program.cs.gz exists.' }
$input = [System.IO.File]::OpenRead($srcGz)
$inputStream = [System.IO.File]::OpenRead($srcGz)
try {
$gzip = New-Object System.IO.Compression.GzipStream($input, [System.IO.Compression.CompressionMode]::Decompress)
$gzipStream = New-Object System.IO.Compression.GzipStream($inputStream, [System.IO.Compression.CompressionMode]::Decompress)
try {
$output = [System.IO.File]::Create($src)
try { $gzip.CopyTo($output) } finally { $output.Dispose() }
} finally { $gzip.Dispose() }
} finally { $input.Dispose() }
$outputStream = [System.IO.File]::Create($src)
try { $gzipStream.CopyTo($outputStream) } finally { $outputStream.Dispose() }
} finally { $gzipStream.Dispose() }
} finally { $inputStream.Dispose() }
}

$overlayApplicator = Join-Path $root 'tools\Apply-ModernVoiceOverlay.ps1'
if (!(Test-Path -LiteralPath $overlayApplicator)) { throw 'Modern voice overlay applicator is missing.' }
& $overlayApplicator -SourcePath $src -ProductName 'SignalFlow Mini' -Marker 'SIGNALFLOW-MINI-MODERN-VOICE-OVERLAY-V1-RD3'

$exe = Join-Path $out 'SignalFlow-Mini.exe'
if (Test-Path $exe) { Remove-Item -Force $exe }
$refs = @('System.dll','System.Core.dll','System.Drawing.dll','System.Windows.Forms.dll')
$addType = Get-Command Add-Type -ErrorAction Stop
$addTypeCommand = Get-Command Add-Type -ErrorAction Stop
$compile = @{
Path = $src
ReferencedAssemblies = $refs
OutputAssembly = $exe
OutputType = 'WindowsApplication'
}
if ($addType.Parameters.ContainsKey('CompilerOptions')) { $compile['CompilerOptions'] = '/optimize+' }
if ($addTypeCommand.Parameters.ContainsKey('CompilerOptions')) { $compile['CompilerOptions'] = '/optimize+' }
Add-Type @compile
if (!(Test-Path $exe)) { throw 'SignalFlow-Mini.exe was not produced.' }
$hash = (Get-FileHash $exe -Algorithm SHA256).Hash.ToLowerInvariant()
Expand Down
108 changes: 108 additions & 0 deletions tools/Apply-ModernVoiceOverlay.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
param(
[Parameter(Mandatory = $true)]
[string]$SourcePath,

[Parameter(Mandatory = $true)]
[string]$ProductName,

[string]$Marker = 'SIGNALPROOF-MODERN-VOICE-OVERLAY-RD3'
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

function Stop-OverlayPatch {
param([string]$Message)
throw ('Modern voice overlay patch stopped: ' + $Message)
}

if (-not (Test-Path -LiteralPath $SourcePath -PathType Leaf)) {
Stop-OverlayPatch ('source file not found: ' + $SourcePath)
}
if ([string]::IsNullOrWhiteSpace($ProductName)) {
Stop-OverlayPatch 'product name is empty'
}

$templatePath = Join-Path $PSScriptRoot 'ModernVoiceOverlay.cs.txt'
if (-not (Test-Path -LiteralPath $templatePath -PathType Leaf)) {
Stop-OverlayPatch ('overlay template not found: ' + $templatePath)
}

$sourceFullPath = [System.IO.Path]::GetFullPath($SourcePath)
$sourceText = [System.IO.File]::ReadAllText($sourceFullPath)
if ([string]::IsNullOrWhiteSpace($sourceText)) {
Stop-OverlayPatch 'source file is empty'
}

$protectedMarkers = @(
'VK_F8 = 0x77',
'WH_KEYBOARD_LL',
'MemoryStream _pcm',
'whisper-cli.exe',
'Clipboard.SetText(text)',
'_overlay.ShowListening(_targetWindow)',
'_overlay.ShowProcessing(_targetWindow)'
)
foreach ($protectedMarker in $protectedMarkers) {
if ($sourceText.IndexOf($protectedMarker, [System.StringComparison]::Ordinal) -lt 0) {
Stop-OverlayPatch ('protected behavior marker missing before patch: ' + $protectedMarker)
}
}

if ($sourceText.IndexOf($Marker, [System.StringComparison]::Ordinal) -ge 0) {
Write-Host ('Modern voice overlay already present: ' + $Marker)
return
}

$classAnchor = 'internal sealed class AudioOverlayForm : Form'
if ($sourceText.IndexOf($classAnchor, [System.StringComparison]::Ordinal) -lt 0) {
Stop-OverlayPatch 'AudioOverlayForm class was not found'
}

$templateText = [System.IO.File]::ReadAllText($templatePath)
if ($templateText.IndexOf('__PRODUCT_NAME__', [System.StringComparison]::Ordinal) -lt 0 -or
$templateText.IndexOf('__MARKER__', [System.StringComparison]::Ordinal) -lt 0) {
Stop-OverlayPatch 'overlay template placeholders are incomplete'
}

$replacement = $templateText.Replace('__PRODUCT_NAME__', $ProductName).Replace('__MARKER__', $Marker)
$pattern = '(?s) internal sealed class AudioOverlayForm : Form\s*\{.*?(?= internal sealed class [A-Za-z0-9_]+HostForm : Form)'
$overlayRegex = New-Object System.Text.RegularExpressions.Regex($pattern)
$overlayMatches = $overlayRegex.Matches($sourceText)
if ($overlayMatches.Count -ne 1) {
Stop-OverlayPatch ('expected exactly one AudioOverlayForm block; found ' + $overlayMatches.Count)
}

$patchedText = $overlayRegex.Replace(
$sourceText,
[System.Text.RegularExpressions.MatchEvaluator]{ param($match) $replacement + [Environment]::NewLine },
1
)

if ([string]::Equals($patchedText, $sourceText, [System.StringComparison]::Ordinal)) {
Stop-OverlayPatch 'patch produced no source change'
}
if ($patchedText.IndexOf($Marker, [System.StringComparison]::Ordinal) -lt 0) {
Stop-OverlayPatch 'modern overlay marker missing after patch'
}
foreach ($protectedMarker in $protectedMarkers) {
if ($patchedText.IndexOf($protectedMarker, [System.StringComparison]::Ordinal) -lt 0) {
Stop-OverlayPatch ('protected behavior marker was lost by patch: ' + $protectedMarker)
}
}
foreach ($requiredVisual in @(
'CreatePillPath',
'DrawRoundBar',
'Transcribing locally',
'LineCap.Round',
'barCount = 11'
)) {
if ($patchedText.IndexOf($requiredVisual, [System.StringComparison]::Ordinal) -lt 0) {
Stop-OverlayPatch ('modern overlay visual marker missing: ' + $requiredVisual)
}
}

$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
[System.IO.File]::WriteAllText($sourceFullPath, $patchedText, $utf8NoBom)
Write-Host ('Applied modern oval voice overlay: ' + $Marker)
Write-Host ('Product label: ' + $ProductName)
Loading