summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStanton Burchett <sburchett@medallia.com>2026-07-30 14:51:22 -0600
committerStanton Burchett <sburchett@medallia.com>2026-07-30 14:51:22 -0600
commit46e3650242cfe38941eb747e601484bd19c137bd (patch)
tree16a1df66b7fead0e8ee1838e4b02179f5d2360b5
parent64b564b52ba7d22ba8129821ab4b30542d971ff5 (diff)
Add OSC 777 support and clean up the notification title
Support OSC 777 ; notify ; title ; body (the rxvt/urxvt notification form) so a program can set an explicit title and body, with the semicolon-safe body rejoined via slice(2).join(";"). Clean the OSC 9 title: prefer a user-set custom tab name, and reduce filesystem-path titles to their last segment so shell tabs no longer show a long, truncated cwd.
-rw-r--r--README.md12
-rw-r--r--package.json2
-rw-r--r--src/osc-notify-decorator.ts2
-rw-r--r--src/osc-notify-middleware.ts26
4 files changed, 35 insertions, 7 deletions
diff --git a/README.md b/README.md
index c6805a4..7ba1514 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,18 @@ Clicking the notification brings Tabby to the front and switches to the exact
tab/pane that emitted the sequence. The notification title reflects the tab's
current title at the time the sequence is received.
+The OSC 9 title is cleaned up: it prefers a user-set custom tab name, and when
+the title looks like a filesystem path (e.g. a shell tab showing its cwd) only
+the last path segment is used instead of the full, often-truncated path.
+
+`OSC 777 ; notify ; title ; body` (the rxvt/urxvt notification form) is also
+supported, letting a program set an explicit title and body:
+
+```sh
+# creates a notification titled 'Build' with body 'Done'
+printf "\033]777;notify;Build;Done\a"
+```
+
### Example commands
```sh
diff --git a/package.json b/package.json
index 52c828b..ed2739d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "tabby-osc-notify",
- "version": "1.0.0",
+ "version": "1.1.0",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"scripts": {
diff --git a/src/osc-notify-decorator.ts b/src/osc-notify-decorator.ts
index abfb796..4beb029 100644
--- a/src/osc-notify-decorator.ts
+++ b/src/osc-notify-decorator.ts
@@ -28,7 +28,7 @@ export default class OscNotifyDecorator extends TerminalDecorator {
private attachToSession(session: BaseSession, tab: BaseTerminalTabComponent<any>) {
const middleware = new OscNotifyMiddleware(
- () => (<any>tab).title,
+ () => (<any>tab).customTitle || (<any>tab).title,
() => {
this.hostWindow.bringToFront();
const parent = this.app.getParentTab(tab);
diff --git a/src/osc-notify-middleware.ts b/src/osc-notify-middleware.ts
index ad2946c..dcfcc1a 100644
--- a/src/osc-notify-middleware.ts
+++ b/src/osc-notify-middleware.ts
@@ -4,6 +4,16 @@ import { Observable, Subject } from "rxjs";
const oscPrefix = Buffer.from("\x1b]");
const oscSuffix = Buffer.from("\x07");
+function cleanTitle(raw: string): string {
+ const t = (raw ?? "").trim();
+ if (!t) return "Terminal";
+ if (t.includes("/") || t.includes("\\")) {
+ const segs = t.split(/[/\\]/).filter(s => s.length > 0);
+ return segs.length ? segs[segs.length - 1] : t;
+ }
+ return t;
+}
+
export default class OscNotifyMiddleware extends SessionMiddleware {
getTitle: () => string;
onClick: () => void;
@@ -39,11 +49,11 @@ export default class OscNotifyMiddleware extends SessionMiddleware {
const oscCode = parseInt(oscCodeString);
if (oscCode === 9) {
- const notification = new Notification(this.getTitle() || "Terminal", {
- body: oscParams[0]
- });
- notification.onclick = () => this.onClick();
- this.notifications.push(notification);
+ this.showNotification(cleanTitle(this.getTitle()), oscParams[0] ?? "");
+ } else if (oscCode === 777 && oscParams[0] === "notify") {
+ const title = oscParams[1] || cleanTitle(this.getTitle());
+ const body = oscParams.slice(2).join(";");
+ this.showNotification(title, body);
} else {
continue;
}
@@ -52,6 +62,12 @@ export default class OscNotifyMiddleware extends SessionMiddleware {
super.feedFromSession(data);
}
+ private showNotification(title: string, body: string): void {
+ const notification = new Notification(title || "Terminal", { body });
+ notification.onclick = () => this.onClick();
+ this.notifications.push(notification);
+ }
+
close(): void {
this.notifications.forEach((notification) => notification.close());
this.notifications = [];