feat(ui): open Mission Control from the title bar
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using ClaudeDo.Ui.Services;
|
using ClaudeDo.Ui.Services;
|
||||||
@@ -32,6 +33,10 @@ public partial class App : Application
|
|||||||
|
|
||||||
FocusClearing.Install();
|
FocusClearing.Install();
|
||||||
|
|
||||||
|
// The main window is authoritative — closing it shuts the app down even if the
|
||||||
|
// modeless Mission Control window is still open.
|
||||||
|
desktop.ShutdownMode = ShutdownMode.OnMainWindowClose;
|
||||||
|
|
||||||
desktop.MainWindow = new MainWindow
|
desktop.MainWindow = new MainWindow
|
||||||
{
|
{
|
||||||
DataContext = services.GetRequiredService<IslandsShellViewModel>(),
|
DataContext = services.GetRequiredService<IslandsShellViewModel>(),
|
||||||
|
|||||||
@@ -168,8 +168,6 @@ sealed class Program
|
|||||||
shell.ConflictResolverFactory =
|
shell.ConflictResolverFactory =
|
||||||
sp.GetRequiredService<Func<string, ClaudeDo.Ui.ViewModels.Conflicts.ConflictResolverViewModel>>();
|
sp.GetRequiredService<Func<string, ClaudeDo.Ui.ViewModels.Conflicts.ConflictResolverViewModel>>();
|
||||||
sp.GetRequiredService<MergeCoordinator>().Handler = shell.RequestConflictResolutionAsync;
|
sp.GetRequiredService<MergeCoordinator>().Handler = shell.RequestConflictResolutionAsync;
|
||||||
var missionControl = sp.GetRequiredService<MissionControlViewModel>();
|
|
||||||
missionControl.OpenInApp = id => _ = shell.RevealTaskAsync(id);
|
|
||||||
return shell;
|
return shell;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
<!-- Window control icons — filled geometries (PathIcon fills, not strokes) -->
|
<!-- Window control icons — filled geometries (PathIcon fills, not strokes) -->
|
||||||
<StreamGeometry x:Key="Icon.WinMin">M4 9 H16 V11 H4 Z</StreamGeometry>
|
<StreamGeometry x:Key="Icon.WinMin">M4 9 H16 V11 H4 Z</StreamGeometry>
|
||||||
<StreamGeometry x:Key="Icon.WinMax">M4 4 H16 V6 H4 Z M4 14 H16 V16 H4 Z M4 4 H6 V16 H4 Z M14 4 H16 V16 H14 Z</StreamGeometry>
|
<StreamGeometry x:Key="Icon.WinMax">M4 4 H16 V6 H4 Z M4 14 H16 V16 H4 Z M4 4 H6 V16 H4 Z M14 4 H16 V16 H14 Z</StreamGeometry>
|
||||||
|
<!-- Icon.Grid (four filled panes — Mission Control launcher) -->
|
||||||
|
<StreamGeometry x:Key="Icon.Grid">M3 3 H9 V9 H3 Z M11 3 H17 V9 H11 Z M3 11 H9 V17 H3 Z M11 11 H17 V17 H11 Z</StreamGeometry>
|
||||||
<StreamGeometry x:Key="Icon.WinRestore">M4 7 H13 V9 H4 Z M4 14 H13 V16 H4 Z M4 7 H6 V16 H4 Z M11 7 H13 V16 H11 Z M7 4 H16 V6 H7 Z M14 4 H16 V13 H14 Z</StreamGeometry>
|
<StreamGeometry x:Key="Icon.WinRestore">M4 7 H13 V9 H4 Z M4 14 H13 V16 H4 Z M4 7 H6 V16 H4 Z M11 7 H13 V16 H11 Z M7 4 H16 V6 H7 Z M14 4 H16 V13 H14 Z</StreamGeometry>
|
||||||
<StreamGeometry x:Key="Icon.WinClose">M4 5 L5 4 L16 15 L15 16 Z M15 4 L16 5 L5 16 L4 15 Z</StreamGeometry>
|
<StreamGeometry x:Key="Icon.WinClose">M4 5 L5 4 L16 15 L15 16 Z M15 4 L16 5 L5 16 L4 15 Z</StreamGeometry>
|
||||||
<!-- Brand check glyph — filled rounded square with inset tick -->
|
<!-- Brand check glyph — filled rounded square with inset tick -->
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using ClaudeDo.Ui.ViewModels;
|
||||||
using ClaudeDo.Ui.ViewModels.Conflicts;
|
using ClaudeDo.Ui.ViewModels.Conflicts;
|
||||||
using ClaudeDo.Ui.ViewModels.Modals;
|
using ClaudeDo.Ui.ViewModels.Modals;
|
||||||
|
|
||||||
@@ -28,4 +29,7 @@ public interface IDialogService
|
|||||||
|
|
||||||
/// <summary>Modal error notice with a single dismiss button.</summary>
|
/// <summary>Modal error notice with a single dismiss button.</summary>
|
||||||
Task ShowErrorAsync(string message);
|
Task ShowErrorAsync(string message);
|
||||||
|
|
||||||
|
/// <summary>Show (or re-show + focus) the modeless Mission Control window. Lazily created; hides on close.</summary>
|
||||||
|
void ShowMissionControl(MissionControlViewModel vm);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ public sealed partial class IslandsShellViewModel : ViewModelBase, IDisposable
|
|||||||
public TasksIslandViewModel? Tasks { get; }
|
public TasksIslandViewModel? Tasks { get; }
|
||||||
public DetailsIslandViewModel? Details { get; }
|
public DetailsIslandViewModel? Details { get; }
|
||||||
public IWorkerClient? Worker { get; }
|
public IWorkerClient? Worker { get; }
|
||||||
|
public MissionControlViewModel? MissionControl { get; }
|
||||||
public UpdateCheckService UpdateCheck => _updateCheck;
|
public UpdateCheckService UpdateCheck => _updateCheck;
|
||||||
|
|
||||||
public string ConnectionText =>
|
public string ConnectionText =>
|
||||||
@@ -206,9 +207,12 @@ public sealed partial class IslandsShellViewModel : ViewModelBase, IDisposable
|
|||||||
Func<WorktreesOverviewModalViewModel> worktreesOverviewVmFactory,
|
Func<WorktreesOverviewModalViewModel> worktreesOverviewVmFactory,
|
||||||
Func<WeeklyReportModalViewModel> weeklyReportVmFactory,
|
Func<WeeklyReportModalViewModel> weeklyReportVmFactory,
|
||||||
Func<MergeModalViewModel> mergeVmFactory,
|
Func<MergeModalViewModel> mergeVmFactory,
|
||||||
Func<RepoImportModalViewModel> repoImportVmFactory)
|
Func<RepoImportModalViewModel> repoImportVmFactory,
|
||||||
|
MissionControlViewModel missionControl)
|
||||||
{
|
{
|
||||||
Lists = lists; Tasks = tasks; Details = details; Worker = worker;
|
Lists = lists; Tasks = tasks; Details = details; Worker = worker;
|
||||||
|
MissionControl = missionControl;
|
||||||
|
MissionControl.OpenInApp = id => _ = RevealTaskAsync(id);
|
||||||
_updateCheck = updateCheck;
|
_updateCheck = updateCheck;
|
||||||
_installerLocator = installerLocator;
|
_installerLocator = installerLocator;
|
||||||
_workerLocator = workerLocator;
|
_workerLocator = workerLocator;
|
||||||
@@ -312,6 +316,13 @@ public sealed partial class IslandsShellViewModel : ViewModelBase, IDisposable
|
|||||||
if (InlineUpdateStatus == text) InlineUpdateStatus = null;
|
if (InlineUpdateStatus == text) InlineUpdateStatus = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void OpenMissionControl()
|
||||||
|
{
|
||||||
|
if (Dialogs is not null && MissionControl is not null)
|
||||||
|
Dialogs.ShowMissionControl(MissionControl);
|
||||||
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private async Task OpenAbout()
|
private async Task OpenAbout()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -88,6 +88,11 @@
|
|||||||
<!-- Right: window controls -->
|
<!-- Right: window controls -->
|
||||||
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="0"
|
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="0"
|
||||||
VerticalAlignment="Center" Margin="0,0,4,0">
|
VerticalAlignment="Center" Margin="0,0,4,0">
|
||||||
|
<Button Classes="title-ctrl"
|
||||||
|
Command="{Binding OpenMissionControlCommand}"
|
||||||
|
ToolTip.Tip="{loc:Tr missionControl.windowTitle}">
|
||||||
|
<PathIcon Data="{StaticResource Icon.Grid}" Width="12" Height="12"/>
|
||||||
|
</Button>
|
||||||
<Button Classes="title-ctrl" Click="OnMinimize">
|
<Button Classes="title-ctrl" Click="OnMinimize">
|
||||||
<PathIcon Data="{StaticResource Icon.WinMin}" Width="10" Height="10"/>
|
<PathIcon Data="{StaticResource Icon.WinMin}" Width="10" Height="10"/>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -42,6 +42,12 @@ public partial class MainWindow : Window
|
|||||||
if (DataContext is IslandsShellViewModel vm)
|
if (DataContext is IslandsShellViewModel vm)
|
||||||
{
|
{
|
||||||
vm.Dialogs = new WindowDialogService(this);
|
vm.Dialogs = new WindowDialogService(this);
|
||||||
|
vm.BringToFront = () =>
|
||||||
|
{
|
||||||
|
if (WindowState == WindowState.Minimized)
|
||||||
|
WindowState = WindowState.Normal;
|
||||||
|
Activate();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using ClaudeDo.Ui.ViewModels;
|
|||||||
using ClaudeDo.Ui.ViewModels.Conflicts;
|
using ClaudeDo.Ui.ViewModels.Conflicts;
|
||||||
using ClaudeDo.Ui.ViewModels.Modals;
|
using ClaudeDo.Ui.ViewModels.Modals;
|
||||||
using ClaudeDo.Ui.Views.Conflicts;
|
using ClaudeDo.Ui.Views.Conflicts;
|
||||||
|
using ClaudeDo.Ui.Views.MissionControl;
|
||||||
using ClaudeDo.Ui.Views.Modals;
|
using ClaudeDo.Ui.Views.Modals;
|
||||||
|
|
||||||
namespace ClaudeDo.Ui.Views;
|
namespace ClaudeDo.Ui.Views;
|
||||||
@@ -22,6 +23,7 @@ namespace ClaudeDo.Ui.Views;
|
|||||||
public sealed class WindowDialogService : IDialogService
|
public sealed class WindowDialogService : IDialogService
|
||||||
{
|
{
|
||||||
private readonly Window _owner;
|
private readonly Window _owner;
|
||||||
|
private MissionControlWindow? _missionControl;
|
||||||
|
|
||||||
public WindowDialogService(Window owner) => _owner = owner;
|
public WindowDialogService(Window owner) => _owner = owner;
|
||||||
|
|
||||||
@@ -111,6 +113,14 @@ public sealed class WindowDialogService : IDialogService
|
|||||||
await dlg.ShowDialog(_owner);
|
await dlg.ShowDialog(_owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ShowMissionControl(MissionControlViewModel vm)
|
||||||
|
{
|
||||||
|
_missionControl ??= new MissionControlWindow { DataContext = vm };
|
||||||
|
if (!_missionControl.IsVisible)
|
||||||
|
_missionControl.Show(); // modeless, independent top-level window
|
||||||
|
_missionControl.Activate(); // bring to front / focus
|
||||||
|
}
|
||||||
|
|
||||||
public Task<bool> ConfirmAsync(string message)
|
public Task<bool> ConfirmAsync(string message)
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<bool>();
|
var tcs = new TaskCompletionSource<bool>();
|
||||||
|
|||||||
Reference in New Issue
Block a user