diff --git a/src/extensionsIntegrated/Terminal/main.js b/src/extensionsIntegrated/Terminal/main.js
index 2a5bcd554..fda71c4aa 100644
--- a/src/extensionsIntegrated/Terminal/main.js
+++ b/src/extensionsIntegrated/Terminal/main.js
@@ -42,6 +42,7 @@ define(function (require, exports, module) {
const Menus = require("command/Menus");
const Commands = require("command/Commands");
+ const KeyBindingManager = require("command/KeyBindingManager");
const NotificationUI = require("widgets/NotificationUI");
const TerminalInstance = require("./TerminalInstance");
const ShellProfiles = require("./ShellProfiles");
@@ -90,6 +91,7 @@ define(function (require, exports, module) {
let processInfo = {}; // id -> processName from PTY
let originalDefaultShellName = null; // System-detected default shell name
let _focusToastShown = false; // Show focus hint toast only once per session
+ let _clearHintShown = false; // Show clear buffer hint toast only once per session
let $panel, $contentArea, $shellDropdown, $flyoutList;
/**
@@ -145,8 +147,41 @@ define(function (require, exports, module) {
// Dropdown chevron button toggles shell selector
$panel.find(".terminal-flyout-dropdown-btn").on("click", _onDropdownButtonClick);
- // Refresh process info when user hovers over the flyout
- $panel.find(".terminal-tab-flyout").on("mouseenter", _refreshAllProcesses);
+ // When the terminal is focused, prevent Phoenix keybindings from
+ // stealing keys that should go to the shell (e.g. Ctrl+L for clear).
+ // The EDITOR_SHORTCUTS list in TerminalInstance.js already defines which
+ // Ctrl combos should pass through to Phoenix; everything else should
+ // reach xterm/the PTY.
+ KeyBindingManager.addGlobalKeydownHook(function (event) {
+ if (event.type !== "keydown") {
+ return false;
+ }
+ // Only intercept when a terminal textarea is focused
+ const el = document.activeElement;
+ if (!el || !$contentArea[0].contains(el)) {
+ return false;
+ }
+ // Let the terminal handle Ctrl/Cmd key combos that aren't
+ // reserved for the editor (those are handled by TerminalInstance's
+ // _customKeyHandler which returns false for them).
+ const ctrlOrMeta = event.ctrlKey || event.metaKey;
+ const key = event.key.toLowerCase();
+ if (ctrlOrMeta && !event.shiftKey && key === "l") {
+ _showClearBufferHintToast();
+ return true; // Block Phoenix, let xterm handle Ctrl+L
+ }
+ // Ctrl+K (Cmd+K on mac): clear terminal scrollback
+ if (ctrlOrMeta && !event.shiftKey && key === "k") {
+ event.preventDefault();
+ _clearActiveTerminal();
+ return true;
+ }
+ return false;
+ });
+
+ // Refresh process info when the tab bar gains focus or mouse enters
+ $panel.find(".terminal-tab-bar").on("mouseenter", _refreshAllProcesses);
+ $panel.find(".terminal-tab-bar").on("focusin", _refreshAllProcesses);
// Listen for panel resize
WorkspaceManager.on("workspaceUpdateLayout", _handleResize);
@@ -155,6 +190,7 @@ define(function (require, exports, module) {
const PanelView = require("view/PanelView");
PanelView.on(PanelView.EVENT_PANEL_SHOWN, function (_event, panelId) {
if (panelId === PANEL_ID) {
+ _updateTabBarMode();
const active = _getActiveTerminal();
if (active) {
active.handleResize();
@@ -333,6 +369,7 @@ define(function (require, exports, module) {
}
_updateFlyout();
+ _refreshAllProcesses();
}
/**
@@ -586,14 +623,23 @@ define(function (require, exports, module) {
}
}
+ /**
+ * Update the expanded/collapsed tab bar class based on panel width
+ */
+ function _updateTabBarMode() {
+ $panel.toggleClass("terminal-tabs-expanded", $panel.width() >= 840);
+ }
+
/**
* Handle workspace resize
*/
function _handleResize() {
+ _updateTabBarMode();
const active = _getActiveTerminal();
if (active) {
active.handleResize();
}
+ _refreshAllProcesses();
}
/**
@@ -622,6 +668,24 @@ define(function (require, exports, module) {
});
}
+ /**
+ * Show a one-time toast hint about Ctrl/Cmd+K to clear terminal buffer
+ */
+ function _showClearBufferHintToast() {
+ if (_clearHintShown) {
+ return;
+ }
+ _clearHintShown = true;
+
+ const isMac = brackets.platform === "mac";
+ const shortcutKey = isMac ? 'Cmd+K' : 'Ctrl+K';
+ const message = StringUtils.format(Strings.TERMINAL_CLEAR_BUFFER_HINT, shortcutKey);
+ NotificationUI.showToastOn($contentArea[0], message, {
+ autoCloseTimeS: 5,
+ dismissOnClick: true
+ });
+ }
+
/**
* Escape HTML special characters
*/
@@ -814,6 +878,7 @@ define(function (require, exports, module) {
if (Phoenix.isTestWindow) {
exports._getActiveTerminal = _getActiveTerminal;
+ exports._refreshAllProcesses = _refreshAllProcesses;
/**
* Write data to the active terminal's PTY. Test-only helper.
diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js
index aaa44a8fb..0b8ac5a1b 100644
--- a/src/nls/root/strings.js
+++ b/src/nls/root/strings.js
@@ -1784,6 +1784,7 @@ define({
"TERMINAL_CLOSE_ALL_STOP_BTN": "Close All & Stop Processes",
"TERMINAL_FOCUS_HINT": "Press {0} to switch between editor and terminal",
"TERMINAL_CLEAR": "Clear Terminal",
+ "TERMINAL_CLEAR_BUFFER_HINT": "💡 Press {0} to clear terminal buffer",
"EXTENDED_COMMIT_MESSAGE": "EXTENDED",
"GETTING_STAGED_DIFF_PROGRESS": "Getting diff of staged files\u2026",
"GIT_COMMIT": "Git commit\u2026",
diff --git a/src/styles/Extn-Terminal.less b/src/styles/Extn-Terminal.less
index ac253797c..3d2a1033b 100644
--- a/src/styles/Extn-Terminal.less
+++ b/src/styles/Extn-Terminal.less
@@ -96,35 +96,44 @@
position: relative;
}
-/* Tab bar: 30px flex spacer, flyout sits inside absolutely */
+/* Tab bar: static sidebar, width switches via .terminal-tabs-expanded on container */
.terminal-tab-bar {
position: relative;
- width: 30px;
- min-width: 30px;
- overflow: visible;
+ width: 60px;
+ min-width: 60px;
+}
+
+.terminal-tabs-expanded .terminal-tab-bar {
+ width: 159px;
+ min-width: 159px;
}
-/* ─── Unified flyout: collapses to 30px, expands on hover ─── */
+/* ─── Tab sidebar: static, no flyout/hover ─── */
.terminal-tab-flyout {
display: flex;
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- width: 30px;
+ position: relative;
+ width: 100%;
+ height: 100%;
background: var(--terminal-tab-bg);
border-left: 1px solid var(--terminal-border);
- z-index: 20;
flex-direction: column;
- transition: width 0.08s ease;
+ overflow: hidden;
}
-.terminal-tab-flyout:hover {
- width: 170px;
- box-shadow: -2px 0 8px rgba(0, 0, 0, 0.3);
+/* Fade gradient at bottom of tab list to hint overflow */
+.terminal-tab-flyout::after {
+ content: '';
+ position: absolute;
+ bottom: 29px; /* sits above the actions row */
+ left: 0;
+ right: 0;
+ height: 20px;
+ background: linear-gradient(to bottom, transparent, var(--terminal-tab-bg));
+ pointer-events: none;
+ z-index: 1;
}
-/* Flyout list: scrollable, scrollbar hidden in collapsed mode */
+/* Tab list: scrollable, scrollbar hidden, scroll via wheel/trackpad */
.terminal-tab-flyout .terminal-flyout-list {
flex: 1;
min-height: 0;
@@ -136,13 +145,6 @@
}
}
-.terminal-tab-flyout:hover .terminal-flyout-list {
- scrollbar-width: auto;
- &::-webkit-scrollbar {
- display: block;
- }
-}
-
/* Flyout item */
.terminal-flyout-item {
position: relative;
@@ -151,8 +153,8 @@
height: 28px;
min-height: 28px;
cursor: pointer;
- color: var(--terminal-tab-text);
- font-size: 11px;
+ color: var(--terminal-tab-active-text);
+ font-size: 12px;
white-space: nowrap;
overflow: hidden;
}
@@ -167,58 +169,47 @@
box-shadow: inset 2px 0 0 #007acc;
}
-/* Icon column: fixed 30px slot, always visible in collapsed mode.
- In expanded mode, CSS order moves it to the right end. */
+/* Icon column: fixed 30px slot */
.terminal-flyout-icon {
width: 30px;
min-width: 30px;
display: flex;
align-items: center;
justify-content: center;
- font-size: 11px;
+ font-size: 12px;
flex-shrink: 0;
}
-/* Title: fills remaining space */
+/* Title & CWD: hidden in collapsed, shown in expanded */
.terminal-flyout-title {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
+ display: none;
}
-/* CWD basename shown on the right */
.terminal-flyout-cwd {
- font-size: 11px;
+ font-size: 12px;
color: var(--terminal-tab-text);
flex-shrink: 1;
min-width: 0;
max-width: 50%;
margin-left: 4px;
margin-right: 2px;
- opacity: 0.6;
+ opacity: 0.8;
overflow: hidden;
text-overflow: ellipsis;
+ display: none;
}
-/* Expanded mode: reorder so icon is at the right end,
- add left padding for close button area */
-.terminal-tab-flyout:hover .terminal-flyout-item {
- padding-left: 30px;
-}
-
-.terminal-tab-flyout:hover .terminal-flyout-icon {
- order: 99;
-}
-
-/* Close button: overlays left padding area, visible only when
- flyout is expanded AND the specific item is hovered */
+/* Close button: absolutely positioned on the right, visible on item hover only */
.terminal-flyout-close {
position: absolute;
- left: 0;
+ right: 0;
top: 0;
bottom: 0;
- width: 30px;
+ width: 20px;
display: none;
align-items: center;
justify-content: center;
@@ -228,7 +219,7 @@
z-index: 1;
}
-.terminal-tab-flyout:hover .terminal-flyout-item:hover .terminal-flyout-close {
+.terminal-flyout-item:hover .terminal-flyout-close {
display: flex;
}
@@ -237,6 +228,21 @@
background: rgba(255, 255, 255, 0.1);
}
+/* ─── Expanded mode ─── */
+.terminal-tabs-expanded .terminal-flyout-title {
+ display: block;
+}
+
+.terminal-tabs-expanded .terminal-flyout-cwd {
+ display: block;
+}
+
+/* Reserve space so cwd text doesn't sit under the close button */
+.terminal-tabs-expanded .terminal-flyout-cwd {
+ margin-right: 20px;
+}
+
+
/* ─── Flyout bottom actions ─── */
.terminal-flyout-actions {
border-top: 1px solid var(--terminal-border);
@@ -259,8 +265,8 @@
height: 100%;
padding: 0;
cursor: pointer;
- color: var(--terminal-tab-text);
- font-size: 11px;
+ color: var(--terminal-tab-active-text);
+ font-size: 12px;
background: transparent;
border: none;
overflow: hidden;
@@ -269,12 +275,11 @@
.terminal-flyout-new-btn:hover {
background: rgba(255, 255, 255, 0.05);
- color: var(--terminal-tab-active-text);
}
-/* Dropdown chevron: hidden in collapsed mode, shown inline when expanded */
+/* Dropdown chevron: visible in both modes */
.terminal-flyout-dropdown-btn {
- display: none;
+ display: flex;
align-items: center;
justify-content: center;
width: 30px;
@@ -289,10 +294,6 @@
flex-shrink: 0;
}
-.terminal-tab-flyout:hover .terminal-flyout-dropdown-btn {
- display: flex;
-}
-
.terminal-flyout-dropdown-btn:hover {
background: rgba(255, 255, 255, 0.05);
color: var(--terminal-tab-active-text);
@@ -304,12 +305,12 @@
flex-shrink: 0;
}
-/* Hide button labels in collapsed mode; show when expanded */
-.terminal-tab-flyout .terminal-btn-label {
+/* Button labels: hidden in collapsed, shown in expanded */
+.terminal-btn-label {
display: none;
}
-.terminal-tab-flyout:hover .terminal-btn-label {
+.terminal-tabs-expanded .terminal-btn-label {
display: inline;
}
diff --git a/test/spec/Terminal-integ-test.js b/test/spec/Terminal-integ-test.js
index 9cc256f62..3066abd28 100644
--- a/test/spec/Terminal-integ-test.js
+++ b/test/spec/Terminal-integ-test.js
@@ -138,12 +138,14 @@ define(function (require, exports, module) {
}
/**
- * Trigger a flyout process refresh so tab titles
+ * Trigger a process refresh so tab titles
* reflect the current foreground process.
*/
function triggerFlyoutRefresh() {
- testWindow.$(".terminal-tab-flyout")
- .trigger("mouseenter");
+ const termModule = testWindow.brackets.getModule(
+ "extensionsIntegrated/Terminal/main"
+ );
+ termModule._refreshAllProcesses();
}
/**