From d6c4f6c781372b1127a1acf16d46a02fd285ed75 Mon Sep 17 00:00:00 2001 From: "Kai Tao (from Dev Box)" Date: Thu, 3 Sep 2026 12:21:02 +0800 Subject: [PATCH 1/4] Replace AutoFix toggles with error handling dropdown Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/cascadia/TerminalApp/FreOverlay.cpp | 158 ++++++++-------- src/cascadia/TerminalApp/FreOverlay.h | 12 +- src/cascadia/TerminalApp/FreOverlay.xaml | 171 +++++++----------- .../Resources/af-ZA/Resources.resw | 49 +++-- .../Resources/am-ET/Resources.resw | 49 +++-- .../Resources/ar-SA/Resources.resw | 49 +++-- .../Resources/as-IN/Resources.resw | 45 +++-- .../Resources/az-Latn-AZ/Resources.resw | 49 +++-- .../Resources/bg-BG/Resources.resw | 49 +++-- .../Resources/bn-IN/Resources.resw | 45 +++-- .../Resources/bs-Latn-BA/Resources.resw | 49 +++-- .../Resources/ca-ES/Resources.resw | 49 +++-- .../Resources/ca-Es-VALENCIA/Resources.resw | 49 +++-- .../Resources/cs-CZ/Resources.resw | 49 +++-- .../Resources/cy-GB/Resources.resw | 49 +++-- .../Resources/da-DK/Resources.resw | 49 +++-- .../Resources/de-DE/Resources.resw | 47 +++-- .../Resources/el-GR/Resources.resw | 49 +++-- .../Resources/en-GB/Resources.resw | 49 +++-- .../Resources/en-US/Resources.resw | 47 ++--- .../Resources/es-ES/Resources.resw | 47 +++-- .../Resources/es-MX/Resources.resw | 49 +++-- .../Resources/et-EE/Resources.resw | 49 +++-- .../Resources/eu-ES/Resources.resw | 49 +++-- .../Resources/fa-IR/Resources.resw | 49 +++-- .../Resources/fi-FI/Resources.resw | 49 +++-- .../Resources/fil-PH/Resources.resw | 49 +++-- .../Resources/fr-CA/Resources.resw | 49 +++-- .../Resources/fr-FR/Resources.resw | 47 +++-- .../Resources/ga-IE/Resources.resw | 49 +++-- .../Resources/gd-gb/Resources.resw | 49 +++-- .../Resources/gl-ES/Resources.resw | 49 +++-- .../Resources/gu-IN/Resources.resw | 45 +++-- .../Resources/he-IL/Resources.resw | 49 +++-- .../Resources/hi-IN/Resources.resw | 45 +++-- .../Resources/hr-HR/Resources.resw | 49 +++-- .../Resources/hu-HU/Resources.resw | 49 +++-- .../Resources/hy-AM/Resources.resw | 49 +++-- .../Resources/id-ID/Resources.resw | 49 +++-- .../Resources/is-IS/Resources.resw | 49 +++-- .../Resources/it-IT/Resources.resw | 47 +++-- .../Resources/ja-JP/Resources.resw | 47 +++-- .../Resources/ka-GE/Resources.resw | 49 +++-- .../Resources/kk-KZ/Resources.resw | 49 +++-- .../Resources/km-KH/Resources.resw | 49 +++-- .../Resources/kn-IN/Resources.resw | 45 +++-- .../Resources/ko-KR/Resources.resw | 47 +++-- .../Resources/kok-IN/Resources.resw | 45 +++-- .../Resources/lb-LU/Resources.resw | 49 +++-- .../Resources/lo-LA/Resources.resw | 49 +++-- .../Resources/lt-LT/Resources.resw | 49 +++-- .../Resources/lv-LV/Resources.resw | 49 +++-- .../Resources/mi-NZ/Resources.resw | 49 +++-- .../Resources/mk-MK/Resources.resw | 49 +++-- .../Resources/ml-IN/Resources.resw | 45 +++-- .../Resources/mr-IN/Resources.resw | 45 +++-- .../Resources/ms-MY/Resources.resw | 49 +++-- .../Resources/mt-MT/Resources.resw | 49 +++-- .../Resources/nb-NO/Resources.resw | 49 +++-- .../Resources/ne-NP/Resources.resw | 45 +++-- .../Resources/nl-NL/Resources.resw | 49 +++-- .../Resources/nn-NO/Resources.resw | 49 +++-- .../Resources/or-IN/Resources.resw | 45 +++-- .../Resources/pa-IN/Resources.resw | 45 +++-- .../Resources/pl-PL/Resources.resw | 49 +++-- .../Resources/pt-BR/Resources.resw | 47 +++-- .../Resources/pt-PT/Resources.resw | 49 +++-- .../Resources/qps-ploc/Resources.resw | 47 +++-- .../Resources/qps-ploca/Resources.resw | 47 +++-- .../Resources/qps-plocm/Resources.resw | 47 +++-- .../Resources/quz-PE/Resources.resw | 49 +++-- .../Resources/ro-RO/Resources.resw | 49 +++-- .../Resources/ru-RU/Resources.resw | 47 +++-- .../Resources/sk-SK/Resources.resw | 49 +++-- .../Resources/sl-SI/Resources.resw | 49 +++-- .../Resources/sq-AL/Resources.resw | 49 +++-- .../Resources/sr-Cyrl-BA/Resources.resw | 49 +++-- .../Resources/sr-Cyrl-RS/Resources.resw | 47 +++-- .../Resources/sr-Latn-RS/Resources.resw | 49 +++-- .../Resources/sv-SE/Resources.resw | 49 +++-- .../Resources/ta-IN/Resources.resw | 45 +++-- .../Resources/te-IN/Resources.resw | 45 +++-- .../Resources/th-TH/Resources.resw | 49 +++-- .../Resources/tr-TR/Resources.resw | 49 +++-- .../Resources/tt-RU/Resources.resw | 49 +++-- .../Resources/ug-CN/Resources.resw | 49 +++-- .../Resources/uk-UA/Resources.resw | 49 +++-- .../Resources/ur-PK/Resources.resw | 45 +++-- .../Resources/uz-Latn-UZ/Resources.resw | 49 +++-- .../Resources/vi-VN/Resources.resw | 49 +++-- .../Resources/zh-CN/Resources.resw | 47 +++-- .../Resources/zh-TW/Resources.resw | 47 +++-- .../TerminalSettingsEditor/AIAgents.cpp | 5 +- .../TerminalSettingsEditor/AIAgents.xaml | 145 ++++++--------- .../AIAgentsViewModel.cpp | 98 +++++----- .../AIAgentsViewModel.h | 13 +- .../AIAgentsViewModel.idl | 13 +- .../TerminalSettingsEditor/MainPage.cpp | 7 +- .../Resources/de-DE/Resources.resw | 33 ++-- .../Resources/en-US/Resources.resw | 38 ++-- .../Resources/es-ES/Resources.resw | 33 ++-- .../Resources/fr-FR/Resources.resw | 33 ++-- .../Resources/it-IT/Resources.resw | 33 ++-- .../Resources/ja-JP/Resources.resw | 33 ++-- .../Resources/ko-KR/Resources.resw | 39 ++-- .../Resources/pt-BR/Resources.resw | 37 ++-- .../Resources/qps-ploc/Resources.resw | 35 ++-- .../Resources/qps-ploca/Resources.resw | 35 ++-- .../Resources/qps-plocm/Resources.resw | 35 ++-- .../Resources/ru-RU/Resources.resw | 37 ++-- .../Resources/sr-Cyrl-RS/Resources.resw | 37 ++-- .../Resources/uk-UA/Resources.resw | 33 ++-- .../Resources/zh-CN/Resources.resw | 39 ++-- .../Resources/zh-TW/Resources.resw | 33 ++-- .../GlobalAppSettings.cpp | 66 +++++-- .../TerminalSettingsModel/GlobalAppSettings.h | 59 ++++++ .../GlobalAppSettings.idl | 18 +- .../TerminalSettingsModel/MTSMSettings.h | 2 - .../TerminalSettingsSerializationHelpers.h | 9 + .../CustomAgentAndPolicyTests.cpp | 110 ++++++++--- 120 files changed, 3384 insertions(+), 2340 deletions(-) diff --git a/src/cascadia/TerminalApp/FreOverlay.cpp b/src/cascadia/TerminalApp/FreOverlay.cpp index 09c50e8dec..daa01ba759 100644 --- a/src/cascadia/TerminalApp/FreOverlay.cpp +++ b/src/cascadia/TerminalApp/FreOverlay.cpp @@ -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 { @@ -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. { @@ -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"))); @@ -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 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); + } + _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()) { @@ -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( @@ -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()) + { + return winrt::unbox_value(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()) + { + if (winrt::unbox_value(item.Tag()) == value) + { + combo.SelectedIndex(static_cast(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 ───────────────────────────────────────────────── @@ -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: @@ -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(); @@ -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")); @@ -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 @@ -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; @@ -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(); diff --git a/src/cascadia/TerminalApp/FreOverlay.h b/src/cascadia/TerminalApp/FreOverlay.h index dcbf93f78f..5aee501f40 100644 --- a/src/cascadia/TerminalApp/FreOverlay.h +++ b/src/cascadia/TerminalApp/FreOverlay.h @@ -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(); @@ -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. diff --git a/src/cascadia/TerminalApp/FreOverlay.xaml b/src/cascadia/TerminalApp/FreOverlay.xaml index 84940c6f6a..6402601f28 100644 --- a/src/cascadia/TerminalApp/FreOverlay.xaml +++ b/src/cascadia/TerminalApp/FreOverlay.xaml @@ -202,10 +202,12 @@ - + + - - - - - 16,12,16,12 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - + TextWrapping="Wrap"> + + + + + + + + + + + - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + +