Skip to content

Add settings for "notifying" on "activity"#20014

Open
carlos-zamora wants to merge 27 commits into
mainfrom
dev/cazamor/toast/activity
Open

Add settings for "notifying" on "activity"#20014
carlos-zamora wants to merge 27 commits into
mainfrom
dev/cazamor/toast/activity

Conversation

@carlos-zamora
Copy link
Copy Markdown
Member

@carlos-zamora carlos-zamora commented Mar 25, 2026

This PR implements the proposal in this comment. It adds 3 settings:

  • notifyOnActivity: send a "notification"(below) of some kind when an inactive tab has new output.
  • notifyOnNextPrompt: Similarly, but when we get a new prompt start (requires shell integration)
  • autoDetectRunningCommand: attempt to automatically detect the progress state of this profile

For the notifyOn* settings, they take any combination of the following flags:

  • tab: flash the taskbar
  • audible: emit an audible bell (This will use the sounds from bellSounds, if specified)
  • taskbar: display an icon in the tab (ala terminal activity indicator/alert when in background #7955 (comment) ) (basically, similar to the bell icon)
  • notification: send a desktop "toast" (read: notification). Clicking that notification will focus the tab that sent it

For the autoDetectRunningCommand setting, we support:

  • false: don't do this
  • true: (requires shell integration) if we got a FTCS_COMMAND_EXECUTED  (133;C), then any output, set our progress state to indeterminate

Validated using this config:

"defaults":
{
	"autoDetectRunningCommand": true,
	"autoMarkPrompts": true, // Needed even when your OMP config has shell integration enabled
	"rightClickContextMenu": true,
	"showMarksOnScrollbar": true
},
"list":
[
	{
		"commandline": "pwsh -nologo",
		"name": "Activity",
		"notifyOnActivity":
		[
			"taskbar",
			"audible",
			"tab",
			"notification"
		]
	},
	{
		"commandline": "pwsh -nologo",
		"name": "Next Prompt",
		"notifyOnNextPrompt":
		[
			"taskbar",
			"audible",
			"tab",
			"notification"
		]
	},

Heavily based on #19935
Co-authored by @zadjii-msft

@carlos-zamora carlos-zamora force-pushed the dev/cazamor/toast/base branch from 4357c17 to aeb531f Compare March 25, 2026 17:29
## Summary of the Pull Request
Targets #20010 

Manually assign an AUMID to our process when we're running unpackaged.
Main difference from #19937 is what AUMID we use. Before, it was per
branding, but the `WindowEmperor` already appends an exe path hash for
unpackaged instances to prevent crosstalk. Here, we're just using the
same pattern: `Microsoft.WindowsTerminal.<hash>`.

Heavily based on #19937
Co-authored by @zadjii-msft
@carlos-zamora carlos-zamora force-pushed the dev/cazamor/toast/activity branch from 47c0ae7 to 09c47c6 Compare March 25, 2026 21:51
@carlos-zamora carlos-zamora force-pushed the dev/cazamor/toast/activity branch from 09c47c6 to 98a4ccb Compare March 27, 2026 02:27
@carlos-zamora carlos-zamora marked this pull request as ready for review March 27, 2026 02:27
@carlos-zamora
Copy link
Copy Markdown
Member Author

Tested this out, it's pretty cool.
Reviewed it myself and using GHCP, everything looks good.

The only thing I can think of is the possibility of adding the experimental. prefix to these settings, but even then, I don't think that necessarily does anything. Like, when would it exit experimental?

So yeah, marked this as ready for review. Enjoy y'all! 😊

Comment thread src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw Outdated
Comment thread src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml Outdated
@carlos-zamora carlos-zamora force-pushed the dev/cazamor/toast/activity branch from 90fdd9b to fbb886e Compare April 17, 2026 02:05
Comment thread doc/cascadia/profiles.schema.json Outdated
"description": "Sets the sound played when the application emits a BEL. When set to an array, the terminal will pick one of those sounds at random.",
"$ref": "#/$defs/BellSound"
},
"notifyOnInactiveOutput": {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about a setting for duration? Ghostty has this as well. I may only want to see this after, say, 10s and not a command that maybe takes a second and I quickly flip away intending to come back in a few seconds. A longer job is more likely something we'll "walk away from" for a longer period instead of just flipping windows back and forth a bit.

If someone really wanted to be alerted for every command when the window is inactive, they can set this to 0 (or maybe that's the default).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... ghostty calls these settings activity-monitor and activity-monitor-threshold. So, we could name this notifyOnInactiveOutputThreshold and also have it take an int representing seconds (default of 0). Then just place them next to each other in the settings UI.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a higher default (IIRC, Ghostty's is 30s when enabled) might solve the other issue you mentioned, but you could also debounce notifications. If they come in within Xs (maybe also configurable), don't notify. Frankly, given the goal is to notify you some task is done - and this would seem to be a long-running task, IMO, since any quick task you're probably coming back to frequently - the "threshold" having a higher default like 30s would probably be sufficient - at least as a start.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"notifyOnInactiveOutput" sounds a bit weird to me and I think I'd prefer something like "notifyOnActivity". It not sending a notification when focused seems implicitly natural to me, so it probably doesn't need to be in the name.

@carlos-zamora
Copy link
Copy Markdown
Member Author

I was just using this feature with Copilot, and it was sending a ton of notifications as it was thinking.
At first, I was thinking that we should only let the session send one notification of this type, but now I'm not sure if that's the right approach. I'm particularly thinking of situations where the attached CLI app may send a bit of output, then wait, then send more. I guess in a situation like that, we definitely want a notification on the second one, and maybe on the first?

Thoughts? CC @DHowett @zadjii-msft

@zadjii-msft
Copy link
Copy Markdown
Member

IMO:

I think we need to send one notification when the tab gets activity and then no more until that pane is focused.
If it emits some text, and you don't focus it, and then five minutes later it emits some more text, we don't send a second notification. That pane is already in the "there was inactive activity" state. We don't need to send another notification until the user activates it (to dismiss that state)

Like, consider how the in-tab indicator would work. It's like the bell icon. "this tab did a thing" stays until the user activates it to dismiss that state.

lhecker
lhecker previously approved these changes Apr 22, 2026
Comment thread src/cascadia/TerminalApp/IPaneContent.idl Outdated
}
},
{
"type": "string",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the setting always used an array, I think that'd be fine too. That's just [two] extra characters after all.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping as is. You'd be surprised how many people forgot the [] back with keyChords haha.

page->Dispatcher().RunAsync(winrt::Windows::UI::Core::CoreDispatcherPriority::Normal, [weakPage{ page->get_weak() }, weakTab, weakContent]() {
if (const auto p{ weakPage.get() })
page->Dispatcher().RunAsync(winrt::Windows::UI::Core::CoreDispatcherPriority::Normal, [weakThis, weakTab, weakContent]() {
if (const auto p{ weakThis.get() })
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

personally i'd revert it, since it's unrelated - what makes it neat?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a lot easier to read.

Comment thread src/cascadia/TerminalApp/TerminalPaneContent.cpp Outdated
Comment thread src/cascadia/TerminalApp/TerminalPaneContent.cpp Outdated
Comment thread src/cascadia/TerminalControl/IControlSettings.idl
Comment thread src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp Outdated
@carlos-zamora carlos-zamora force-pushed the dev/cazamor/toast/activity branch from fbb886e to 6c2b796 Compare April 30, 2026 00:23
Base automatically changed from dev/cazamor/toast/base to main April 30, 2026 00:24
@carlos-zamora carlos-zamora dismissed lhecker’s stale review April 30, 2026 00:24

The base branch was changed.

@microsoft-github-policy-service microsoft-github-policy-service Bot added the Product-Terminal The new Windows Terminal. label Apr 30, 2026
Copy link
Copy Markdown
Member

@lhecker lhecker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love it! The only minor qualm I have is the naming of "notifyOnInactiveOutput".

@carlos-zamora

This comment was marked as resolved.

Copy link
Copy Markdown
Member

@DHowett DHowett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5/51 blocking for discussion...

Comment thread doc/cascadia/profiles.schema.json Outdated
Comment thread src/cascadia/TerminalApp/IPaneContent.idl Outdated
Comment thread src/cascadia/TerminalApp/Tab.cpp Outdated
@microsoft-github-policy-service microsoft-github-policy-service Bot added the Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something label May 4, 2026
Copy link
Copy Markdown
Member

@DHowett DHowett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

34/51

Comment thread src/cascadia/TerminalApp/TabHeaderControl.xaml
page->Dispatcher().RunAsync(winrt::Windows::UI::Core::CoreDispatcherPriority::Normal, [weakPage{ page->get_weak() }, weakTab, weakContent]() {
if (const auto p{ weakPage.get() })
page->Dispatcher().RunAsync(winrt::Windows::UI::Core::CoreDispatcherPriority::Normal, [weakThis, weakTab, weakContent]() {
if (const auto p{ weakThis.get() })
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

personally i'd revert it, since it's unrelated - what makes it neat?

Comment thread src/cascadia/TerminalControl/ControlCore.cpp
Comment thread src/cascadia/TerminalControl/ControlCore.h Outdated
Comment thread src/cascadia/TerminalControl/IControlSettings.idl Outdated
Comment thread src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw Outdated
Comment on lines +145 to +150
pair_type{ "none", AllClear },
pair_type{ "taskbar", ValueType::Taskbar },
pair_type{ "audible", ValueType::Audible },
pair_type{ "tab", ValueType::Tab },
pair_type{ "notification", ValueType::Notification },
pair_type{ "all", AllSet },
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this differ from the other notification styles we have? if so, why? if not, why have a separate type for it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good eye. The overlap is a bit weird here:

< investigation notes>

OutputNotificationStyle BellStyle
taskbar
audible
tab
notification
window
visual = window + taskbar

Note: OutputNotificationStyle is used by notifyOnActivity and notifyOnNextPrompt.

The question kinda becomes:

  • should notifyOnActivity/notifyOnNextPrompt support window?
    • I can see this being useful. It would be a more aggressive flash whenever the terminal session/control is visible. If it's a background tab, it shouldn't flash.
    • my opinion: ✅
  • should bellStyle support tab?
    • BEL seems critical enough to deserve a tab indicator. I don't think that should be configurable. My concern is specifically that someone accidentally omits tab and effectively silences BEL's visual indicator.
    • my opinion: ❌
    • Then, should tab be removed from notifyOnActivity/notifyOnNextPrompt?
      • No? A tab indicator seems valuable. It's an easy way to visually identify which tabs are requesting attention. It's a sensible way to opt-in for this setting/behavior, especially since it's disabled by default.
      • my opinion: ❌

Implementation-wise, we could consolidate these to be a single NotificationStyle enum in the backend. Then just omit the ones that aren't used in TerminalSettingsSerializationHelpers.h. The fix in the TerminalSettingsEditor side would be a bit more troublesome due to the EnumMappings, but doable.

</ investigation notes>

Though I like the idea of adding window for consistency, I don't think it's worth merging the enums.

Let me know what you think

CC @zadjii-msft (you may have opinions on https://github.com/microsoft/terminal/pull/20014/changes/BASE..b044fd661d730a70fdb11c0c0c5ee4b1e2ba20b6#r3185470645 too)


auto FromJson(const Json::Value& json)
{
if (json.isBool())
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a NEW SETTING, I do not believe it needs a fallback boolean mapper... right?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need? No.

That said, something like "notifyOnActivity": true and "notifyOnNextPrompt": true feels like a good QOL improvement. Much easier to read/write.

That said, true is pretty ambiguous here. IMO. There's a lot of flags, so on first glance, it's a bit overwhelming. I'd probably default to "all" and call it a day. Really this is indicative of a larger issue on how to simplify these settings, but I'll leave that for another day.

tldr: removing boolean support for now. I think there's value to having a simple boolean toggle, but I think that's a part of a broader discussion of how to simplify these settings more.

Comment thread src/cascadia/TerminalApp/TerminalPaneContent.cpp Outdated
@microsoft-github-policy-service microsoft-github-policy-service Bot removed the Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something label May 5, 2026
@carlos-zamora

This comment was marked as resolved.

@carlos-zamora
Copy link
Copy Markdown
Member Author

Weird interaction to note. When notifyOnActivity is enabled on a Copilot session, I get notified nonstop. I suspect that it's because Copilot is constantly writing to the buffer, so technically this feature is functioning "by design", but it's definitely not a good experience.

A "toggle notifying on activity" action could be useful here. Currently, I have to go into the settings and enable/disable notifications at a profile-level. But I'm finding that I don't like that level of customization that much. I generally only use one PowerShell profile, so I'm finding that I prefer session-level customization over profile-level ones.

@carlos-zamora
Copy link
Copy Markdown
Member Author

Similarly, for notifyOnNextPrompt running a simple one-off command like OpenConsole.slnx or code . will open the app, then immediately send out a desktop notification. Again, this is "by design", but I wonder if a better experience would be to opt-in for notifications when I know I'm going to be waiting for a while.

For both of these, Heath's suggestion of a minimum threshold are relevant. Should be easy to add too, so that's probably the next step of work to do in this PR.

Comment thread src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw Fixed
@github-actions

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area-Extensibility A feature that would ideally be fulfilled by us having an extension model. Area-UserInterface Issues pertaining to the user interface of the Console or Terminal Issue-Feature Complex enough to require an in depth planning process and actual budgeted, scheduled work. Product-Terminal The new Windows Terminal. zBugBash-Consider

Projects

None yet

6 participants