Skip to content

Commit cddac19

Browse files
committed
feat(terminal): add View > Terminal menu entry and improve panel toggling
- Add VIEW_TERMINAL command to Commands.js and View menu - Remove toolbar-terminal sidebar icon and related CSS - Terminal command shows/focuses panel, cycles through terminals when panel is visible and focused with 2+ terminals open - Extract _togglePanels() in WorkspaceManager for reuse by both Escape key and status bar chevron - Escape toggles bottom panel, Shift+Escape cycles open panel tabs - Add PanelView.showNextPanel() for cycling open bottom panels - Fix DefaultPanelView terminal card to use Commands.VIEW_TERMINAL
1 parent 2882817 commit cddac19

9 files changed

Lines changed: 76 additions & 81 deletions

File tree

src/command/Commands.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,9 @@ define(function (require, exports, module) {
297297
/** Toggles problems panel visibility */
298298
exports.VIEW_TOGGLE_PROBLEMS = "view.toggleProblems"; // CodeInspection.js toggleProblems()
299299

300+
/** Opens the terminal panel */
301+
exports.VIEW_TERMINAL = "view.terminal"; // Terminal/main.js _showTerminal()
302+
300303
/** Toggles line numbers visibility */
301304
exports.TOGGLE_LINE_NUMBERS = "view.toggleLineNumbers"; // EditorOptionHandlers.js _getToggler()
302305

src/command/DefaultMenus.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ define(function (require, exports, module) {
231231
menu.addMenuItem(Commands.TOGGLE_RULERS);
232232
menu.addMenuDivider();
233233
menu.addMenuItem(Commands.VIEW_TOGGLE_PROBLEMS);
234+
menu.addMenuItem(Commands.VIEW_TERMINAL);
234235
menu.addMenuItem(Commands.VIEW_TOGGLE_INSPECTION);
235236

236237
/*

src/extensionsIntegrated/Terminal/main.js

Lines changed: 26 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ define(function (require, exports, module) {
3030

3131
const AppInit = require("utils/AppInit");
3232
const CommandManager = require("command/CommandManager");
33-
const Menus = require("command/Menus");
3433
const WorkspaceManager = require("view/WorkspaceManager");
3534
const ProjectManager = require("project/ProjectManager");
3635
const ExtensionUtils = require("utils/ExtensionUtils");
@@ -40,6 +39,7 @@ define(function (require, exports, module) {
4039
const Strings = require("strings");
4140
const StringUtils = require("utils/StringUtils");
4241

42+
const Commands = require("command/Commands");
4343
const TerminalInstance = require("./TerminalInstance");
4444
const ShellProfiles = require("./ShellProfiles");
4545
const panelHTML = require("text!./terminal-panel.html");
@@ -48,7 +48,7 @@ define(function (require, exports, module) {
4848
ExtensionUtils.loadStyleSheet(module, "../../thirdparty/xterm/xterm.css");
4949

5050
// Constants
51-
const CMD_TOGGLE_TERMINAL = "terminal.toggle";
51+
const CMD_VIEW_TERMINAL = Commands.VIEW_TERMINAL;
5252
const CMD_NEW_TERMINAL = "terminal.new";
5353
const PANEL_ID = "terminal-panel";
5454
const PANEL_MIN_SIZE = 100;
@@ -254,7 +254,7 @@ define(function (require, exports, module) {
254254
// Show panel if hidden
255255
if (!panel.isVisible()) {
256256
panel.show();
257-
_updateToolbarIcon(true);
257+
258258
}
259259

260260
// Spawn PTY process
@@ -329,7 +329,7 @@ define(function (require, exports, module) {
329329
// If no terminals left, hide the panel
330330
if (terminalInstances.length === 0) {
331331
panel.hide();
332-
_updateToolbarIcon(false);
332+
333333
}
334334

335335
_updateFlyout();
@@ -467,23 +467,28 @@ define(function (require, exports, module) {
467467
}
468468

469469
/**
470-
* Toggle the terminal panel visibility
470+
* Show the terminal panel. Creates a new terminal if none exist.
471+
* If the panel is visible and the active terminal is focused and there
472+
* are 2+ terminals, cycles to the next one. Otherwise just shows and
473+
* focuses the active terminal.
471474
*/
472-
function _togglePanel() {
473-
if (panel.isVisible()) {
474-
panel.hide();
475-
_updateToolbarIcon(false);
475+
function _showTerminal() {
476+
if (terminalInstances.length === 0) {
477+
_createNewTerminal();
478+
return;
479+
}
480+
const active = _getActiveTerminal();
481+
const terminalHasFocus = active && active.$container &&
482+
active.$container[0].contains(document.activeElement);
483+
if (terminalInstances.length >= 2 && panel.isVisible() && terminalHasFocus) {
484+
const activeIdx = terminalInstances.findIndex(t => t.id === activeTerminalId);
485+
const nextIdx = (activeIdx + 1) % terminalInstances.length;
486+
_activateTerminal(terminalInstances[nextIdx].id);
476487
} else {
477-
if (terminalInstances.length === 0) {
478-
_createNewTerminal();
479-
} else {
480-
panel.show();
481-
_updateToolbarIcon(true);
482-
const active = _getActiveTerminal();
483-
if (active) {
484-
active.handleResize();
485-
active.focus();
486-
}
488+
panel.show();
489+
if (active) {
490+
active.handleResize();
491+
active.focus();
487492
}
488493
}
489494
}
@@ -507,18 +512,6 @@ define(function (require, exports, module) {
507512
}
508513
}
509514

510-
/**
511-
* Update toolbar icon active state
512-
*/
513-
function _updateToolbarIcon(isActive) {
514-
const $icon = $("#toolbar-terminal");
515-
if (isActive) {
516-
$icon.addClass("selected-button");
517-
} else {
518-
$icon.removeClass("selected-button");
519-
}
520-
}
521-
522515
/**
523516
* Escape HTML special characters
524517
*/
@@ -541,13 +534,7 @@ define(function (require, exports, module) {
541534

542535
// Register commands
543536
CommandManager.register("New Terminal", CMD_NEW_TERMINAL, _createNewTerminal);
544-
CommandManager.register("Toggle Terminal", CMD_TOGGLE_TERMINAL, _togglePanel);
545-
546-
// Add menu item
547-
const fileMenu = Menus.getMenu(Menus.AppMenuBar.FILE_MENU);
548-
if (fileMenu) {
549-
fileMenu.addMenuItem(CMD_NEW_TERMINAL, null, Menus.AFTER, "file.close");
550-
}
537+
CommandManager.register(Strings.CMD_VIEW_TERMINAL, CMD_VIEW_TERMINAL, _showTerminal);
551538

552539
// Initialize on app ready
553540
AppInit.appReady(function () {
@@ -558,12 +545,6 @@ define(function (require, exports, module) {
558545
_initNodeConnector();
559546
_createPanel();
560547

561-
// Set up toolbar icon click handler
562-
const $toolbarIcon = $("#toolbar-terminal");
563-
$toolbarIcon.html('<i class="fa-solid fa-terminal"></i>');
564-
$toolbarIcon.removeClass("forced-hidden");
565-
$toolbarIcon.on("click", _togglePanel);
566-
567548
// Detect shells
568549
ShellProfiles.init(nodeConnector).then(function () {
569550
const shells = ShellProfiles.getShells();
@@ -581,6 +562,6 @@ define(function (require, exports, module) {
581562
});
582563

583564
// Export for testing
584-
exports.CMD_TOGGLE_TERMINAL = CMD_TOGGLE_TERMINAL;
565+
exports.CMD_VIEW_TERMINAL = CMD_VIEW_TERMINAL;
585566
exports.CMD_NEW_TERMINAL = CMD_NEW_TERMINAL;
586567
});

src/index.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -995,7 +995,6 @@
995995
<div id="plugin-icons-bar">
996996
<div class="buttons">
997997
<a id="toolbar-go-live" href="#"></a> <!-- tooltip for this is set in JS -->
998-
<a id="toolbar-terminal" href="#" class="forced-hidden" title="Terminal"></a>
999998
<a id="toolbar-extension-manager" href="#"></a>
1000999
<a id="update-notification" href="#" class="forced-hidden"></a>
10011000
</div>

src/nls/root/strings.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,7 @@ define({
650650
"CMD_TOGGLE_WORD_WRAP": "Word Wrap",
651651
"CMD_VIEW_TOGGLE_INSPECTION": "Lint Files on Save",
652652
"CMD_VIEW_TOGGLE_PROBLEMS": "Problems",
653+
"CMD_VIEW_TERMINAL": "Terminal",
653654
"CMD_WORKINGSET_SORT_BY_ADDED": "Sort by Added",
654655
"CMD_WORKINGSET_SORT_BY_NAME": "Sort by Name",
655656
"CMD_WORKINGSET_SORT_BY_TYPE": "Sort by Type",

src/styles/Extn-Terminal.less

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -390,23 +390,6 @@
390390
background-color: var(--terminal-background) !important;
391391
}
392392

393-
/* ─── Toolbar icon in right sidebar ─── */
394-
#toolbar-terminal {
395-
display: flex !important;
396-
align-items: center;
397-
justify-content: center;
398-
}
399-
400-
#toolbar-terminal > i {
401-
font-size: 14px;
402-
line-height: 24px;
403-
color: #bbb;
404-
}
405-
406-
#toolbar-terminal:hover > i {
407-
color: #fff;
408-
}
409-
410393
/* Empty state */
411394
.terminal-empty-state {
412395
display: flex;

src/view/DefaultPanelView.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ define(function (require, exports, module) {
7171
id: "terminal",
7272
icon: "fa-solid fa-terminal",
7373
label: "Terminal",
74-
commandID: "terminal.toggle",
74+
commandID: Commands.VIEW_TERMINAL,
7575
nativeOnly: true
7676
}
7777
];

src/view/PanelView.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,12 +765,31 @@ define(function (require, exports, module) {
765765
return closedIds;
766766
}
767767

768+
/**
769+
* Cycle to the next open bottom panel tab. If the container is hidden
770+
* or no panels are open, does nothing and returns false.
771+
* @return {boolean} true if a panel switch occurred
772+
*/
773+
function showNextPanel() {
774+
if (_openIds.length <= 0) {
775+
return false;
776+
}
777+
const currentIdx = _activeId ? _openIds.indexOf(_activeId) : -1;
778+
const nextIdx = (currentIdx + 1) % _openIds.length;
779+
const nextPanel = _panelMap[_openIds[nextIdx]];
780+
if (nextPanel) {
781+
nextPanel.show();
782+
}
783+
return true;
784+
}
785+
768786
EventDispatcher.makeEventDispatcher(exports);
769787

770788
// Public API
771789
exports.Panel = Panel;
772790
exports.init = init;
773791
exports.getOpenBottomPanelIDs = getOpenBottomPanelIDs;
792+
exports.showNextPanel = showNextPanel;
774793
exports.hideAllOpenPanels = hideAllOpenPanels;
775794
exports.exitMaximizeOnResize = exitMaximizeOnResize;
776795
exports.enterMaximizeOnResize = enterMaximizeOnResize;

src/view/WorkspaceManager.js

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -389,15 +389,7 @@ define(function (require, exports, module) {
389389

390390
$statusBarPanelToggle.on("click", function () {
391391
_statusBarToggleInProgress = true;
392-
if ($bottomPanelContainer.is(":visible")) {
393-
Resizer.hide($bottomPanelContainer[0]);
394-
triggerUpdateLayout();
395-
} else if (PanelView.getOpenBottomPanelIDs().length > 0) {
396-
Resizer.show($bottomPanelContainer[0]);
397-
triggerUpdateLayout();
398-
} else {
399-
_showDefaultPanel();
400-
}
392+
_togglePanels();
401393
_statusBarToggleInProgress = false;
402394
});
403395

@@ -631,15 +623,28 @@ define(function (require, exports, module) {
631623
}
632624
}
633625

634-
function _handleEscapeKey() {
635-
// Collapse the entire bottom panel container, keeping all tabs intact.
636-
// Maximize state is preserved so the panel re-opens maximized.
637-
if ($bottomPanelContainer && $bottomPanelContainer.is(":visible")) {
626+
/**
627+
* Toggle the bottom panel container: hide if visible, show if there are
628+
* open panels, or show the default panel when nothing is open.
629+
* @return {boolean} true if the toggle was handled
630+
*/
631+
function _togglePanels() {
632+
if (!$bottomPanelContainer) {
633+
return false;
634+
}
635+
if ($bottomPanelContainer.is(":visible")) {
638636
Resizer.hide($bottomPanelContainer[0]);
639-
triggerUpdateLayout();
640-
return true;
637+
} else if (PanelView.getOpenBottomPanelIDs().length > 0) {
638+
Resizer.show($bottomPanelContainer[0]);
639+
} else {
640+
_showDefaultPanel();
641641
}
642-
return false;
642+
triggerUpdateLayout();
643+
return true;
644+
}
645+
646+
function _handleEscapeKey() {
647+
return _togglePanels();
643648
}
644649

645650
// pressing escape when focused on editor will hide the bottom panel container
@@ -666,7 +671,10 @@ define(function (require, exports, module) {
666671
return;
667672
}
668673

669-
if (event.keyCode === KeyEvent.DOM_VK_ESCAPE) {
674+
if (event.shiftKey) {
675+
// Shift+Escape: cycle through open bottom panels
676+
PanelView.showNextPanel();
677+
} else {
670678
_handleEscapeKey();
671679
}
672680

0 commit comments

Comments
 (0)