diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml
index ce76468..3c087b4 100644
--- a/.github/workflows/build-and-release.yml
+++ b/.github/workflows/build-and-release.yml
@@ -13,8 +13,10 @@ jobs:
runs-on: windows-latest
steps:
- - name: Check out repository
+ - name: Check out repository, with full history
uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
- name: Set up .NET
uses: actions/setup-dotnet@v4
@@ -33,22 +35,11 @@ jobs:
run: |
Add-Content $env:GITHUB_PATH "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22000.0\x64"
- - name: Install code signing certificate
- env:
- base64Pfx: ${{ secrets.PFX_B64 }}
- password: ${{ secrets.PFX_PASS }}
- run: |
- $securePassword = ConvertTo-SecureString -String $env:password -AsPlainText -Force
- $pfxBytes = [System.Convert]::FromBase64String($env:base64Pfx)
- $tempPfxPath = [System.IO.Path]::GetTempFileName()
- [System.IO.File]::WriteAllBytes($tempPfxPath, $pfxBytes)
- Import-PfxCertificate -FilePath $tempPfxPath -CertStoreLocation Cert:\CurrentUser\My -Password $securePassword
-
- - name: Run package script
- shell: pwsh
+ - name: Build x64 and ARM64
run: |
- ./package.ps1
- mkdir -Force dist
+ dotnet restore
+ dotnet msbuild TailscaleClient.csproj -t:Rebuild -p:Platform=x64 -p:Configuration=Release -p:OutDir="./dist/x64/"
+ dotnet msbuild TailscaleClient.csproj -t:Rebuild -p:Platform=arm64 -p:Configuration=Release -p:OutDir="./dist/arm64/"
- name: Upload build artifacts
uses: actions/upload-artifact@v4
@@ -58,7 +49,7 @@ jobs:
release:
name: Release Package
- runs-on: ubuntu-latest
+ runs-on: windows-latest
needs: build
if: startsWith(github.ref, 'refs/tags/v')
@@ -68,14 +59,31 @@ jobs:
with:
name: dist
- - name: "Upload to R2"
+ - name: Install packaging tools
+ run: |
+ dotnet tool install -g nbgv
+ dotnet tool install -g vpk
+
+ - name: Sync vpk to current version
+ run: |
+ vpk download http --url https://tsc.xirreal.dev/
+
+ - name: Package x64
+ run: |
+ vpk pack --packId TailscaleClient --packVersion $(nbgv get-version -v SimpleVersion) --packDir ./x64 --channel win-x64 --framework net8.0-x64-runtime
+
+ - name: Package arm64
+ run: |
+ vpk pack --packId TailscaleClient --packVersion $(nbgv get-version -v SimpleVersion) --packDir ./arm64 --channel win-arm64 --framework net8.0-arm64-runtime
+
+ - name: Upload to R2
env:
AWS_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: auto
AWS_ENDPOINT_URL: ${{ secrets.S3_ENDPOINT_URL }}
run: |
- aws s3 sync ./ s3://tsc/
+ aws s3 sync ./Releases s3://tsc/
- name: Create a new release
uses: softprops/action-gh-release@v2
diff --git a/.gitignore b/.gitignore
index 4795340..1f0f61d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -363,4 +363,5 @@ MigrationBackup/
FodyWeavers.xsd
# Custom dist folder
-dist/
\ No newline at end of file
+dist/
+codegen/
\ No newline at end of file
diff --git a/App.xaml.cs b/App.xaml.cs
index eae4a65..48ffed7 100644
--- a/App.xaml.cs
+++ b/App.xaml.cs
@@ -1,4 +1,6 @@
-using Microsoft.UI.Xaml;
+using System;
+using Microsoft.UI.Xaml;
+using Velopack;
namespace TailscaleClient;
@@ -10,7 +12,26 @@ public partial class App : Application
public App()
{
+ VelopackApp.Build().Run();
InitializeComponent();
+
+ var mgr = new UpdateManager("https://tsc.xirreal.dev");
+
+ try
+ {
+ var newVersion = mgr.CheckForUpdates();
+ if (newVersion == null)
+ {
+ return;
+ }
+
+ mgr.DownloadUpdates(newVersion);
+ mgr.ApplyUpdatesAndRestart(newVersion);
+ } catch (Exception e)
+ {
+ // TODO: Show failed update bar, possibly a badge on settings?
+ return;
+ }
}
protected override void OnLaunched(LaunchActivatedEventArgs args)
diff --git a/Assets/AccountCard.xaml b/Assets/AccountCard.xaml
index cc0206e..63f4bc1 100644
--- a/Assets/AccountCard.xaml
+++ b/Assets/AccountCard.xaml
@@ -34,8 +34,8 @@
-
-
+
+
diff --git a/Assets/DeviceCard.xaml b/Assets/DeviceCard.xaml
index f1b93d4..1d66864 100644
--- a/Assets/DeviceCard.xaml
+++ b/Assets/DeviceCard.xaml
@@ -23,7 +23,7 @@
-
+
diff --git a/Assets/DeviceCard.xaml.cs b/Assets/DeviceCard.xaml.cs
index 8e22ad3..569383d 100644
--- a/Assets/DeviceCard.xaml.cs
+++ b/Assets/DeviceCard.xaml.cs
@@ -31,11 +31,9 @@ private static string FormatDaysUntilExpiry(int daysUntilExpiry)
return daysUntilExpiry switch
{
- <
- 0 => "Expired",
+ <0 => "Expired",
0 => "Key expiring today",
_ => $"Expiring in {daysUntilExpiry} {daysText}"
-
};
}
@@ -43,8 +41,7 @@ private static Windows.UI.Color FormatColorForExpiry(int daysUntilExpiry)
{
return daysUntilExpiry switch
{
- <=
- 0 => Windows.UI.Color.FromArgb(255, 255, 0, 0),
+ <=0 => Windows.UI.Color.FromArgb(255, 255, 0, 0),
_ => Windows.UI.Color.FromArgb(255, 255, 150, 0),
};
}
@@ -157,7 +154,7 @@ public DeviceCard(Core.Types.PeerInfo device, Dictionary
+
+
+
+
+
+
+
diff --git a/Assets/FlippedSwitch.xaml.cs b/Assets/FlippedSwitch.xaml.cs
new file mode 100644
index 0000000..96cdbc8
--- /dev/null
+++ b/Assets/FlippedSwitch.xaml.cs
@@ -0,0 +1,64 @@
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+
+namespace TailscaleClient.Assets;
+
+public sealed partial class FlippedSwitch : UserControl, INotifyPropertyChanged
+{
+ public FlippedSwitch()
+ {
+ InitializeComponent();
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+ public void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ public static readonly DependencyProperty IsOnProperty =
+ DependencyProperty.Register(
+ nameof(IsOn),
+ typeof(bool),
+ typeof(FlippedSwitch),
+ new PropertyMetadata(false, OnIsOnChanged));
+
+ public bool IsOn
+ {
+ get => (bool)GetValue(IsOnProperty);
+ set => SetValue(IsOnProperty, value);
+ }
+
+ private static void OnIsOnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var control = (FlippedSwitch)d;
+ control.UpdateStatusText();
+ }
+
+ public event RoutedEventHandler Toggled;
+
+ private void ToggleSwitch_Toggled(object sender, RoutedEventArgs e)
+ {
+ Toggled?.Invoke(sender, e);
+ UpdateStatusText();
+ }
+
+ private string _status = "Off";
+ public string Status
+ {
+ get => _status;
+ set
+ {
+ _status = value;
+ OnPropertyChanged();
+ }
+ }
+
+ private void UpdateStatusText()
+ {
+ Status = ToggleSwitch.IsOn ? "On" : "Off";
+ }
+}
diff --git a/Assets/Icons/AppIcon.altform-lightunplated_targetsize-16.png b/Assets/Icons/AppIcon.altform-lightunplated_targetsize-16.png
deleted file mode 100644
index c0ad129..0000000
Binary files a/Assets/Icons/AppIcon.altform-lightunplated_targetsize-16.png and /dev/null differ
diff --git a/Assets/Icons/AppIcon.altform-lightunplated_targetsize-24.png b/Assets/Icons/AppIcon.altform-lightunplated_targetsize-24.png
deleted file mode 100644
index b1af4cd..0000000
Binary files a/Assets/Icons/AppIcon.altform-lightunplated_targetsize-24.png and /dev/null differ
diff --git a/Assets/Icons/AppIcon.altform-lightunplated_targetsize-256.png b/Assets/Icons/AppIcon.altform-lightunplated_targetsize-256.png
deleted file mode 100644
index b9e2919..0000000
Binary files a/Assets/Icons/AppIcon.altform-lightunplated_targetsize-256.png and /dev/null differ
diff --git a/Assets/Icons/AppIcon.altform-lightunplated_targetsize-32.png b/Assets/Icons/AppIcon.altform-lightunplated_targetsize-32.png
deleted file mode 100644
index 12b1bd3..0000000
Binary files a/Assets/Icons/AppIcon.altform-lightunplated_targetsize-32.png and /dev/null differ
diff --git a/Assets/Icons/AppIcon.altform-lightunplated_targetsize-48.png b/Assets/Icons/AppIcon.altform-lightunplated_targetsize-48.png
deleted file mode 100644
index c361874..0000000
Binary files a/Assets/Icons/AppIcon.altform-lightunplated_targetsize-48.png and /dev/null differ
diff --git a/Assets/Icons/AppIcon.altform-unplated_targetsize-16.png b/Assets/Icons/AppIcon.altform-unplated_targetsize-16.png
deleted file mode 100644
index c81ab65..0000000
Binary files a/Assets/Icons/AppIcon.altform-unplated_targetsize-16.png and /dev/null differ
diff --git a/Assets/Icons/AppIcon.altform-unplated_targetsize-24.png b/Assets/Icons/AppIcon.altform-unplated_targetsize-24.png
deleted file mode 100644
index add5a90..0000000
Binary files a/Assets/Icons/AppIcon.altform-unplated_targetsize-24.png and /dev/null differ
diff --git a/Assets/Icons/AppIcon.altform-unplated_targetsize-256.png b/Assets/Icons/AppIcon.altform-unplated_targetsize-256.png
deleted file mode 100644
index 3bddadb..0000000
Binary files a/Assets/Icons/AppIcon.altform-unplated_targetsize-256.png and /dev/null differ
diff --git a/Assets/Icons/AppIcon.altform-unplated_targetsize-32.png b/Assets/Icons/AppIcon.altform-unplated_targetsize-32.png
deleted file mode 100644
index a764f3a..0000000
Binary files a/Assets/Icons/AppIcon.altform-unplated_targetsize-32.png and /dev/null differ
diff --git a/Assets/Icons/AppIcon.altform-unplated_targetsize-48.png b/Assets/Icons/AppIcon.altform-unplated_targetsize-48.png
deleted file mode 100644
index 5ce2654..0000000
Binary files a/Assets/Icons/AppIcon.altform-unplated_targetsize-48.png and /dev/null differ
diff --git a/Assets/Icons/AppIcon.scale-100.png b/Assets/Icons/AppIcon.scale-100.png
deleted file mode 100644
index 2f422eb..0000000
Binary files a/Assets/Icons/AppIcon.scale-100.png and /dev/null differ
diff --git a/Assets/Icons/AppIcon.scale-125.png b/Assets/Icons/AppIcon.scale-125.png
deleted file mode 100644
index d94703e..0000000
Binary files a/Assets/Icons/AppIcon.scale-125.png and /dev/null differ
diff --git a/Assets/Icons/AppIcon.scale-150.png b/Assets/Icons/AppIcon.scale-150.png
deleted file mode 100644
index a9a9a16..0000000
Binary files a/Assets/Icons/AppIcon.scale-150.png and /dev/null differ
diff --git a/Assets/Icons/AppIcon.scale-200.png b/Assets/Icons/AppIcon.scale-200.png
deleted file mode 100644
index 93b7575..0000000
Binary files a/Assets/Icons/AppIcon.scale-200.png and /dev/null differ
diff --git a/Assets/Icons/AppIcon.scale-400.png b/Assets/Icons/AppIcon.scale-400.png
deleted file mode 100644
index 1a98fb4..0000000
Binary files a/Assets/Icons/AppIcon.scale-400.png and /dev/null differ
diff --git a/Assets/Icons/AppIcon.targetsize-16.png b/Assets/Icons/AppIcon.targetsize-16.png
deleted file mode 100644
index c81ab65..0000000
Binary files a/Assets/Icons/AppIcon.targetsize-16.png and /dev/null differ
diff --git a/Assets/Icons/AppIcon.targetsize-24.png b/Assets/Icons/AppIcon.targetsize-24.png
deleted file mode 100644
index add5a90..0000000
Binary files a/Assets/Icons/AppIcon.targetsize-24.png and /dev/null differ
diff --git a/Assets/Icons/AppIcon.targetsize-256.png b/Assets/Icons/AppIcon.targetsize-256.png
deleted file mode 100644
index 3bddadb..0000000
Binary files a/Assets/Icons/AppIcon.targetsize-256.png and /dev/null differ
diff --git a/Assets/Icons/AppIcon.targetsize-32.png b/Assets/Icons/AppIcon.targetsize-32.png
deleted file mode 100644
index a764f3a..0000000
Binary files a/Assets/Icons/AppIcon.targetsize-32.png and /dev/null differ
diff --git a/Assets/Icons/AppIcon.targetsize-48.png b/Assets/Icons/AppIcon.targetsize-48.png
deleted file mode 100644
index 5ce2654..0000000
Binary files a/Assets/Icons/AppIcon.targetsize-48.png and /dev/null differ
diff --git a/Assets/Icons/AppIconBase-Dark.ico b/Assets/Icons/AppIconBase-Dark.ico
index 6512e53..2be61c8 100644
Binary files a/Assets/Icons/AppIconBase-Dark.ico and b/Assets/Icons/AppIconBase-Dark.ico differ
diff --git a/Assets/Icons/AppIconBase-Light.ico b/Assets/Icons/AppIconBase-Light.ico
index 4ffe50d..48ae47b 100644
Binary files a/Assets/Icons/AppIconBase-Light.ico and b/Assets/Icons/AppIconBase-Light.ico differ
diff --git a/Core/API.cs b/Core/API.cs
index e50b0ed..4a12be2 100644
--- a/Core/API.cs
+++ b/Core/API.cs
@@ -47,8 +47,7 @@ public static bool Init()
{
BaseAddress = new Uri("http://local-tailscaled.sock"),
DefaultRequestHeaders = { { "User-Agent", "Go-http-client/1.1" },
- { "Tailscale-Cap", "95" }, // TODO: Get the real value i just
- // copied it from tailscale-ipn packets
+ { "Tailscale-Cap", "113" }, // Latest (as of 2025/02/07) from https://github.com/tailscale/tailscale/blob/main/tailcfg/tailcfg.go
{ "Accept-Encoding", "gzip" } },
Timeout = TimeSpan.FromSeconds(2)
};
@@ -182,9 +181,9 @@ public static Types.Profile GetCurrentUser()
return GET>("/localapi/v0/profiles/");
}
- public static Types.Prefs GetPrefs()
+ public static Types.MaskedPrefs GetPrefs()
{
- return GET("/localapi/v0/prefs");
+ return GET("/localapi/v0/prefs");
}
public static void SwitchEmptyProfile()
@@ -202,19 +201,19 @@ public static void DeleteProfile(string userId)
DELETE