feat(ui): open Settings from the Mission Control header

This commit is contained in:
Mika Kuns
2026-06-25 16:49:20 +02:00
parent f6ecfc995f
commit 7f4dc8b973
6 changed files with 38 additions and 6 deletions

View File

@@ -240,7 +240,8 @@
"redock": "Andocken", "redock": "Andocken",
"windowTitle": "Mission Control", "windowTitle": "Mission Control",
"clearFinished": "Erledigte entfernen", "clearFinished": "Erledigte entfernen",
"empty": "Keine laufenden Aufgaben" "empty": "Keine laufenden Aufgaben",
"settings": "Einstellungen"
}, },
"modals": { "modals": {
"logVisualizer": { "logVisualizer": {

View File

@@ -240,7 +240,8 @@
"redock": "Re-dock", "redock": "Re-dock",
"windowTitle": "Mission Control", "windowTitle": "Mission Control",
"clearFinished": "Clear finished", "clearFinished": "Clear finished",
"empty": "No running tasks" "empty": "No running tasks",
"settings": "Settings"
}, },
"modals": { "modals": {
"logVisualizer": { "logVisualizer": {

View File

@@ -214,6 +214,7 @@ public sealed partial class IslandsShellViewModel : ViewModelBase, IDisposable
MissionControl = missionControl; MissionControl = missionControl;
MissionControl.OpenInApp = id => _ = RevealTaskAsync(id); MissionControl.OpenInApp = id => _ = RevealTaskAsync(id);
MissionControl.ShowDetached = (monitor, reDock) => Dialogs?.ShowDetachedMonitor(monitor, reDock); MissionControl.ShowDetached = (monitor, reDock) => Dialogs?.ShowDetachedMonitor(monitor, reDock);
MissionControl.OpenSettingsRequested = () => Lists.OpenSettingsCommand.Execute(null);
_updateCheck = updateCheck; _updateCheck = updateCheck;
_installerLocator = installerLocator; _installerLocator = installerLocator;
_workerLocator = workerLocator; _workerLocator = workerLocator;

View File

@@ -37,6 +37,9 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
// invoked when that window closes. // invoked when that window closes.
public Action<TaskMonitorViewModel, Action>? ShowDetached { get; set; } public Action<TaskMonitorViewModel, Action>? ShowDetached { get; set; }
// View-layer seam: open the app Settings modal from the Mission Control window.
public Action? OpenSettingsRequested { get; set; }
public bool HasMonitors => Monitors.Count > 0; public bool HasMonitors => Monitors.Count > 0;
public MissionControlViewModel(IDbContextFactory<ClaudeDoDbContext> dbFactory, IWorkerClient worker) public MissionControlViewModel(IDbContextFactory<ClaudeDoDbContext> dbFactory, IWorkerClient worker)
@@ -115,6 +118,9 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
} }
} }
[RelayCommand]
private void OpenSettings() => OpenSettingsRequested?.Invoke();
public void MoveMonitor(TaskMonitorViewModel dragged, TaskMonitorViewModel target) public void MoveMonitor(TaskMonitorViewModel dragged, TaskMonitorViewModel target)
{ {
if (ReferenceEquals(dragged, target)) return; if (ReferenceEquals(dragged, target)) return;

View File

@@ -18,9 +18,18 @@
Text="{loc:Tr missionControl.windowTitle}" Text="{loc:Tr missionControl.windowTitle}"
Foreground="{DynamicResource TextBrush}" Foreground="{DynamicResource TextBrush}"
LetterSpacing="1.4" VerticalAlignment="Center" /> LetterSpacing="1.4" VerticalAlignment="Center" />
<Button Grid.Column="1" Classes="btn" <StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="8"
VerticalAlignment="Center">
<Button Classes="icon-btn"
Command="{Binding OpenSettingsCommand}"
ToolTip.Tip="{loc:Tr missionControl.settings}">
<PathIcon Data="{StaticResource Icon.Settings}" Width="15" Height="15"
Foreground="{DynamicResource TextMuteBrush}"/>
</Button>
<Button Classes="btn"
Content="{loc:Tr missionControl.clearFinished}" Content="{loc:Tr missionControl.clearFinished}"
Command="{Binding ClearFinishedCommand}" /> Command="{Binding ClearFinishedCommand}" />
</StackPanel>
</Grid> </Grid>
</Border> </Border>

View File

@@ -1,7 +1,9 @@
using System; using System;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
using ClaudeDo.Ui.Services; using ClaudeDo.Ui.Services;
@@ -31,6 +33,18 @@ public sealed class WindowDialogService : IDialogService
private IslandsShellViewModel? Shell => _owner.DataContext as IslandsShellViewModel; private IslandsShellViewModel? Shell => _owner.DataContext as IslandsShellViewModel;
// Own modals to the window the user is actually looking at (e.g. the Mission Control
// window when Settings is opened from there), falling back to the main window.
private Window ActiveOwner()
{
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
var active = desktop.Windows.FirstOrDefault(w => w.IsActive);
if (active is not null) return active;
}
return _owner;
}
public async Task ShowAboutAsync(AboutModalViewModel vm) public async Task ShowAboutAsync(AboutModalViewModel vm)
{ {
var dlg = new AboutModalView { DataContext = vm }; var dlg = new AboutModalView { DataContext = vm };
@@ -48,7 +62,7 @@ public sealed class WindowDialogService : IDialogService
public async Task ShowSettingsAsync(SettingsModalViewModel vm) public async Task ShowSettingsAsync(SettingsModalViewModel vm)
{ {
var dlg = new SettingsModalView { DataContext = vm }; var dlg = new SettingsModalView { DataContext = vm };
await dlg.ShowDialog(_owner); await dlg.ShowDialog(ActiveOwner());
} }
public async Task ShowListSettingsAsync(ListSettingsModalViewModel vm) public async Task ShowListSettingsAsync(ListSettingsModalViewModel vm)