Skip to content

Commit e8d5a79

Browse files
committed
Merge branch 'release/v2026.09'
2 parents 7b5ef8c + 83edc51 commit e8d5a79

82 files changed

Lines changed: 2022 additions & 607 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

TRANSLATION.md

Lines changed: 156 additions & 13 deletions
Large diffs are not rendered by default.

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2026.08
1+
2026.09

src/AI/Service.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using System.ClientModel;
33
using System.Collections.Generic;
44
using System.Text.Json.Serialization;
5-
using System.Threading.Tasks;
65
using Azure.AI.OpenAI;
76
using CommunityToolkit.Mvvm.ComponentModel;
87
using OpenAI;
@@ -55,7 +54,7 @@ public string Model
5554
set;
5655
} = string.Empty;
5756

58-
public async Task<List<string>> FetchAvailableModelsAsync()
57+
public void FetchAvailableModels()
5958
{
6059
var allModels = GetOpenAIClient().GetOpenAIModelClient().GetModels();
6160
AvailableModels = new List<string>();
@@ -71,8 +70,6 @@ public async Task<List<string>> FetchAvailableModelsAsync()
7170
{
7271
Model = null;
7372
}
74-
75-
return AvailableModels;
7673
}
7774

7875
public ChatClient GetChatClient()
@@ -84,8 +81,8 @@ private OpenAIClient GetOpenAIClient()
8481
{
8582
var credential = new ApiKeyCredential(ReadApiKeyFromEnv ? Environment.GetEnvironmentVariable(ApiKey) : ApiKey);
8683
return Server.Contains("openai.azure.com/", StringComparison.Ordinal)
87-
? new AzureOpenAIClient(new Uri(Server), credential, new AzureOpenAIClientOptions() { UserAgentApplicationId = string.Empty })
88-
: new OpenAIClient(credential, new() { Endpoint = new Uri(Server), UserAgentApplicationId = string.Empty });
84+
? new AzureOpenAIClient(new Uri(Server), credential)
85+
: new OpenAIClient(credential, new() { Endpoint = new Uri(Server) });
8986
}
9087

9188
private string _name = string.Empty;

src/App.Commands.cs

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,61 @@ public static bool IsCheckForUpdateCommandVisible
3737
}
3838
}
3939

40-
public static readonly Command OpenPreferencesCommand = new Command(async _ => await ShowDialog(new Views.Preferences()));
41-
public static readonly Command OpenHotkeysCommand = new Command(async _ => await ShowDialog(new Views.Hotkeys()));
42-
public static readonly Command OpenAppDataDirCommand = new Command(_ => Native.OS.OpenInFileManager(Native.OS.DataDir));
43-
public static readonly Command OpenAboutCommand = new Command(async _ => await ShowDialog(new Views.About()));
44-
public static readonly Command CheckForUpdateCommand = new Command(_ => (Current as App)?.Check4Update(true));
45-
public static readonly Command QuitCommand = new Command(_ => Quit(0));
40+
public static readonly Command OpenPreferencesCommand = new Command(async _ =>
41+
{
42+
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner })
43+
{
44+
var dialog = new Views.Preferences();
45+
await dialog.ShowDialog(owner);
46+
}
47+
});
48+
49+
public static readonly Command OpenHotkeysCommand = new Command(async _ =>
50+
{
51+
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner })
52+
{
53+
var dialog = new Views.Hotkeys();
54+
await dialog.ShowDialog(owner);
55+
}
56+
});
57+
58+
public static readonly Command OpenAppDataDirCommand = new Command(_ =>
59+
{
60+
Native.OS.OpenInFileManager(Native.OS.DataDir);
61+
});
62+
63+
public static readonly Command OpenAboutCommand = new Command(async _ =>
64+
{
65+
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner })
66+
{
67+
var dialog = new Views.About();
68+
await dialog.ShowDialog(owner);
69+
}
70+
});
71+
72+
public static readonly Command CheckForUpdateCommand = new Command(_ =>
73+
{
74+
(Current as App)?.Check4Update(true);
75+
});
76+
77+
public static readonly Command QuitCommand = new Command(_ =>
78+
{
79+
Quit(0);
80+
});
4681

4782
public static readonly Command HideAppCommand = new Command(_ =>
4883
{
49-
if (Current is App app && app.TryGetFeature(typeof(IActivatableLifetime)) is IActivatableLifetime lifetime)
50-
lifetime.TryEnterBackground();
84+
Native.OS.HideSelf();
85+
});
86+
87+
public static readonly Command HideOtherApplicationsCommand = new Command(_ =>
88+
{
89+
Native.OS.HideOtherApplications();
5190
});
5291

53-
public static readonly Command ShowAppCommand = new Command(_ =>
92+
public static readonly Command ShowAllApplicationsCommand = new Command(_ =>
5493
{
55-
if (Current is App app && app.TryGetFeature(typeof(IActivatableLifetime)) is IActivatableLifetime lifetime)
56-
lifetime.TryLeaveBackground();
94+
Native.OS.ShowAllApplications();
5795
});
5896
}
5997
}

src/App.Extensions.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.IO;
4+
using System.Text;
5+
using Avalonia.Media;
36

47
namespace SourceGit
58
{
@@ -14,6 +17,47 @@ public static string Escaped(this string value)
1417
{
1518
return value.Replace("\"", "\\\"", StringComparison.Ordinal);
1619
}
20+
21+
public static string FormatFontNames(string input)
22+
{
23+
if (string.IsNullOrEmpty(input))
24+
return string.Empty;
25+
26+
var parts = input.Split(',');
27+
var trimmed = new List<string>();
28+
29+
foreach (var part in parts)
30+
{
31+
var t = part.Trim();
32+
if (string.IsNullOrEmpty(t))
33+
continue;
34+
35+
var sb = new StringBuilder();
36+
var prevChar = '\0';
37+
38+
foreach (var c in t)
39+
{
40+
if (c == ' ' && prevChar == ' ')
41+
continue;
42+
sb.Append(c);
43+
prevChar = c;
44+
}
45+
46+
var name = sb.ToString();
47+
try
48+
{
49+
var fontFamily = FontFamily.Parse(name);
50+
if (fontFamily.FamilyTypefaces.Count > 0)
51+
trimmed.Add(name);
52+
}
53+
catch
54+
{
55+
// Ignore exceptions.
56+
}
57+
}
58+
59+
return trimmed.Count > 0 ? string.Join(',', trimmed) : string.Empty;
60+
}
1761
}
1862

1963
public static class CommandExtensions

src/App.axaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@
4545
<NativeMenuItem Header="{DynamicResource Text.OpenAppDataDir}" Command="{x:Static s:App.OpenAppDataDirCommand}"/>
4646
<NativeMenuItemSeparator/>
4747
<NativeMenuItem Header="{DynamicResource Text.App.Hide}" Command="{x:Static s:App.HideAppCommand}" Gesture="⌘+H"/>
48-
<NativeMenuItem Header="{DynamicResource Text.App.ShowAll}" Command="{x:Static s:App.ShowAppCommand}"/>
48+
<NativeMenuItem Header="{DynamicResource Text.App.HideOthers}" Command="{x:Static s:App.HideOtherApplicationsCommand}" Gesture="⌘+Alt+H"/>
49+
<NativeMenuItem Header="{DynamicResource Text.App.ShowAll}" Command="{x:Static s:App.ShowAllApplicationsCommand}"/>
4950
<NativeMenuItemSeparator/>
5051
<NativeMenuItem Header="{DynamicResource Text.Quit}" Command="{x:Static s:App.QuitCommand}" Gesture="⌘+Q"/>
5152
</NativeMenu>

src/App.axaml.cs

Lines changed: 26 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
using System;
2-
using System.Collections.Generic;
32
using System.IO;
43
using System.Net.Http;
5-
using System.Text;
64
using System.Text.Json;
75
using System.Threading.Tasks;
86

@@ -75,79 +73,6 @@ public static AppBuilder BuildAvaloniaApp()
7573
#endregion
7674

7775
#region Utility Functions
78-
public static Task ShowDialog(object data, Window owner = null)
79-
{
80-
if (owner == null)
81-
{
82-
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } mainWindow })
83-
owner = mainWindow;
84-
else
85-
return null;
86-
}
87-
88-
if (data is Views.ChromelessWindow window)
89-
return window.ShowDialog(owner);
90-
91-
window = Views.ControlExtensions.CreateFromViewModels(data) as Views.ChromelessWindow;
92-
if (window != null)
93-
{
94-
window.DataContext = data;
95-
return window.ShowDialog(owner);
96-
}
97-
98-
return null;
99-
}
100-
101-
public static void ShowWindow(object data)
102-
{
103-
if (data is not Views.ChromelessWindow window)
104-
{
105-
window = Views.ControlExtensions.CreateFromViewModels(data) as Views.ChromelessWindow;
106-
if (window == null)
107-
return;
108-
109-
window.DataContext = data;
110-
}
111-
112-
do
113-
{
114-
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { Windows: { Count: > 0 } windows })
115-
{
116-
// Try to find the actived window (fall back to `MainWindow`)
117-
Window actived = windows[0];
118-
if (!actived.IsActive)
119-
{
120-
for (var i = 1; i < windows.Count; i++)
121-
{
122-
var test = windows[i];
123-
if (test.IsActive)
124-
{
125-
actived = test;
126-
break;
127-
}
128-
}
129-
}
130-
131-
// Get the screen where current window locates.
132-
var screen = actived.Screens.ScreenFromWindow(actived) ?? actived.Screens.Primary;
133-
if (screen == null)
134-
break;
135-
136-
// Calculate the startup position (Center Screen Mode) of target window
137-
var rect = new PixelRect(PixelSize.FromSize(window.ClientSize, actived.DesktopScaling));
138-
var centeredRect = screen.WorkingArea.CenterRect(rect);
139-
if (actived.Screens.ScreenFromPoint(centeredRect.Position) == null)
140-
break;
141-
142-
// Use the startup position
143-
window.WindowStartupLocation = WindowStartupLocation.Manual;
144-
window.Position = centeredRect.Position;
145-
}
146-
} while (false);
147-
148-
window.Show();
149-
}
150-
15176
public static async Task<bool> AskConfirmAsync(string message, Models.ConfirmButtonType buttonType = Models.ConfirmButtonType.OkCancel)
15277
{
15378
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner })
@@ -253,8 +178,8 @@ public static void SetFonts(string defaultFont, string monospaceFont)
253178
app._fontsOverrides = null;
254179
}
255180

256-
defaultFont = app.FixFontFamilyName(defaultFont);
257-
monospaceFont = app.FixFontFamilyName(monospaceFont);
181+
defaultFont = StringExtensions.FormatFontNames(defaultFont);
182+
monospaceFont = StringExtensions.FormatFontNames(monospaceFont);
258183

259184
var resDic = new ResourceDictionary();
260185
if (!string.IsNullOrEmpty(defaultFont))
@@ -303,15 +228,9 @@ public static ViewModels.Launcher GetLauncher()
303228
public static void Quit(int exitCode)
304229
{
305230
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
306-
{
307-
desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown;
308-
desktop.MainWindow?.Close();
309231
desktop.Shutdown(exitCode);
310-
}
311232
else
312-
{
313233
Environment.Exit(exitCode);
314-
}
315234
}
316235
#endregion
317236

@@ -547,10 +466,27 @@ private void TryLaunchAsNormal(IClassicDesktopStyleApplicationLifetime desktop)
547466

548467
var pref = ViewModels.Preferences.Instance;
549468
pref.SetCanModify();
469+
pref.UpdateAvailableAIModels();
550470

551471
_launcher = new ViewModels.Launcher(startupRepo);
552472
desktop.MainWindow = new Views.Launcher() { DataContext = _launcher };
553-
desktop.ShutdownMode = ShutdownMode.OnMainWindowClose;
473+
desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown;
474+
475+
// Fix macOS crash when quiting from Dock
476+
if (OperatingSystem.IsMacOS())
477+
{
478+
desktop.ShutdownRequested += (_, e) =>
479+
{
480+
e.Cancel = true;
481+
Dispatcher.UIThread.Post(() => Quit(0));
482+
};
483+
}
484+
485+
desktop.Exit += (_, _) =>
486+
{
487+
_ipcChannel?.Dispose();
488+
_ipcChannel = null;
489+
};
554490

555491
_ipcChannel.MessageReceived += repo =>
556492
{
@@ -562,8 +498,6 @@ private void TryLaunchAsNormal(IClassicDesktopStyleApplicationLifetime desktop)
562498
});
563499
};
564500

565-
desktop.Exit += (_, _) => _ipcChannel.Dispose();
566-
567501
#if !DISABLE_UPDATE_DETECTION
568502
if (pref.ShouldCheck4UpdateOnStartup())
569503
Check4Update();
@@ -619,7 +553,12 @@ private void ShowSelfUpdateResult(object data)
619553
{
620554
Dispatcher.UIThread.Invoke(async () =>
621555
{
622-
await ShowDialog(new ViewModels.SelfUpdate { Data = data });
556+
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner })
557+
{
558+
var ctx = new ViewModels.SelfUpdate { Data = data };
559+
var dialog = new Views.SelfUpdate() { DataContext = ctx };
560+
await dialog.ShowDialog(owner);
561+
}
623562
});
624563
}
625564
catch
@@ -629,47 +568,6 @@ private void ShowSelfUpdateResult(object data)
629568
}
630569
#endregion
631570

632-
private string FixFontFamilyName(string input)
633-
{
634-
if (string.IsNullOrEmpty(input))
635-
return string.Empty;
636-
637-
var parts = input.Split(',');
638-
var trimmed = new List<string>();
639-
640-
foreach (var part in parts)
641-
{
642-
var t = part.Trim();
643-
if (string.IsNullOrEmpty(t))
644-
continue;
645-
646-
var sb = new StringBuilder();
647-
var prevChar = '\0';
648-
649-
foreach (var c in t)
650-
{
651-
if (c == ' ' && prevChar == ' ')
652-
continue;
653-
sb.Append(c);
654-
prevChar = c;
655-
}
656-
657-
var name = sb.ToString();
658-
try
659-
{
660-
var fontFamily = FontFamily.Parse(name);
661-
if (fontFamily.FamilyTypefaces.Count > 0)
662-
trimmed.Add(name);
663-
}
664-
catch
665-
{
666-
// Ignore exceptions.
667-
}
668-
}
669-
670-
return trimmed.Count > 0 ? string.Join(',', trimmed) : string.Empty;
671-
}
672-
673571
private Models.IpcChannel _ipcChannel = null;
674572
private ViewModels.Launcher _launcher = null;
675573
private ResourceDictionary _activeLocale = null;

0 commit comments

Comments
 (0)