add "notifyOnActivityThreshold" & "notifyOnNextPromptThreshold"

This commit is contained in:
Carlos Zamora
2026-05-12 18:02:48 -07:00
parent 30020752ca
commit 5f38be735b
12 changed files with 116 additions and 0 deletions

View File

@@ -2924,6 +2924,18 @@
],
"description": "Controls how you are notified when a new shell prompt is detected. Requires shell integration."
},
"notifyOnActivityThreshold": {
"type": "integer",
"minimum": 0,
"default": 5,
"description": "Minimum number of seconds between consecutive activity notifications for this profile. Use this to suppress repeated notifications from chatty processes. The first notification after the pane has been silent always fires; subsequent notifications within this window are suppressed. Use 0 to always notify."
},
"notifyOnNextPromptThreshold": {
"type": "integer",
"minimum": 0,
"default": 5,
"description": "Suppress the next-prompt notification unless the just-finished command ran for at least this many seconds. Requires shell integration. Use 0 to always notify."
},
"autoDetectRunningCommand": {
"default": false,
"description": "Automatically detect when a command is running and show a progress indicator in the tab and taskbar.",

View File

@@ -44,6 +44,7 @@ namespace winrt::TerminalApp::implementation
_controlEvents._SetTaskbarProgress = _control.SetTaskbarProgress(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlSetTaskbarProgress });
_controlEvents._ReadOnlyChanged = _control.ReadOnlyChanged(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlReadOnlyChanged });
_controlEvents._FocusFollowMouseRequested = _control.FocusFollowMouseRequested(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlFocusFollowMouseRequested });
_controlEvents._OutputStarted = _control.OutputStarted(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlOutputStartedHandler });
_controlEvents._OutputBurstEnded = _control.OutputIdle(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlOutputBurstEndedHandler });
}
void TerminalPaneContent::_removeControlEvents()
@@ -329,12 +330,32 @@ namespace winrt::TerminalApp::implementation
const auto notifyStyle = _profile.NotifyOnNextPrompt();
if (notifyStyle != OutputNotificationStyle::None)
{
if (const auto thresholdInSeconds = _profile.NotifyOnNextPromptThreshold(); thresholdInSeconds > 0)
{
if (_lastOutputStartedAt == 0)
{
return;
}
if (const auto elapsedMs = GetTickCount64() - _lastOutputStartedAt; elapsedMs < (static_cast<uint64_t>(thresholdInSeconds) * 1000))
{
_lastOutputStartedAt = 0;
return;
}
}
_lastOutputStartedAt = 0;
NotificationRequested.raise(*this,
*winrt::make_self<TerminalApp::implementation::NotificationEventArgs>(notifyStyle, false));
}
}
}
void TerminalPaneContent::_controlOutputStartedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*eventArgs*/)
{
_lastOutputStartedAt = GetTickCount64();
}
// The underlying TermControl::OutputIdle event is fired on the trailing
// edge of a 100ms-debounced output burst (see ControlCore::Initialize).
// When "notifyOnActivity" is enabled, we get one event per burst of
@@ -347,6 +368,16 @@ namespace winrt::TerminalApp::implementation
const auto notifyStyle = _profile.NotifyOnActivity();
if (notifyStyle != OutputNotificationStyle::None)
{
const auto now = GetTickCount64();
const auto thresholdSeconds = _profile.NotifyOnActivityThreshold();
if (thresholdSeconds > 0 &&
_lastActivityNotificationAt != 0 &&
(now - _lastActivityNotificationAt) < (static_cast<uint64_t>(thresholdSeconds) * 1000))
{
return;
}
_lastActivityNotificationAt = now;
NotificationRequested.raise(*this,
*winrt::make_self<TerminalApp::implementation::NotificationEventArgs>(notifyStyle, false));
}

View File

@@ -82,11 +82,17 @@ namespace winrt::TerminalApp::implementation
winrt::Windows::Media::Playback::MediaPlayer _bellPlayer{ nullptr };
bool _bellPlayerCreated{ false };
// Tracks the GetTickCount64() for NotifyOnActivityThreshold
// and NotifyOnNextPromptThreshold respectively.
uint64_t _lastActivityNotificationAt{ 0 };
uint64_t _lastOutputStartedAt{ 0 };
struct ControlEventTokens
{
winrt::Microsoft::Terminal::Control::TermControl::ConnectionStateChanged_revoker _ConnectionStateChanged;
winrt::Microsoft::Terminal::Control::TermControl::WarningBell_revoker _WarningBell;
winrt::Microsoft::Terminal::Control::TermControl::PromptStarted_revoker _PromptStarted;
winrt::Microsoft::Terminal::Control::TermControl::OutputStarted_revoker _OutputStarted;
winrt::Microsoft::Terminal::Control::TermControl::OutputIdle_revoker _OutputBurstEnded;
winrt::Microsoft::Terminal::Control::TermControl::CloseTerminalRequested_revoker _CloseTerminalRequested;
winrt::Microsoft::Terminal::Control::TermControl::RestartTerminalRequested_revoker _RestartTerminalRequested;
@@ -107,6 +113,7 @@ namespace winrt::TerminalApp::implementation
void _controlWarningBellHandler(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& e);
void _controlPromptStartedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& eventArgs);
void _controlOutputStartedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& eventArgs);
void _controlOutputBurstEndedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& eventArgs);
void _controlReadOnlyChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& e);

View File

@@ -104,6 +104,8 @@ namespace Microsoft.Terminal.Control
OutputNotificationStyle NotifyOnActivity { get; };
OutputNotificationStyle NotifyOnNextPrompt { get; };
Int32 NotifyOnActivityThreshold { get; };
Int32 NotifyOnNextPromptThreshold { get; };
Boolean AutoDetectRunningCommand { get; };
// NOTE! When adding something here, make sure to update ControlProperties.h too!

View File

@@ -356,6 +356,8 @@ namespace winrt::Microsoft::Terminal::Settings
_DragDropDelimiter = profile.DragDropDelimiter();
_NotifyOnActivity = profile.NotifyOnActivity();
_NotifyOnNextPrompt = profile.NotifyOnNextPrompt();
_NotifyOnActivityThreshold = profile.NotifyOnActivityThreshold();
_NotifyOnNextPromptThreshold = profile.NotifyOnNextPromptThreshold();
_AutoDetectRunningCommand = profile.AutoDetectRunningCommand();
}

View File

@@ -165,6 +165,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
OBSERVABLE_PROJECTED_SETTING(_profile, DragDropDelimiter);
OBSERVABLE_PROJECTED_SETTING(_profile, NotifyOnActivity);
OBSERVABLE_PROJECTED_SETTING(_profile, NotifyOnNextPrompt);
OBSERVABLE_PROJECTED_SETTING(_profile, NotifyOnActivityThreshold);
OBSERVABLE_PROJECTED_SETTING(_profile, NotifyOnNextPromptThreshold);
OBSERVABLE_PROJECTED_SETTING(_profile, AutoDetectRunningCommand);
WINRT_PROPERTY(bool, IsBaseLayer, false);

View File

@@ -158,6 +158,8 @@ namespace Microsoft.Terminal.Settings.Editor
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AllowVtClipboardWrite);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Control.OutputNotificationStyle, NotifyOnActivity);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Control.OutputNotificationStyle, NotifyOnNextPrompt);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Int32, NotifyOnActivityThreshold);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Int32, NotifyOnNextPromptThreshold);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AutoDetectRunningCommand);
}
}

View File

@@ -218,6 +218,20 @@
</StackPanel>
</local:SettingContainer>
<!-- Notify On Activity Minimum Duration -->
<local:SettingContainer x:Name="NotifyOnActivityThreshold"
x:Uid="Profile_NotifyOnActivityThreshold"
ClearSettingValue="{x:Bind Profile.ClearNotifyOnActivityThreshold}"
HasSettingValue="{x:Bind Profile.HasNotifyOnActivityThreshold, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.NotifyOnActivityThresholdOverrideSource, Mode=OneWay}">
<muxc:NumberBox x:Uid="Profile_NotifyOnActivityThresholdBox"
LargeChange="10"
Minimum="0"
SmallChange="1"
Style="{StaticResource NumberBoxSettingStyle}"
Value="{x:Bind Profile.NotifyOnActivityThreshold, Mode=TwoWay}" />
</local:SettingContainer>
<!-- Notify On Next Prompt -->
<local:SettingContainer x:Name="NotifyOnNextPrompt"
x:Uid="Profile_NotifyOnNextPrompt"
@@ -238,6 +252,20 @@
</StackPanel>
</local:SettingContainer>
<!-- Notify On Next Prompt Minimum Duration -->
<local:SettingContainer x:Name="NotifyOnNextPromptThreshold"
x:Uid="Profile_NotifyOnNextPromptThreshold"
ClearSettingValue="{x:Bind Profile.ClearNotifyOnNextPromptThreshold}"
HasSettingValue="{x:Bind Profile.HasNotifyOnNextPromptThreshold, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.NotifyOnNextPromptThresholdOverrideSource, Mode=OneWay}">
<muxc:NumberBox x:Uid="Profile_NotifyOnNextPromptThresholdBox"
LargeChange="10"
Minimum="0"
SmallChange="1"
Style="{StaticResource NumberBoxSettingStyle}"
Value="{x:Bind Profile.NotifyOnNextPromptThreshold, Mode=TwoWay}" />
</local:SettingContainer>
<!-- Auto Detect Running Command -->
<local:SettingContainer x:Name="AutoDetectRunningCommand"
x:Uid="Profile_AutoDetectRunningCommand"

View File

@@ -1585,6 +1585,18 @@
<value>Choose how to be notified when a background tab produces new output.</value>
<comment>Help text for the notify on activity setting.</comment>
</data>
<data name="Profile_NotifyOnActivityThreshold.Header" xml:space="preserve">
<value>Minimum seconds between activity notifications</value>
<comment>Header for a numeric control that sets the minimum number of seconds between consecutive "notify on activity" notifications. Useful for chatty programs.</comment>
</data>
<data name="Profile_NotifyOnActivityThreshold.HelpText" xml:space="preserve">
<value>Suppress repeated activity notifications within this many seconds of the previous one. Set to 0 to always notify.</value>
<comment>Help text for the minimum-duration cooldown applied to "notify on activity" notifications.</comment>
</data>
<data name="Profile_NotifyOnActivityThresholdBox.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Minimum seconds between activity notifications</value>
<comment>Accessible name for a numeric control that sets the minimum number of seconds between consecutive "notify on activity" notifications.</comment>
</data>
<data name="Profile_NotifyOnNextPrompt.Header" xml:space="preserve">
<value>Notify on next prompt</value>
<comment>Header for a control to set the notification style when a new shell prompt is detected (requires shell integration).</comment>
@@ -1593,6 +1605,18 @@
<value>Choose how to be notified when a command finishes and a new prompt appears. Requires shell integration.</value>
<comment>Help text for the notify on next prompt setting.</comment>
</data>
<data name="Profile_NotifyOnNextPromptThreshold.Header" xml:space="preserve">
<value>Minimum duration for the next prompt notification</value>
<comment>Header for a numeric control that sets the minimum number of seconds a command must run before its completion triggers a "notify on next prompt" notification.</comment>
</data>
<data name="Profile_NotifyOnNextPromptThreshold.HelpText" xml:space="preserve">
<value>Only notify when the command ran for at least this many seconds. Requires shell integration. Set to 0 to always notify.</value>
<comment>Help text for the minimum-duration threshold applied to "notify on next prompt" notifications.</comment>
</data>
<data name="Profile_NotifyOnNextPromptThresholdBox.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Minimum duration for next prompt notification</value>
<comment>Accessible name for a numeric control that sets the minimum number of seconds a command must run before its completion triggers a "notify on next prompt" notification.</comment>
</data>
<data name="Profile_AutoDetectRunningCommand.Header" xml:space="preserve">
<value>Auto-detect running command</value>
<comment>Header for a control to configure automatic detection of a running command's progress state.</comment>

View File

@@ -112,6 +112,8 @@ Author(s):
X(Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, "pathTranslationStyle", Microsoft::Terminal::Control::PathTranslationStyle::None) \
X(Microsoft::Terminal::Control::OutputNotificationStyle, NotifyOnActivity, "notifyOnActivity", Microsoft::Terminal::Control::OutputNotificationStyle::None) \
X(Microsoft::Terminal::Control::OutputNotificationStyle, NotifyOnNextPrompt, "notifyOnNextPrompt", Microsoft::Terminal::Control::OutputNotificationStyle::None) \
X(int32_t, NotifyOnActivityThreshold, "notifyOnActivityThreshold", 5) \
X(int32_t, NotifyOnNextPromptThreshold, "notifyOnNextPromptThreshold", 5) \
X(bool, AutoDetectRunningCommand, "autoDetectRunningCommand", false)
// Intentionally omitted Profile settings:

View File

@@ -98,6 +98,8 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_PROFILE_SETTING(String, DragDropDelimiter);
INHERITABLE_PROFILE_SETTING(Microsoft.Terminal.Control.OutputNotificationStyle, NotifyOnActivity);
INHERITABLE_PROFILE_SETTING(Microsoft.Terminal.Control.OutputNotificationStyle, NotifyOnNextPrompt);
INHERITABLE_PROFILE_SETTING(Int32, NotifyOnActivityThreshold);
INHERITABLE_PROFILE_SETTING(Int32, NotifyOnNextPromptThreshold);
INHERITABLE_PROFILE_SETTING(Boolean, AutoDetectRunningCommand);
}
}

View File

@@ -90,5 +90,7 @@
X(winrt::Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, winrt::Microsoft::Terminal::Control::PathTranslationStyle::None) \
X(winrt::Microsoft::Terminal::Control::OutputNotificationStyle, NotifyOnActivity, winrt::Microsoft::Terminal::Control::OutputNotificationStyle::None) \
X(winrt::Microsoft::Terminal::Control::OutputNotificationStyle, NotifyOnNextPrompt, winrt::Microsoft::Terminal::Control::OutputNotificationStyle::None) \
X(int32_t, NotifyOnActivityThreshold, 5) \
X(int32_t, NotifyOnNextPromptThreshold, 5) \
X(bool, AutoDetectRunningCommand, false) \
X(winrt::hstring, DragDropDelimiter, L" ")