Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/OpenClaw.SetupEngine.UI/SetupWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public sealed partial class SetupWindow : Window
public event EventHandler? AdvancedSetupRequested;
public event EventHandler<SetupCompletedEventArgs>? SetupCompleted;
public bool IsClosed => _isClosed;
public bool CanNavigateToWizard => !_isClosed && _setupLock is not null;

[DllImport("user32.dll")]
private static extern uint GetDpiForWindow(IntPtr hwnd);
Expand Down Expand Up @@ -92,7 +93,20 @@ public SetupWindow(string? configPath = null)

public void NavigateToCapabilities() => RootFrame.Navigate(typeof(CapabilitiesPage), _config);
public void NavigateToProgress() => RootFrame.Navigate(typeof(ProgressPage), _config);
public void NavigateToWizard() => RootFrame.Navigate(typeof(WizardPage), _config);
public bool TryNavigateToWizard()
{
if (!CanNavigateToWizard)
return false;

RootFrame.Navigate(typeof(WizardPage), _config);
return true;
}

public void NavigateToWizard()
{
if (!TryNavigateToWizard())
throw new InvalidOperationException("Setup window is not ready to navigate to the gateway wizard.");
}
public void NavigateToPermissions() => RootFrame.Navigate(typeof(PermissionsPage), _config);
public void NavigateToComplete(bool success, TimeSpan elapsed, string? logPath, string? errorMessage = null)
=> RootFrame.Navigate(typeof(CompletePage), new CompletePageArgs(success, elapsed, logPath, errorMessage));
Expand Down
42 changes: 37 additions & 5 deletions src/OpenClaw.Tray.WinUI/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2678,7 +2678,7 @@ private string BuildTrayTooltip() =>

#region Window Management

internal void ShowHub(string? navigateTo = null, bool activate = true, string? originTag = null)
internal void ShowHub(string? navigateTo = null, bool activate = true)
{
if (_hubWindow == null || _hubWindow.IsClosed)
{
Expand Down Expand Up @@ -2729,7 +2729,7 @@ internal void ShowHub(string? navigateTo = null, bool activate = true, string? o

if (navigateTo != null)
{
_hubWindow.NavigateTo(navigateTo, originTag);
_hubWindow.NavigateTo(navigateTo);
}
if (activate)
{
Expand Down Expand Up @@ -3017,17 +3017,22 @@ private void ShowActivityStream(string? filter = null)
}

private async Task ShowOnboardingAsync()
{
await EnsureSetupWindowAsync();
}

private async Task<(SetupWindow? Window, bool CreatedNew)> EnsureSetupWindowAsync()
{
if (_settings == null)
return;
return (null, false);

if (_setupWindow != null)
{
var existingSetupWindow = _setupWindow;
await existingSetupWindow.WaitForInitialContentReadyAsync();
if (ReferenceEquals(_setupWindow, existingSetupWindow) && !existingSetupWindow.IsClosed)
existingSetupWindow.BringToFrontForSetupLaunch();
return;
return (existingSetupWindow, false);
}

try
Expand All @@ -3047,10 +3052,37 @@ private async Task ShowOnboardingAsync()
setupWindow.BringToFrontForSetupLaunch();
Logger.Info("Opened tray-hosted setup window");
}
return (setupWindow, true);
}
catch (Exception ex)
{
Logger.Error($"Failed to open setup window: {ex}");
return (null, false);
}
}

private async Task ShowGatewayWizardAsync()
{
var (setupWindow, createdNew) = await EnsureSetupWindowAsync();
if (setupWindow == null)
return;

// Only steer a freshly created setup window to the gateway wizard. An
// already-open setup window may be mid-install on ProgressPage, whose
// Unloaded handler cancels the running setup pipeline — navigating it
// away would abort an in-progress install. In that case leave the
// existing window on its current page (already brought to the front).
if (!createdNew)
{
Logger.Info("Setup window already open; skipping direct gateway wizard navigation to avoid interrupting active setup");
return;
}

await setupWindow.WaitForInitialContentReadyAsync();
if (ReferenceEquals(_setupWindow, setupWindow) && !setupWindow.IsClosed)
{
if (!setupWindow.TryNavigateToWizard())
Logger.Warn("Setup window is not ready for direct gateway wizard navigation; leaving current setup page visible");
}
}

Expand Down Expand Up @@ -3242,7 +3274,6 @@ private void OpenDashboard(string? path = null)

void IAppCommands.OpenDashboard(string? path) => OpenDashboard(path);
void IAppCommands.Navigate(string pageTag) => ShowHub(pageTag);
void IAppCommands.Navigate(string pageTag, string? originTag) => ShowHub(pageTag, originTag: originTag);
void IAppCommands.Reconnect() => _ = _connectionManager?.ReconnectAsync();
void IAppCommands.Disconnect()
{
Expand All @@ -3253,6 +3284,7 @@ void IAppCommands.Disconnect()
void IAppCommands.ShowChat() => ShowChatWindow();
void IAppCommands.CheckForUpdates() => _ = _updateCoordinator!.CheckForUpdatesUserInitiatedAsync();
void IAppCommands.ShowOnboarding() => _ = ShowOnboardingAsync();
void IAppCommands.ShowGatewayWizard() => _ = ShowGatewayWizardAsync();
void IAppCommands.ShowConnectionStatus() => ShowConnectionStatusWindow();
void IAppCommands.NotifySettingsSaved() => OnSettingsSaved(this, EventArgs.Empty);

Expand Down
1 change: 1 addition & 0 deletions src/OpenClaw.Tray.WinUI/Helpers/FluentIconCatalog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public static class FluentIconCatalog
public const string Clear = "\uE74D"; // Delete — clear/reset a buffer
public const string Develop = "\uE943"; // Code — engineering / explorations action
public const string AgentEvents = "\uE81C"; // History — agent events feed
public const string Doctor = "\uE95E"; // Health — "Run gateway doctor" health-check action

// ── Agents / Workspace surface ─────────────────────────────────
// Workspace concept (per-agent file viewer). Reuses the Folder
Expand Down
5 changes: 4 additions & 1 deletion src/OpenClaw.Tray.WinUI/Pages/ConfigPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Microsoft.UI.Xaml.Controls"
xmlns:toolkit="using:CommunityToolkit.WinUI.Controls"
xmlns:helpers="using:OpenClawTray.Helpers"
xmlns:local="using:OpenClawTray.Controls">

<Grid Padding="24,20,24,16">
Expand All @@ -18,7 +19,8 @@
</Grid.RowDefinitions>

<!-- Page header -->
<Grid Grid.Row="0" ColumnSpacing="16">
<StackPanel Grid.Row="0" Spacing="4">
<Grid ColumnSpacing="16">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
Expand All @@ -45,6 +47,7 @@
</StackPanel>
</Button>
</Grid>
</StackPanel>

<StackPanel Grid.Row="1" Spacing="8">
<InfoBar x:Name="ConnectionInfoBar"
Expand Down
1 change: 1 addition & 0 deletions src/OpenClaw.Tray.WinUI/Pages/ConfigPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using OpenClawTray.Controls;
using OpenClawTray.Helpers;
using OpenClawTray.Services;
using OpenClawTray.Windows;
using System;
using System.Collections.Generic;
using System.ComponentModel;
Expand Down
6 changes: 3 additions & 3 deletions src/OpenClaw.Tray.WinUI/Pages/ConnectionPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1848,12 +1848,12 @@ private void OnInstallLocalWslGateway(object sender, RoutedEventArgs e)

// ─── Operator card navigation ────────────────────────────────────

private void OnOpenSessions(object sender, RoutedEventArgs e) => ((IAppCommands)CurrentApp).Navigate("sessions", "connection");
private void OnOpenInstances(object sender, RoutedEventArgs e) => ((IAppCommands)CurrentApp).Navigate("instances", "connection");
private void OnOpenSessions(object sender, RoutedEventArgs e) => ((IAppCommands)CurrentApp).Navigate("sessions");
private void OnOpenInstances(object sender, RoutedEventArgs e) => ((IAppCommands)CurrentApp).Navigate("instances");

// ─── Node card navigation ────────────────────────────────────────

private void OnOpenPermissions(object sender, RoutedEventArgs e) => ((IAppCommands)CurrentApp).Navigate("permissions", "connection");
private void OnOpenPermissions(object sender, RoutedEventArgs e) => ((IAppCommands)CurrentApp).Navigate("permissions");

private void OnCopyNodeApproveCommand(object sender, RoutedEventArgs e)
{
Expand Down
26 changes: 26 additions & 0 deletions src/OpenClaw.Tray.WinUI/Pages/DebugPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,32 @@
</controls:InfoBar.Content>
</controls:InfoBar>

<!-- Gateway — shown only when the active gateway is an
app-managed WSL distro we can run commands in
(CanControlWslGateway). Hidden for SSH/remote
gateways. Visibility is driven from code-behind in
UpdateGatewayDoctorCard(). -->
<StackPanel x:Name="GatewayDoctorSection"
Spacing="{StaticResource DiagSettingsCardSpacing}"
Visibility="Collapsed">
<TextBlock x:Uid="DiagnosticsPage_Section_Gateway"
Text="Gateway"
Style="{StaticResource DiagSectionHeaderTextBlockStyle}"/>

<toolkit:SettingsCard x:Uid="DiagnosticsPage_Card_Doctor"
x:Name="GatewayDoctorCard"
Header="Run gateway doctor"
Description="Opens a terminal and runs openclaw doctor to check the local gateway for problems."
IsClickEnabled="True"
Click="OnRunGatewayDoctor"
AutomationProperties.AutomationId="DiagnosticsRunGatewayDoctor">
<toolkit:SettingsCard.HeaderIcon>
<FontIcon Glyph="{x:Bind helpers:FluentIconCatalog.Doctor, Mode=OneTime}"
FontFamily="{ThemeResource SymbolThemeFontFamily}"/>
</toolkit:SettingsCard.HeaderIcon>
</toolkit:SettingsCard>
</StackPanel>

<!-- Share diagnostics with support -->
<TextBlock x:Uid="DiagnosticsPage_Section_Share"
Text="Share diagnostics with support"
Expand Down
48 changes: 47 additions & 1 deletion src/OpenClaw.Tray.WinUI/Pages/DebugPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ public sealed partial class DebugPage : Page
private AppState? _appState;
private bool _suppressOverrideChange;

private IGatewayTerminalLauncher? _terminalLauncher;
private GatewayHostAccessPlan _doctorAccessPlan = GatewayHostAccessPlan.None();

private IGatewayTerminalLauncher TerminalLauncher =>
_terminalLauncher ??= new GatewayTerminalLauncher(new OpenClawTray.AppLogger());

private static readonly string LocalAppData = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "OpenClawTray");
private static readonly string LogPath = Path.Combine(LocalAppData, "openclaw-tray.log");
Expand Down Expand Up @@ -107,6 +113,7 @@ public void Initialize()
// (per docs/DATA_FLOW_ARCHITECTURE.md reactive-by-default ethos).
CurrentApp.SettingsChanged += OnSettingsChanged;
UpdateStatusInfoBar();
UpdateGatewayDoctorCard();
LoadDeviceIdentity();
LoadChatSurfaceOverrides();
}
Expand All @@ -118,11 +125,16 @@ private void OnAppStateChanged(object? sender, PropertyChangedEventArgs e)
case nameof(AppState.Status):
case nameof(AppState.GatewaySelf):
UpdateStatusInfoBar();
UpdateGatewayDoctorCard();
break;
}
}

private void OnSettingsChanged(object? sender, EventArgs e) => UpdateStatusInfoBar();
private void OnSettingsChanged(object? sender, EventArgs e)
{
UpdateStatusInfoBar();
UpdateGatewayDoctorCard();
}

/// <summary>
/// Reset detail-mode state when the user navigates to a different
Expand Down Expand Up @@ -179,6 +191,40 @@ private void UpdateStatusInfoBar()
private void OnManageOnConnection(object sender, RoutedEventArgs e)
=> ((IAppCommands)CurrentApp).Navigate("connection");

// ── Gateway doctor (app-managed WSL only) ────────────────────────

/// <summary>
/// Show the "Run gateway doctor" card only when the active gateway is an
/// app-managed WSL distro we can run commands in (CanControlWslGateway).
/// SSH/remote gateways have no such control surface, so the section stays
/// collapsed. Mirrors ConnectionPage's gateway-host gating.
/// </summary>
private void UpdateGatewayDoctorCard()
{
var activeRecord = CurrentApp.Registry?.GetActive();
_doctorAccessPlan = GatewayHostAccessClassifier.Classify(activeRecord);
GatewayDoctorSection.Visibility = _doctorAccessPlan.CanControlWslGateway
? Visibility.Visible
: Visibility.Collapsed;
}

private void OnRunGatewayDoctor(object sender, RoutedEventArgs e)
{
if (!_doctorAccessPlan.CanControlWslGateway)
{
return;
}

try
{
TerminalLauncher.OpenGatewayDoctor(_doctorAccessPlan);
}
catch (Exception ex)
{
OpenClawTray.Services.Logger.Warn($"[DebugPage] Failed to launch gateway doctor: {ex.Message}");
}
}

// ── Detail view (recent log) ─────────────────────────────────────

private void OnOpenEventTimeline(object sender, RoutedEventArgs e)
Expand Down
14 changes: 0 additions & 14 deletions src/OpenClaw.Tray.WinUI/Pages/InstancesPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,6 @@
<Grid HorizontalAlignment="Stretch">
<StackPanel Padding="24" Spacing="16" HorizontalAlignment="Stretch" MaxWidth="900">

<!-- Inline back link — only shown when the user arrived via a
cross-page link from Connection. Set by InstancesPage.Initialize
based on HubWindow.LastNavigationOrigin. -->
<HyperlinkButton x:Name="BackToConnectionLink"
Click="OnBackToConnectionClicked"
Padding="0" Margin="0,0,0,-8"
Visibility="Collapsed"
AutomationProperties.AutomationId="BackToConnectionLink">
<StackPanel Orientation="Horizontal" Spacing="6">
<FontIcon Glyph="&#xE72B;" FontSize="12"/>
<TextBlock x:Uid="InstancesPage_BackToConnectionText" Text="Back to Connection"/>
</StackPanel>
</HyperlinkButton>

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
Expand Down
11 changes: 0 additions & 11 deletions src/OpenClaw.Tray.WinUI/Pages/InstancesPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,6 @@ public InstancesPage()
/// <summary>Called by HubWindow when this page becomes the navigation target.</summary>
public void Initialize()
{
// Show "← Back to Connection" only when the user arrived from
// Connection's cross-page link; staying hidden when the rail nav
// is used keeps the page chrome quiet for direct navigation.
var hub = CurrentApp.ActiveHubWindow as HubWindow;
BackToConnectionLink.Visibility = hub?.LastNavigationOrigin == "connection"
? Visibility.Visible
: Visibility.Collapsed;

if (_appState != null) _appState.PropertyChanged -= OnAppStateChanged;
_appState = CurrentApp.AppState!;
_appState.PropertyChanged += OnAppStateChanged;
Expand All @@ -71,9 +63,6 @@ public void Initialize()
}
}

private void OnBackToConnectionClicked(object sender, RoutedEventArgs e)
=> ((IAppCommands)CurrentApp).Navigate("connection");

private void OnAppStateChanged(object? sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
Expand Down
14 changes: 0 additions & 14 deletions src/OpenClaw.Tray.WinUI/Pages/PermissionsPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,6 @@
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Padding="24" Spacing="8" HorizontalAlignment="Stretch" MaxWidth="900">

<!-- Inline back link — only shown when the user arrived via a
cross-page link from Connection. Set by PermissionsPage.Initialize
based on HubWindow.LastNavigationOrigin. -->
<HyperlinkButton x:Name="BackToConnectionLink"
Click="OnBackToConnectionClicked"
Padding="0" Margin="0,0,0,4"
Visibility="Collapsed"
AutomationProperties.AutomationId="BackToConnectionLink">
<StackPanel Orientation="Horizontal" Spacing="6">
<FontIcon Glyph="&#xE72B;" FontSize="12"/>
<TextBlock x:Uid="PermissionsPage_BackToConnection" Text="Back to Connection"/>
</StackPanel>
</HyperlinkButton>

<!-- ───────── Page header ───────── -->
<TextBlock x:Uid="PermissionsPage_Permissions" Text="Permissions"
Style="{StaticResource TitleTextBlockStyle}"
Expand Down
11 changes: 0 additions & 11 deletions src/OpenClaw.Tray.WinUI/Pages/PermissionsPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,6 @@ public void Initialize()
{
HostnameText.Text = Environment.MachineName;

// Show "← Back to Connection" only when the user arrived from
// Connection's cross-page link; staying hidden when the rail nav
// is used keeps the page chrome quiet for direct navigation.
var hub = CurrentApp.ActiveHubWindow as HubWindow;
BackToConnectionLink.Visibility = hub?.LastNavigationOrigin == "connection"
? Visibility.Visible
: Visibility.Collapsed;

BindNodeModeMaster();
BuildCapabilityToggles();
UpdateMcpStatus();
Expand All @@ -59,9 +51,6 @@ public void Initialize()
LoadAllowlist(CurrentApp.AppState?.Config);
}

private void OnBackToConnectionClicked(object sender, RoutedEventArgs e)
=> ((IAppCommands)CurrentApp).Navigate("connection");

private void OnLoaded(object sender, RoutedEventArgs e)
{
if (CurrentApp.Settings != null)
Expand Down
Loading