Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
158 changes: 81 additions & 77 deletions src/cascadia/TerminalApp/FreOverlay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Windows::UI::Xaml::Documents;
namespace Automation = winrt::Windows::UI::Xaml::Automation;
namespace Model = winrt::Microsoft::Terminal::Settings::Model;

namespace winrt::TerminalApp::implementation
{
Expand Down Expand Up @@ -202,8 +203,8 @@ namespace winrt::TerminalApp::implementation
WelcomeSubtitleLink().Text(RS_(L"FreOverlay_WelcomeSubtitleLink"));
SettingsSubtitlePrefix().Text(RS_(L"FreOverlay_SettingsSubtitlePrefix"));
SettingsSubtitleLink().Text(RS_(L"FreOverlay_SettingsSubtitleLink"));
AutoDetectShellIntegrationHintPrefix().Text(RS_(L"FreOverlay_AutoDetectShellIntegrationHintPrefix"));
AutoDetectShellIntegrationHintLink().Text(RS_(L"FreOverlay_AutoDetectShellIntegrationHintLink"));
AutoErrorHandlingShellIntegrationHintPrefix().Text(RS_(L"FreOverlay_AutoErrorHandlingShellIntegrationHintPrefix"));
AutoErrorHandlingShellIntegrationHintLink().Text(RS_(L"FreOverlay_AutoErrorHandlingShellIntegrationHintLink"));

// Split the description on "ACP" (locked token) so it can be rendered as an inline Hyperlink.
{
Expand All @@ -225,10 +226,6 @@ namespace winrt::TerminalApp::implementation
}

// Set toggle On/Off labels
AutoDetectToggle().OnContent(winrt::box_value(RS_(L"FreOverlay_ToggleOn")));
AutoDetectToggle().OffContent(winrt::box_value(RS_(L"FreOverlay_ToggleOff")));
AutoErrorToggle().OnContent(winrt::box_value(RS_(L"FreOverlay_ToggleOn")));
AutoErrorToggle().OffContent(winrt::box_value(RS_(L"FreOverlay_ToggleOff")));
ShowTokenUsageAndCostToggle().OnContent(winrt::box_value(RS_(L"FreOverlay_ToggleOn")));
ShowTokenUsageAndCostToggle().OffContent(winrt::box_value(RS_(L"FreOverlay_ToggleOff")));
SessionManagementToggle().OnContent(winrt::box_value(RS_(L"FreOverlay_ToggleOn")));
Expand Down Expand Up @@ -263,31 +260,37 @@ namespace winrt::TerminalApp::implementation
else if (currentPos == L"top") PanePositionComboBox().SelectedIndex(3);
else PanePositionComboBox().SelectedIndex(0); // default: bottom

// Set toggles from current settings, respecting GPO policy.
// Detection drives the suggestion toggle's enabled state (see
// _UpdateSuggestionEnabledState), so configure it first.
AutoDetectToggle().IsOn(globals.EffectiveAutoErrorDetectionEnabled());
auto handlingItems = AutoErrorHandlingComboBox().Items();
handlingItems.Clear();
const std::pair<winrt::hstring, Model::AutoErrorHandling> handlingOptions[] = {
{ RS_(L"FreOverlay_AutoErrorHandling_Off"), Model::AutoErrorHandling::Off },
{ RS_(L"FreOverlay_AutoErrorHandling_DetectErrorsAutomatically"), Model::AutoErrorHandling::DetectErrorsAutomatically },
{ RS_(L"FreOverlay_AutoErrorHandling_DetectErrorsAndSendToAgentForFixesAutomatically"), Model::AutoErrorHandling::DetectErrorsAndSendToAgentForFixesAutomatically },
};
for (const auto& [label, value] : handlingOptions)
{
ComboBoxItem item;
item.Content(winrt::box_value(label));
item.Tag(winrt::box_value(value));
if (value == Model::AutoErrorHandling::DetectErrorsAndSendToAgentForFixesAutomatically &&
globals.IsAutoErrorHandlingPolicyRestricted())
{
item.IsEnabled(false);
}
handlingItems.Append(item);
}
Comment on lines +270 to +281
_SelectAutoErrorHandling(globals.EffectiveAutoErrorHandling());
_UpdateAutoErrorHandlingHint();

// Master-detail: EffectiveAutoFixEnabled already returns false when
// detection is off, so the suggestion toggle starts consistent with the
// master toggle (and reflects the stored preference when detection is
// on).
AutoErrorToggle().IsOn(globals.EffectiveAutoFixEnabled());
ShowTokenUsageAndCostToggle().IsOn(globals.ShowTokenUsageAndCost());
if (globals.IsAutoFixPolicyLocked())
if (globals.IsAutoErrorHandlingPolicyRestricted())
{
const auto policyText = RS_(L"FreOverlay_PolicyLocked");
AutoErrorPolicyNotice().Text(policyText);
AutoErrorPolicyNotice().Visibility(Visibility::Visible);
// Accessibility: explain why the toggle is disabled
Automation::AutomationProperties::SetHelpText(AutoErrorToggle(), policyText);
AutoErrorHandlingPolicyNotice().Text(policyText);
AutoErrorHandlingPolicyNotice().Visibility(Visibility::Visible);
Automation::AutomationProperties::SetHelpText(AutoErrorHandlingComboBox(), policyText);
}

// Apply the detection→suggestion dependency once both toggles are
// configured (also covers the GPO-locked case via the policy check
// inside the helper).
_UpdateSuggestionEnabledState();

// Session management toggle — honour AllowAgentSessionHooks GPO
if (globals.IsAgentSessionHooksPolicyLocked())
{
Expand All @@ -308,9 +311,7 @@ namespace winrt::TerminalApp::implementation
Automation::AutomationProperties::SetName(
SettingsPage(), RS_(L"FreOverlay_SettingsTitle/Text"));
Automation::AutomationProperties::SetName(
AutoDetectToggle(), RS_(L"FreOverlay_AutoDetectLabel/Text"));
Automation::AutomationProperties::SetName(
AutoErrorToggle(), RS_(L"FreOverlay_AutoErrorLabel/Text"));
AutoErrorHandlingComboBox(), RS_(L"FreOverlay_AutoErrorHandlingLabel/Text"));
Automation::AutomationProperties::SetName(
ShowTokenUsageAndCostToggle(), RS_(L"FreOverlay_ShowTokenUsageAndCostLabel/Text"));
Automation::AutomationProperties::SetName(
Expand Down Expand Up @@ -376,48 +377,52 @@ namespace winrt::TerminalApp::implementation
}
}

// ── Detection → suggestion dependency ───────────────────────────────

void FreOverlay::_OnAutoDetectToggled(const IInspectable& /*sender*/,
const RoutedEventArgs& /*args*/)
void FreOverlay::_OnAutoErrorHandlingSelectionChanged(
const IInspectable& /*sender*/,
const SelectionChangedEventArgs& /*args*/)
{
_UpdateSuggestionEnabledState();
_UpdateAutoErrorHandlingHint();
}

// Hide/show the whole hint row (icon + text) — the (i) glyph would
// otherwise dangle when detection is off and the side-effect described
// by the hint no longer applies. Mirrors SessionManagementHintRow.
auto toggle = AutoDetectToggle();
auto row = AutoDetectShellIntegrationHintRow();
if (toggle && row)
Model::AutoErrorHandling FreOverlay::_SelectedAutoErrorHandling()
{
if (const auto combo = AutoErrorHandlingComboBox())
{
row.Visibility(toggle.IsOn() ? Visibility::Visible : Visibility::Collapsed);
if (const auto item = combo.SelectedItem().try_as<ComboBoxItem>())
{
return winrt::unbox_value<Model::AutoErrorHandling>(item.Tag());
}
}
return Model::AutoErrorHandling::Off;
}

void FreOverlay::_UpdateSuggestionEnabledState()
void FreOverlay::_SelectAutoErrorHandling(const Model::AutoErrorHandling value)
{
// Guard: Toggled can fire during InitializeComponent before the
// sibling control exists.
auto detect = AutoDetectToggle();
auto suggest = AutoErrorToggle();
if (!detect || !suggest)
if (const auto combo = AutoErrorHandlingComboBox())
{
return;
for (uint32_t i = 0; i < combo.Items().Size(); ++i)
{
if (const auto item = combo.Items().GetAt(i).try_as<ComboBoxItem>())
{
if (winrt::unbox_value<Model::AutoErrorHandling>(item.Tag()) == value)
{
combo.SelectedIndex(static_cast<int32_t>(i));
return;
}
}
}
combo.SelectedIndex(0);
}
}

const bool detectionOn = detect.IsOn();
const bool autoFixLocked = _settings && _settings.GlobalSettings().IsAutoFixPolicyLocked();

// Master-detail: detection off ⇒ turn the suggestion off and disable it
// (can't configure a suggestion you can't detect).
// Detection on ⇒ re-enable it; its On/Off is the stored preference
// (set on init), so re-enabling doesn't force it on. The auto-fix GPO
// can still lock it off.
if (!detectionOn)
void FreOverlay::_UpdateAutoErrorHandlingHint()
{
if (const auto row = AutoErrorHandlingShellIntegrationHintRow())
{
suggest.IsOn(false);
row.Visibility(_SelectedAutoErrorHandling() == Model::AutoErrorHandling::Off ?
Visibility::Collapsed :
Visibility::Visible);
}
suggest.IsEnabled(detectionOn && !autoFixLocked);
}

// ── Page navigation ─────────────────────────────────────────────────
Expand Down Expand Up @@ -1215,27 +1220,23 @@ namespace winrt::TerminalApp::implementation
ErrorText().Text(RS_(L"FreOverlay_InstallErrorShellIntegrationExecutionPolicy"));
url += L"#41-powershell";
// Same remediation as generic shell-integration failure: turn
// off error detection so the user can save and continue. Once
// off Auto error handling so the user can save and continue. Once
// they fix execution policy they can re-enable it from Settings.
AutoDetectToggle().IsOn(false);
_UpdateSuggestionEnabledState();
_SelectAutoErrorHandling(Model::AutoErrorHandling::Off);
if (_settings)
{
_settings.GlobalSettings().AutoErrorDetectionEnabled(false);
_settings.GlobalSettings().AutoFixEnabled(false);
_settings.GlobalSettings().AutoErrorHandling(Model::AutoErrorHandling::Off);
}
break;
case FreProblemKind::ShellIntegration:
ErrorText().Text(RS_(L"FreOverlay_InstallErrorShellIntegration"));
url += L"#4-shell-integration";
// Remediation: turn off error detection (and its dependent
// suggestion) so the user can save and continue without it.
AutoDetectToggle().IsOn(false);
_UpdateSuggestionEnabledState();
// Remediation: turn off Auto error handling so the user can save
// and continue without shell integration.
_SelectAutoErrorHandling(Model::AutoErrorHandling::Off);
if (_settings)
{
_settings.GlobalSettings().AutoErrorDetectionEnabled(false);
_settings.GlobalSettings().AutoFixEnabled(false);
_settings.GlobalSettings().AutoErrorHandling(Model::AutoErrorHandling::Off);
}
break;
case FreProblemKind::Hooks:
Expand Down Expand Up @@ -1428,14 +1429,14 @@ namespace winrt::TerminalApp::implementation
agentId = entry.Id();
}
}
const auto autoErrorHandling = _SelectedAutoErrorHandling();

if (_settings)
{
const auto& globals = _settings.GlobalSettings();
globals.AcpAgent(agentId);
globals.DelegateAgent(agentId);
globals.AutoErrorDetectionEnabled(AutoDetectToggle().IsOn());
globals.AutoFixEnabled(AutoErrorToggle().IsOn());
globals.AutoErrorHandling(autoErrorHandling);
globals.ShowTokenUsageAndCost(ShowTokenUsageAndCostToggle().IsOn());

const auto posIdx = PanePositionComboBox().SelectedIndex();
Expand All @@ -1457,12 +1458,15 @@ namespace winrt::TerminalApp::implementation
// 3. Install prerequisites if needed (blocking — cannot proceed without these)
const bool needsCopilot = (agentId == L"copilot") && !_IsAgentInstalled(L"copilot");
const bool needsNode = (agentId == L"claude" || agentId == L"codex") && !_IsNodeInstalled();
const auto autoErrorHandlingName =
autoErrorHandling == Model::AutoErrorHandling::Off ? "off" :
autoErrorHandling == Model::AutoErrorHandling::DetectErrorsAutomatically ? "detectErrorsAutomatically" :
"detectErrorsAndSendToAgentForFixesAutomatically";

_agentPaneLog("[FRE] Save: agent=" + winrt::to_string(agentId)
+ " needsCopilot=" + (needsCopilot ? "y" : "n")
+ " needsNode=" + (needsNode ? "y" : "n")
+ " detect=" + (AutoDetectToggle().IsOn() ? "on" : "off")
+ " suggest=" + (AutoErrorToggle().IsOn() ? "on" : "off")
+ " autoErrorHandling=" + autoErrorHandlingName
+ " tokenUsageAndCost=" + (ShowTokenUsageAndCostToggle().IsOn() ? "on" : "off")
+ " hooks=" + (SessionManagementToggle().IsOn() ? "on" : "off"));

Expand Down Expand Up @@ -1626,7 +1630,7 @@ namespace winrt::TerminalApp::implementation
// Helper internally does co_await winrt::resume_background(),
// so the continuation may resume on a thread-pool thread.
// Hop back to the UI thread before the subsequent
// AutoDetectToggle().IsOn() read and any later _ShowProblem
// AutoErrorHandlingComboBox selection read and any later _ShowProblem
// call. Without this, XAML access from the thread pool
// throws RPC_E_WRONG_THREAD, which IAsyncAction swallows —
// the SavingOverlay would then be stuck with no error
Expand All @@ -1642,8 +1646,8 @@ namespace winrt::TerminalApp::implementation
}
}

// 5. Shell integration — only when error detection is enabled.
if (AutoDetectToggle().IsOn())
// 5. Both enabled auto-error-handling modes require shell integration.
if (_SelectedAutoErrorHandling() != Model::AutoErrorHandling::Off)
{
auto self = weak.get();
if (!self) co_return;
Expand Down Expand Up @@ -1804,7 +1808,7 @@ namespace winrt::TerminalApp::implementation

// Guard against being called before InitializeComponent has populated
// the named XAML elements — matches the pattern used elsewhere in
// this file (see _UpdateSuggestionEnabledState, _OnAutoDetectToggled).
// this file (see _UpdateAutoErrorHandlingHint).
auto scroller = SettingsFormScroller();
auto overlay = SavingOverlay();
auto ring = SavingProgressRing();
Expand Down
12 changes: 6 additions & 6 deletions src/cascadia/TerminalApp/FreOverlay.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ namespace winrt::TerminalApp::implementation
const winrt::Windows::UI::Xaml::Controls::SelectionChangedEventArgs& args);
void _OnSessionManagementToggled(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::UI::Xaml::RoutedEventArgs& args);
void _OnAutoDetectToggled(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::UI::Xaml::RoutedEventArgs& args);
void _OnAutoErrorHandlingSelectionChanged(
const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::UI::Xaml::Controls::SelectionChangedEventArgs& args);

// No-op kept for IDL compatibility.
void ResetDragOffset();
Expand Down Expand Up @@ -119,10 +120,9 @@ namespace winrt::TerminalApp::implementation
// editing, and parks focus on the help link.
void _FinalizeProblemDisplay(const std::wstring& url);

// Apply the detection→suggestion master-detail dependency: detection
// off turns the suggestion toggle off and disables it; detection on
// re-enables it (preserving the stored value).
void _UpdateSuggestionEnabledState();
winrt::Microsoft::Terminal::Settings::Model::AutoErrorHandling _SelectedAutoErrorHandling();
void _SelectAutoErrorHandling(winrt::Microsoft::Terminal::Settings::Model::AutoErrorHandling value);
void _UpdateAutoErrorHandlingHint();

// (Re)build the agent dropdown from the GPO-filtered registry, labeling
// each entry with its live install state. Safe to call repeatedly (e.g.
Expand Down
Loading
Loading