Compare commits

...
Author SHA1 Message Date
SuperDooper 2c1c489af4 Retire GitHub workflow after successful Gitea release 2026-09-10 09:09:00 +02:00
SuperDooper f7e2d92c4d Retire GitHub workflow after successful Gitea release 2026-09-10 09:08:58 +02:00
SuperDooper 6d9a0d7516 Retire GitHub workflow after successful Gitea release 2026-09-10 09:08:56 +02:00
SuperDooper 4cccab1d17 Retire GitHub workflow after successful Gitea release 2026-09-10 09:08:54 +02:00
SuperDooper 19379169b9 Update Windows release notes for beta.63 2026-05-11 22:26:56 +02:00
SuperDooper 8a104ad8d0 Login: reload background browser immediately after sign-in (no 3s delay) 2026-05-11 22:23:16 +02:00
SuperDooper 59c911dff4 WebViewFetchWindow: add ReloadAsync for post-login re-navigation 2026-05-11 22:23:14 +02:00
SuperDooper 127fc82b2a Update Windows release notes for beta.62 2026-05-11 22:21:35 +02:00
SuperDooper 9bde5b74a0 Login: relax signedIn check to any claude.ai cookie 2026-05-11 22:13:59 +02:00
SuperDooper bdede36b5f Login: always enable Done button; fix can't-move-past-login 2026-05-11 22:13:58 +02:00
SuperDooper d2cb75a8dc Installer: bundle all DLLs (remove single-file publish) 2026-05-11 22:05:41 +02:00
SuperDooper 01188b3009 Build: remove PublishSingleFile from Windows stable workflow 2026-05-11 22:05:40 +02:00
SuperDooper 54b89b8e55 Build: remove PublishSingleFile from Windows beta workflow 2026-05-11 22:05:39 +02:00
SuperDooper 24dbf013e7 Fix clipboard crash: use SetDataObject(false) to skip Flush() 2026-05-11 21:44:21 +02:00
SuperDooper 5a799899ef Installer: use userdesktop instead of commondesktop (no admin needed) 2026-05-11 21:41:14 +02:00
SuperDooper f318713ba5 Fix installer OutputDir: use . relative to SourceDir 2026-05-11 21:34:23 +02:00
SuperDooper b9d2c8a5f3 CI: build Inno Setup installer instead of zip 2026-05-11 21:27:59 +02:00
SuperDooper 8883000098 CI: build Inno Setup installer instead of zip 2026-05-11 21:27:57 +02:00
SuperDooper a8acf734ed Updater: use Inno Setup installer for auto-update 2026-05-11 21:27:46 +02:00
SuperDooper c432355be1 Add Inno Setup installer script 2026-05-11 21:27:36 +02:00
SuperDooper 9d3a06bf7a Diagnostics: retry clipboard on COMException to fix crash 2026-05-11 21:20:04 +02:00
SuperDooper 6cc4c1dc2f Diagnostics: update privacy note 2026-05-11 21:07:53 +02:00
SuperDooper e2fff371ee Diagnostics: show (none) for empty last error in copy 2026-05-11 21:06:53 +02:00
12 changed files with 89 additions and 482 deletions
-79
View File
@@ -1,79 +0,0 @@
name: Release Beta
on:
push:
tags:
- 'v*-beta*'
jobs:
release-beta:
runs-on: macos-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Get version from tag
id: version
run: echo "version=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT
- name: Build
run: |
xcodebuild \
-project ClaudeChecker.xcodeproj \
-scheme ClaudeChecker \
-configuration Release \
-derivedDataPath build \
clean build
- name: Zip app
run: |
ditto -c -k --keepParent \
build/Build/Products/Release/ClaudeChecker.app \
ClaudeChecker.zip
- name: Create GitHub pre-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "$GITHUB_REF_NAME" ClaudeChecker.zip \
--title "$GITHUB_REF_NAME" \
--prerelease \
--notes "$(cat RELEASE_NOTES.md 2>/dev/null || echo 'Beta release ${{ steps.version.outputs.version }}')"
- name: Update version-beta.json
run: |
notes=$(cat RELEASE_NOTES.md 2>/dev/null || echo "Beta release ${{ steps.version.outputs.version }}")
jq -n \
--arg version "${{ steps.version.outputs.version }}" \
--arg url "https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/ClaudeChecker.zip" \
--arg notes "$notes" \
'{version: $version, url: $url, notes: $notes}' > version-beta.json
- name: Update beta badge in README
run: |
shields_version=$(echo "${{ steps.version.outputs.version }}" | sed 's/-/--/g')
tag="${{ github.ref_name }}"
export BADGE="[![Beta](https://img.shields.io/badge/beta-${shields_version}-orange?style=flat)](https://github.com/${{ github.repository }}/releases/tag/${tag}) <!-- BETA_BADGE -->"
python3 -c "
import os
badge = os.environ['BADGE']
lines = open('README.md').readlines()
out = []
for line in lines:
out.append(badge + chr(10) if '<!-- BETA_BADGE -->' in line else line)
open('README.md', 'w').writelines(out)
"
- name: Commit version-beta.json and README
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add version-beta.json README.md
git commit -m "Beta release ${{ github.ref_name }}"
git pull --rebase origin main
git push origin HEAD:main
-102
View File
@@ -1,102 +0,0 @@
name: Release Windows Beta
on:
push:
tags:
- 'win-v*-beta*'
jobs:
release-windows-beta:
runs-on: windows-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Get version from tag
id: version
shell: bash
run: echo "version=${GITHUB_REF_NAME#win-v}" >> $GITHUB_OUTPUT
- name: Publish
run: |
dotnet publish windows/ClaudeCheckerWindows.csproj `
-c Release -r win-x64 `
--self-contained true `
-p:PublishSingleFile=true `
-p:Version=${{ steps.version.outputs.version }} `
-o publish
- name: Zip
shell: pwsh
run: |
Copy-Item -Recurse windows/Assets publish/Assets
Compress-Archive -Path publish/* -DestinationPath ClaudeChecker-Windows.zip
- name: Create GitHub pre-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
notes=$(cat windows/RELEASE_NOTES.md 2>/dev/null || echo "Windows beta ${{ steps.version.outputs.version }}")
gh release create "$GITHUB_REF_NAME" ClaudeChecker-Windows.zip \
--title "$GITHUB_REF_NAME" \
--prerelease \
--notes "$notes"
- name: Update version-windows-beta.json on main
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
shell: bash
run: |
notes=$(cat windows/RELEASE_NOTES.md 2>/dev/null || echo "Windows beta ${{ steps.version.outputs.version }}")
content=$(jq -n \
--arg version "${{ steps.version.outputs.version }}" \
--arg url "https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/ClaudeChecker-Windows.zip" \
--arg notes "$notes" \
'{version: $version, url: $url, notes: $notes}' | base64 -w0)
sha=$(gh api "repos/${{ github.repository }}/contents/version-windows-beta.json?ref=main" --jq .sha 2>/dev/null || echo "")
if [ -n "$sha" ]; then
gh api --method PUT "repos/${{ github.repository }}/contents/version-windows-beta.json" \
--field "message=Windows beta release ${{ github.ref_name }}" \
--field "content=$content" \
--field "branch=main" \
--field "sha=$sha"
else
gh api --method PUT "repos/${{ github.repository }}/contents/version-windows-beta.json" \
--field "message=Windows beta release ${{ github.ref_name }}" \
--field "content=$content" \
--field "branch=main"
fi
- name: Update Windows beta badge in README on main
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
shell: bash
run: |
version="${{ steps.version.outputs.version }}"
tag="${{ github.ref_name }}"
shields_version=$(echo "$version" | sed 's/-/--/g')
export WIN_BADGE="[![Win Beta](https://img.shields.io/badge/Win_Beta-${shields_version}-blue?style=flat)](https://github.com/${{ github.repository }}/releases/tag/${tag}) <!-- WIN_BETA_BADGE -->"
sha=$(gh api "repos/${{ github.repository }}/contents/README.md?ref=main" --jq .sha)
content=$(gh api "repos/${{ github.repository }}/contents/README.md?ref=main" --jq .content | python3 -c "
import sys, base64, os
raw = sys.stdin.read().strip()
text = base64.b64decode(raw).decode()
badge = os.environ['WIN_BADGE']
lines = text.splitlines(keepends=True)
out = [badge + chr(10) if '<!-- WIN_BETA_BADGE -->' in line else line for line in lines]
print(base64.b64encode(''.join(out).encode()).decode(), end='')
")
gh api --method PUT "repos/${{ github.repository }}/contents/README.md" \
--field "message=Windows beta badge $tag" \
--field "content=$content" \
--field "branch=main" \
--field "sha=$sha"
-114
View File
@@ -1,114 +0,0 @@
name: Release Windows
on:
push:
tags:
- 'win-v[0-9]*'
- '!win-v*-*'
jobs:
release-windows:
runs-on: windows-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Get version from tag
id: version
shell: bash
run: echo "version=${GITHUB_REF_NAME#win-v}" >> $GITHUB_OUTPUT
- name: Publish
run: |
dotnet publish windows/ClaudeCheckerWindows.csproj `
-c Release -r win-x64 `
--self-contained true `
-p:PublishSingleFile=true `
-p:Version=${{ steps.version.outputs.version }} `
-o publish
- name: Zip
shell: pwsh
run: |
Copy-Item -Recurse windows/Assets publish/Assets
Compress-Archive -Path publish/* -DestinationPath ClaudeChecker-Windows.zip
- name: Create GitHub release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
notes=$(cat windows/RELEASE_NOTES.md 2>/dev/null || echo "Windows release ${{ steps.version.outputs.version }}")
gh release create "$GITHUB_REF_NAME" ClaudeChecker-Windows.zip \
--title "$GITHUB_REF_NAME" \
--notes "$notes"
- name: Update version-windows.json on main
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
shell: bash
run: |
notes=$(cat windows/RELEASE_NOTES.md 2>/dev/null || echo "Windows release ${{ steps.version.outputs.version }}")
content=$(jq -n \
--arg version "${{ steps.version.outputs.version }}" \
--arg url "https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/ClaudeChecker-Windows.zip" \
--arg notes "$notes" \
'{version: $version, url: $url, notes: $notes}' | base64 -w0)
sha=$(gh api "repos/${{ github.repository }}/contents/version-windows.json?ref=main" --jq .sha 2>/dev/null || echo "")
if [ -n "$sha" ]; then
gh api --method PUT "repos/${{ github.repository }}/contents/version-windows.json" \
--field "message=Windows release ${{ github.ref_name }}" \
--field "content=$content" \
--field "branch=main" \
--field "sha=$sha"
else
gh api --method PUT "repos/${{ github.repository }}/contents/version-windows.json" \
--field "message=Windows release ${{ github.ref_name }}" \
--field "content=$content" \
--field "branch=main"
fi
- name: Update Windows stable badge in README on main
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
shell: bash
run: |
version="${{ steps.version.outputs.version }}"
tag="${{ github.ref_name }}"
shields_version=$(echo "$version" | sed 's/-/--/g')
export WIN_STABLE_BADGE="[![Win Stable](https://img.shields.io/badge/Win_Stable-${shields_version}-blue?style=flat)](https://github.com/${{ github.repository }}/releases/tag/${tag}) <!-- WIN_STABLE_BADGE -->"
sha=$(gh api "repos/${{ github.repository }}/contents/README.md?ref=main" --jq .sha)
content=$(gh api "repos/${{ github.repository }}/contents/README.md?ref=main" --jq .content | python3 -c "
import sys, base64, os
raw = sys.stdin.read().strip()
text = base64.b64decode(raw).decode()
stable_badge = os.environ['WIN_STABLE_BADGE']
lines = text.splitlines(keepends=True)
out = []
for line in lines:
if '<!-- WIN_STABLE_BADGE -->' in line:
out.append(stable_badge + chr(10))
rest = line.replace(' <!-- WIN_STABLE_BADGE -->', '').replace('<!-- WIN_STABLE_BADGE -->', '')
if rest.strip() and '<!-- WIN_BETA_BADGE -->' in rest:
out.append('<!-- WIN_BETA_BADGE -->' + chr(10))
elif rest.strip():
out.append(rest)
elif '<!-- WIN_BETA_BADGE -->' in line:
out.append('<!-- WIN_BETA_BADGE -->' + chr(10))
else:
out.append(line)
print(base64.b64encode(''.join(out).encode()).decode(), end='')
")
gh api --method PUT "repos/${{ github.repository }}/contents/README.md" \
--field "message=Windows stable badge $tag" \
--field "content=$content" \
--field "branch=main" \
--field "sha=$sha"
-111
View File
@@ -1,111 +0,0 @@
name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
if: ${{ !contains(github.ref_name, '-') }}
runs-on: macos-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Get version from tag
id: version
run: echo "version=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT
- name: Build
run: |
xcodebuild \
-project ClaudeChecker.xcodeproj \
-scheme ClaudeChecker \
-configuration Release \
-derivedDataPath build \
clean build
- name: Zip app
run: |
ditto -c -k --keepParent \
build/Build/Products/Release/ClaudeChecker.app \
ClaudeChecker.zip
- name: Compute SHA256
id: sha
run: echo "sha256=$(shasum -a 256 ClaudeChecker.zip | awk '{print $1}')" >> $GITHUB_OUTPUT
- name: Create GitHub release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "$GITHUB_REF_NAME" ClaudeChecker.zip \
--title "$GITHUB_REF_NAME" \
--notes "$(cat RELEASE_NOTES.md 2>/dev/null || echo 'Release ${{ steps.version.outputs.version }}')"
- name: Update version.json
run: |
notes=$(cat RELEASE_NOTES.md 2>/dev/null || echo "Release ${{ steps.version.outputs.version }}")
jq -n \
--arg version "${{ steps.version.outputs.version }}" \
--arg url "https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/ClaudeChecker.zip" \
--arg notes "$notes" \
'{version: $version, url: $url, notes: $notes}' > version.json
- name: Update Homebrew cask
run: |
sed -i '' \
-e 's/version "[^"]*"/version "${{ steps.version.outputs.version }}"/' \
-e 's/sha256 "[^"]*"/sha256 "${{ steps.sha.outputs.sha256 }}"/' \
../homebrew-tap/Casks/claudechecker.rb 2>/dev/null || true
- name: Update README badge
env:
NEW_VERSION: ${{ steps.version.outputs.version }}
run: |
python3 -c "
import os, re
v = os.environ['NEW_VERSION']
content = open('README.md').read()
content = re.sub(r'version-[0-9][^-]*-orange', 'version-' + v + '-orange', content)
lines = content.splitlines(keepends=True)
out = []
for line in lines:
if '<!-- BETA_BADGE -->' in line:
continue
out.append(line)
if 'license-MIT-blue' in line:
out.append('<!-- BETA_BADGE -->' + chr(10))
open('README.md', 'w').writelines(out)
"
- name: Commit version.json and README
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add version.json README.md
git commit -m "Release ${{ github.ref_name }}"
git pull --rebase origin main
git push origin HEAD:main
- name: Update Homebrew tap
env:
TAP_TOKEN: ${{ secrets.TAP_TOKEN }}
run: |
git clone https://x-access-token:${{ secrets.TAP_TOKEN }}@github.com/${{ github.repository_owner }}/homebrew-tap /tmp/homebrew-tap
sed -i '' \
-e 's/version "[^"]*"/version "${{ steps.version.outputs.version }}"/' \
-e 's/sha256 "[^"]*"/sha256 "${{ steps.sha.outputs.sha256 }}"/' \
/tmp/homebrew-tap/Casks/claudechecker.rb
cd /tmp/homebrew-tap
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Casks/claudechecker.rb
git commit -m "Update claudechecker to ${{ github.ref_name }}"
git push
+1 -1
View File
@@ -35,7 +35,7 @@
VerticalAlignment="Center" Text="Waiting for sign-in…"/>
<Button Grid.Column="1" Content="Done — I'm signed in"
Style="{StaticResource PrimaryButton}" Click="Done_Click"
x:Name="DoneButton" IsEnabled="False"/>
x:Name="DoneButton"/>
</Grid>
</Border>
</Grid>
+2 -4
View File
@@ -33,13 +33,11 @@ public partial class LoginWindow : Window
var cookies = await Browser.CoreWebView2.CookieManager.GetCookiesAsync("https://claude.ai");
var signedIn = System.Linq.Enumerable.Any(cookies, c =>
c.Name.Equals("sessionKey", System.StringComparison.OrdinalIgnoreCase) ||
c.Name.StartsWith("sk-ant", System.StringComparison.OrdinalIgnoreCase));
c.Domain.Contains("claude.ai") || c.Domain.Contains("anthropic.com"));
await Dispatcher.InvokeAsync(() =>
{
DoneButton.IsEnabled = !uri.Contains("/login") && !uri.Contains("/signin");
StatusText.Text = signedIn
StatusText.Text = signedIn && !uri.Contains("/login") && !uri.Contains("/signin")
? "Signed in — click Done to continue."
: "Complete sign-in, then click Done.";
});
+1 -1
View File
@@ -282,7 +282,7 @@
Style="{StaticResource SmallButton}" Click="CopyDiag_Click"/>
</Grid>
</Border>
<TextBlock DockPanel.Dock="Top" Text="Email and org IDs are redacted when copied."
<TextBlock DockPanel.Dock="Top" Text="Org IDs are redacted when copied."
FontSize="10" Foreground="{DynamicResource SecondaryBrush}"
Margin="14,6,14,6"/>
<ScrollViewer VerticalScrollBarVisibility="Auto">
+19 -4
View File
@@ -495,8 +495,21 @@ public partial class PopupWindow : Window
private void CopyDiag_Click(object s, RoutedEventArgs e)
{
Clipboard.SetText(BuildDiagText());
CopyDiagButton.Content = "Copied! (IDs redacted)";
try
{
// SetDataObject(data, false) skips Flush() — avoids CLIPBRD_E_CANT_OPEN crash
var diagText = BuildDiagText();
for (int i = 0; i < 5; i++)
{
try { Clipboard.SetDataObject(diagText, false); break; }
catch (Exception) { System.Threading.Thread.Sleep(100); }
}
CopyDiagButton.Content = "Copied! (IDs redacted)";
}
catch (Exception)
{
CopyDiagButton.Content = "Copy failed";
}
var t = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1.5) };
t.Tick += (_, _) => { CopyDiagButton.Content = "Copy All"; t.Stop(); };
t.Start();
@@ -624,7 +637,7 @@ public partial class PopupWindow : Window
sb.AppendLine();
sb.AppendLine($"Last path: {VM.DiagLastPath}");
sb.AppendLine($"Last status: {VM.DiagLastStatus}");
sb.AppendLine($"Last error: {VM.DiagLastError}");
sb.AppendLine($"Last error: {(string.IsNullOrEmpty(VM.DiagLastError) ? "(none)" : VM.DiagLastError)}");
if (VM.DiagLastFetch.HasValue)
sb.AppendLine($"Last fetch: {VM.DiagLastFetch.Value:HH:mm:ss}");
sb.AppendLine();
@@ -695,7 +708,9 @@ public partial class PopupWindow : Window
_ = Task.Run(async () =>
{
await Task.Delay(3000);
// Re-navigate background browser with fresh session, then refresh immediately
await Application.Current.Dispatcher.InvokeAsync(
() => App.BackgroundBrowser.ReloadAsync()).Task.Unwrap();
await VM.RefreshAsync();
await Application.Current.Dispatcher.InvokeAsync(InitSettings);
});
+3 -21
View File
@@ -1,23 +1,5 @@
## What's new in beta.49
### Architecture
- All data (usage, plan, overage, prepaid credits) now fetched via the persistent background WebView2 — the same always-live session as the macOS app, so Cloudflare cookies never expire between refreshes
- HttpClient path kept only as a brief startup fallback before the browser is ready
- Settings file simplified: only stores session signal, burn history, and user preferences — no more cached API responses
## What's new in beta.63
### Bug fixes
- Plan label (e.g. "Pro") now updates on every refresh, not just at login
- Extra Usage Credits values now correct (were 100× too large)
- Limit and balance now update live on every refresh
- Refresh interval dropdown now shows "1 min", "2 min" etc. correctly
- ComboBox no longer flashes bright blue when clicked
- Buttons now show a visible pressed state
- Footer "Updated X ago" counter now ticks every second; shows "X min, Y sec ago" past 60 s
- Removed stray sparkline bars from the 5 h and 7 d limit cards
- App window now shown on launch
### UI improvements
- Gauge percentage larger, "used" label removed
- Each limit card now shows an "after reset" or "today HH:MM" badge
- Session Diary card redesigned to match macOS layout (stats row + sparkline, no Claude header)
- Reset date moved inline with time remaining
- After signing in, usage data now loads immediately instead of waiting 3 seconds
- Background browser re-navigates to claude.ai right after login so the live session is ready for the first refresh
+8 -45
View File
@@ -2,7 +2,6 @@ using System;
using System.Diagnostics;
using System.Reflection;
using System.IO;
using System.IO.Compression;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
@@ -136,9 +135,7 @@ public class UpdateManager : INotifyPropertyChanged
try
{
var tmpDir = Path.Combine(Path.GetTempPath(), $"CCUpdate_{Guid.NewGuid():N}");
Directory.CreateDirectory(tmpDir);
var zipPath = Path.Combine(tmpDir, "ClaudeChecker.zip");
var installerPath = Path.Combine(Path.GetTempPath(), $"ClaudeCheckerInstaller_{Guid.NewGuid():N}.exe");
using var http = new HttpClient();
var response = await http.GetAsync(DownloadUrl, HttpCompletionOption.ResponseHeadersRead);
@@ -148,7 +145,7 @@ public class UpdateManager : INotifyPropertyChanged
var received = 0L;
await using (var stream = await response.Content.ReadAsStreamAsync())
await using (var file = File.Create(zipPath))
await using (var file = File.Create(installerPath))
{
var buffer = new byte[81920];
int read;
@@ -157,48 +154,21 @@ public class UpdateManager : INotifyPropertyChanged
await file.WriteAsync(buffer.AsMemory(0, read));
received += read;
if (total > 0)
DownloadProgress = (double)received / total * 0.8;
DownloadProgress = (double)received / total;
}
}
StatusMessage = "Unpacking…";
DownloadProgress = 0.85;
var extractDir = Path.Combine(tmpDir, "extracted");
ZipFile.ExtractToDirectory(zipPath, extractDir, overwriteFiles: true);
// Find the installer exe
var newExe = FindExe(extractDir);
if (newExe == null) throw new Exception("ClaudeChecker.exe not found in update package.");
DownloadProgress = 0.95;
StatusMessage = "Installing…";
// Write a batch script to replace the exe after we exit
var currentExe = Process.GetCurrentProcess().MainModule!.FileName;
var script = $"""
@echo off
timeout /t 2 /nobreak > nul
copy /Y "{newExe}" "{currentExe}"
start "" "{currentExe}"
rmdir /S /Q "{tmpDir}"
""";
var scriptPath = Path.Combine(Path.GetTempPath(), "claudechecker_update.bat");
await File.WriteAllTextAsync(scriptPath, script);
DownloadProgress = 1.0;
StatusMessage = "Installed! Relaunching…";
StatusMessage = "Installing…";
UpdateComplete = true;
await Task.Delay(800);
await Task.Delay(500);
Process.Start(new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = $"/C \"{scriptPath}\"",
CreateNoWindow = true,
UseShellExecute = false,
FileName = installerPath,
Arguments = "/SILENT /CLOSEAPPLICATIONS",
UseShellExecute = true,
});
Application.Current.Dispatcher.Invoke(() => Application.Current.Shutdown());
@@ -211,13 +181,6 @@ public class UpdateManager : INotifyPropertyChanged
}
}
private static string? FindExe(string dir)
{
foreach (var f in Directory.EnumerateFiles(dir, "ClaudeChecker.exe", SearchOption.AllDirectories))
return f;
return null;
}
public static bool IsNewer(string version, string current)
{
static (int[] Base, int[]? Pre) Parse(string v)
+14
View File
@@ -129,6 +129,20 @@ public sealed class WebViewFetchWindow : Window
return await tcs.Task;
}
// Re-navigate to claude.ai with the current session (call after sign-in).
public async Task ReloadAsync()
{
if (_wv.CoreWebView2 == null) { await InitAsync(); return; }
_readyForScript = false;
var navDone = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
EventHandler<CoreWebView2NavigationCompletedEventArgs>? h = null;
h = (_, e) => { _wv.CoreWebView2.NavigationCompleted -= h; navDone.TrySetResult(e.IsSuccess); };
_wv.CoreWebView2.NavigationCompleted += h;
_wv.CoreWebView2.Navigate("https://claude.ai");
await Task.WhenAny(navDone.Task, Task.Delay(15000));
_readyForScript = navDone.Task.IsCompletedSuccessfully && navDone.Task.Result;
}
// ── Shared init ───────────────────────────────────────────────────────────
private int _initGuard;
+41
View File
@@ -0,0 +1,41 @@
#ifndef AppVersion
#define AppVersion "0.0.1"
#endif
[Setup]
AppName=ClaudeChecker
AppVersion={#AppVersion}
AppPublisher=superdooper86
AppPublisherURL=https://github.com/superdooper86/claudechecker
AppSupportURL=https://github.com/superdooper86/claudechecker/issues
AppUpdatesURL=https://github.com/superdooper86/claudechecker/releases
DefaultDirName={localappdata}\ClaudeChecker
DefaultGroupName=ClaudeChecker
DisableProgramGroupPage=yes
PrivilegesRequired=lowest
OutputDir=.
OutputBaseFilename=ClaudeChecker-Installer
Compression=lzma
SolidCompression=yes
WizardStyle=modern
UninstallDisplayIcon={app}\ClaudeChecker.exe
SourceDir=..
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[Files]
Source: "publish\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
[Icons]
Name: "{group}\ClaudeChecker"; Filename: "{app}\ClaudeChecker.exe"
Name: "{userdesktop}\ClaudeChecker"; Filename: "{app}\ClaudeChecker.exe"; Tasks: desktopicon
[Run]
; Silent install (auto-update): launch automatically
Filename: "{app}\ClaudeChecker.exe"; Flags: nowait; Check: WizardSilent
; Manual install: show launch checkbox on final page
Filename: "{app}\ClaudeChecker.exe"; Description: "{cm:LaunchProgram,ClaudeChecker}"; Flags: nowait postinstall skipifsilent