diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js
index 68a7d5db1..5ff0846eb 100644
--- a/src/nls/root/strings.js
+++ b/src/nls/root/strings.js
@@ -1484,6 +1484,8 @@ define({
"ERROR_NOTHING_SELECTED": "Nothing is selected!",
"ERROR_SAVE_FIRST": "Save the document first!",
"ERROR_TERMINAL_NOT_FOUND": "Terminal was not found for your OS, you can define a custom Terminal command in the settings",
+ "TERMINAL_CLOSE_CONFIRM_TITLE": "Active Process Running",
+ "TERMINAL_CLOSE_CONFIRM_MSG": "Terminal has an active process running:
{0}.
Are you sure you want to close it?",
"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
new file mode 100644
index 000000000..68512dcdb
--- /dev/null
+++ b/src/styles/Extn-Terminal.less
@@ -0,0 +1,418 @@
+/*
+ * GNU AGPL-3.0 License
+ *
+ * Copyright (c) 2021 - present core.ai . All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see https://opensource.org/licenses/AGPL-3.0.
+ *
+ */
+
+/* Terminal Extension Styles */
+
+/* Dark theme (default) */
+.dark .terminal-panel-container,
+.darkTheme .terminal-panel-container {
+ --terminal-background: #1e1e1e;
+ --terminal-foreground: #cccccc;
+ --terminal-cursor: #ffffff;
+ --terminal-selection: rgba(255, 255, 255, 0.2);
+ --terminal-border: #333333;
+ --terminal-toolbar-bg: #1e1e1e;
+ --terminal-tab-bg: #181818;
+ --terminal-tab-text: #999999;
+ --terminal-tab-active-bg: rgba(255, 255, 255, 0.05);
+ --terminal-tab-active-text: #cccccc;
+ --terminal-ansi-black: #000000;
+ --terminal-ansi-red: #cd3131;
+ --terminal-ansi-green: #0dbc79;
+ --terminal-ansi-yellow: #e5e510;
+ --terminal-ansi-blue: #2472c8;
+ --terminal-ansi-magenta: #bc3fbc;
+ --terminal-ansi-cyan: #11a8cd;
+ --terminal-ansi-white: #e5e5e5;
+ --terminal-ansi-bright-black: #666666;
+ --terminal-ansi-bright-red: #f14c4c;
+ --terminal-ansi-bright-green: #23d18b;
+ --terminal-ansi-bright-yellow: #f5f543;
+ --terminal-ansi-bright-blue: #3b8eea;
+ --terminal-ansi-bright-magenta: #d670d6;
+ --terminal-ansi-bright-cyan: #29b8db;
+ --terminal-ansi-bright-white: #ffffff;
+}
+
+/* Light theme */
+.terminal-panel-container {
+ --terminal-background: #ffffff;
+ --terminal-foreground: #383a42;
+ --terminal-cursor: #383a42;
+ --terminal-selection: rgba(0, 0, 0, 0.12);
+ --terminal-border: #e0e0e0;
+ --terminal-toolbar-bg: #f5f5f5;
+ --terminal-tab-bg: #eeeeee;
+ --terminal-tab-text: #666666;
+ --terminal-tab-active-bg: rgba(0, 0, 0, 0.05);
+ --terminal-tab-active-text: #333333;
+ --terminal-ansi-black: #383a42;
+ --terminal-ansi-red: #e45649;
+ --terminal-ansi-green: #50a14f;
+ --terminal-ansi-yellow: #c18401;
+ --terminal-ansi-blue: #4078f2;
+ --terminal-ansi-magenta: #a626a4;
+ --terminal-ansi-cyan: #0184bc;
+ --terminal-ansi-white: #a0a1a7;
+ --terminal-ansi-bright-black: #4f525e;
+ --terminal-ansi-bright-red: #e06c75;
+ --terminal-ansi-bright-green: #98c379;
+ --terminal-ansi-bright-yellow: #d19a66;
+ --terminal-ansi-bright-blue: #61afef;
+ --terminal-ansi-bright-magenta: #c678dd;
+ --terminal-ansi-bright-cyan: #56b6c2;
+ --terminal-ansi-bright-white: #ffffff;
+}
+
+.terminal-panel-container {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ background: var(--terminal-toolbar-bg);
+}
+
+/* Body: content + tab bar spacer */
+.terminal-body {
+ display: flex;
+ flex: 1;
+ min-height: 0;
+ position: relative;
+}
+
+/* Tab bar: 30px flex spacer, flyout sits inside absolutely */
+.terminal-tab-bar {
+ position: relative;
+ width: 30px;
+ min-width: 30px;
+ overflow: visible;
+}
+
+/* ─── Unified flyout: collapses to 30px, expands on hover ─── */
+.terminal-tab-flyout {
+ display: flex;
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ width: 30px;
+ background: var(--terminal-tab-bg);
+ border-left: 1px solid var(--terminal-border);
+ z-index: 20;
+ flex-direction: column;
+ transition: width 0.08s ease;
+}
+
+.terminal-tab-flyout:hover {
+ width: 170px;
+ box-shadow: -2px 0 8px rgba(0, 0, 0, 0.3);
+}
+
+/* Flyout list: scrollable, scrollbar hidden in collapsed mode */
+.terminal-tab-flyout .terminal-flyout-list {
+ flex: 1;
+ min-height: 0;
+ overflow-x: hidden;
+ overflow-y: auto;
+ scrollbar-width: none;
+ &::-webkit-scrollbar {
+ display: none;
+ }
+}
+
+.terminal-tab-flyout:hover .terminal-flyout-list {
+ scrollbar-width: auto;
+ &::-webkit-scrollbar {
+ display: block;
+ }
+}
+
+/* Flyout item */
+.terminal-flyout-item {
+ position: relative;
+ display: flex;
+ align-items: center;
+ height: 28px;
+ min-height: 28px;
+ cursor: pointer;
+ color: var(--terminal-tab-text);
+ font-size: 11px;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+.terminal-flyout-item:hover {
+ background: rgba(255, 255, 255, 0.05);
+}
+
+.terminal-flyout-item.active {
+ background: var(--terminal-tab-active-bg);
+ color: var(--terminal-tab-active-text);
+ 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. */
+.terminal-flyout-icon {
+ width: 30px;
+ min-width: 30px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 11px;
+ flex-shrink: 0;
+}
+
+/* Title: fills remaining space */
+.terminal-flyout-title {
+ flex: 1;
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+/* CWD basename shown on the right */
+.terminal-flyout-cwd {
+ font-size: 11px;
+ color: var(--terminal-tab-text);
+ flex-shrink: 1;
+ min-width: 0;
+ max-width: 50%;
+ margin-left: 4px;
+ margin-right: 2px;
+ opacity: 0.6;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+/* 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 */
+.terminal-flyout-close {
+ position: absolute;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ width: 30px;
+ display: none;
+ align-items: center;
+ justify-content: center;
+ font-size: 10px;
+ color: var(--terminal-tab-text);
+ background: transparent;
+ z-index: 1;
+}
+
+.terminal-tab-flyout:hover .terminal-flyout-item:hover .terminal-flyout-close {
+ display: flex;
+}
+
+.terminal-flyout-close:hover {
+ color: var(--terminal-tab-active-text);
+ background: rgba(255, 255, 255, 0.1);
+}
+
+/* ─── Flyout bottom actions ─── */
+.terminal-flyout-actions {
+ border-top: 1px solid var(--terminal-border);
+ position: relative;
+}
+
+/* New terminal row: + button fills, dropdown chevron on the right */
+.terminal-flyout-new-row {
+ display: flex;
+ align-items: center;
+ height: 28px;
+}
+
+.terminal-flyout-new-btn {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 6px;
+ flex: 1;
+ height: 100%;
+ padding: 0;
+ cursor: pointer;
+ color: var(--terminal-tab-text);
+ font-size: 11px;
+ background: transparent;
+ border: none;
+ overflow: hidden;
+ white-space: nowrap;
+}
+
+.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 */
+.terminal-flyout-dropdown-btn {
+ display: none;
+ align-items: center;
+ justify-content: center;
+ width: 30px;
+ height: 100%;
+ padding: 0;
+ cursor: pointer;
+ color: var(--terminal-tab-text);
+ font-size: 9px;
+ background: transparent;
+ border: none;
+ border-left: 1px solid var(--terminal-border);
+ 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);
+}
+
+/* Icon in action buttons */
+.terminal-flyout-btn-icon {
+ font-size: 12px;
+ flex-shrink: 0;
+}
+
+/* Hide button labels in collapsed mode; show when expanded */
+.terminal-tab-flyout .terminal-btn-label {
+ display: none;
+}
+
+.terminal-tab-flyout:hover .terminal-btn-label {
+ display: inline;
+}
+
+/* ─── Shell dropdown (pops above the actions row) ─── */
+.terminal-shell-dropdown {
+ position: absolute;
+ bottom: 100%;
+ right: 0;
+ min-width: 180px;
+ background: var(--terminal-tab-bg);
+ border: 1px solid var(--terminal-border);
+ border-radius: 4px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
+ z-index: 100;
+ padding: 4px 0;
+}
+
+.terminal-shell-dropdown .shell-option {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 6px 12px;
+ cursor: pointer;
+ color: var(--terminal-tab-text);
+ font-size: 12px;
+ white-space: nowrap;
+}
+
+.terminal-shell-dropdown .shell-option:hover {
+ background: rgba(255, 255, 255, 0.1);
+ color: var(--terminal-tab-active-text);
+}
+
+.terminal-shell-dropdown .shell-option .shell-default-badge {
+ font-size: 10px;
+ opacity: 0.5;
+ margin-left: auto;
+}
+
+.terminal-shell-dropdown .shell-option .shell-check {
+ width: 14px;
+ font-size: 11px;
+ text-align: center;
+ flex-shrink: 0;
+}
+
+/* ─── Terminal Content Area ─── */
+.terminal-content-area {
+ flex: 1;
+ position: relative;
+ background: var(--terminal-background);
+ min-width: 0;
+}
+
+.terminal-instance-container {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ display: none;
+ box-sizing: border-box;
+ background: var(--terminal-background);
+}
+
+.terminal-instance-container.active {
+ display: block;
+}
+
+/* xterm.js overrides */
+.terminal-instance-container .xterm {
+ height: 100%;
+ padding-left: 4px;
+}
+
+.terminal-instance-container .xterm-viewport {
+ overflow-y: auto;
+ background-color: var(--terminal-background) !important;
+}
+
+/* ─── Toolbar icon in right sidebar ─── */
+#toolbar-terminal {
+ display: flex !important;
+ align-items: center;
+ justify-content: center;
+}
+
+#toolbar-terminal > i {
+ font-size: 14px;
+ line-height: 24px;
+ color: #bbb;
+}
+
+#toolbar-terminal:hover > i {
+ color: #fff;
+}
+
+/* Empty state */
+.terminal-empty-state {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ color: var(--terminal-tab-text);
+ font-size: 13px;
+}
diff --git a/src/styles/brackets.less b/src/styles/brackets.less
index 72fbe7031..358093536 100644
--- a/src/styles/brackets.less
+++ b/src/styles/brackets.less
@@ -49,6 +49,7 @@
@import "Extn-SidebarTabs.less";
@import "Extn-BottomPanelTabs.less";
@import "Extn-AIChatPanel.less";
+@import "Extn-Terminal.less";
@import "UserProfile.less";
@import "phoenix-pro.less";
diff --git a/src/thirdparty/licences/xterm.markdown b/src/thirdparty/licences/xterm.markdown
new file mode 100644
index 000000000..4472336c9
--- /dev/null
+++ b/src/thirdparty/licences/xterm.markdown
@@ -0,0 +1,21 @@
+Copyright (c) 2017-2019, The xterm.js authors (https://github.com/xtermjs/xterm.js)
+Copyright (c) 2014-2016, SourceLair Private Company (https://www.sourcelair.com)
+Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/src/view/DefaultPanelView.js b/src/view/DefaultPanelView.js
index 353fc5b10..f6e6edda5 100644
--- a/src/view/DefaultPanelView.js
+++ b/src/view/DefaultPanelView.js
@@ -66,6 +66,13 @@ define(function (require, exports, module) {
icon: "fa-solid fa-keyboard",
label: Strings.KEYBOARD_SHORTCUT_PANEL_TITLE || "Keyboard Shortcuts",
commandID: Commands.HELP_TOGGLE_SHORTCUTS_PANEL
+ },
+ {
+ id: "terminal",
+ icon: "fa-solid fa-terminal",
+ label: "Terminal",
+ commandID: "terminal.toggle",
+ nativeOnly: true
}
];
@@ -90,6 +97,9 @@ define(function (require, exports, module) {
let $buttonsRow = $('
');
_panelButtons.forEach(function (btn) {
+ if (btn.nativeOnly && !Phoenix.isNativeApp) {
+ return;
+ }
let $button = $('
')
.attr("data-command", btn.commandID)
.attr("data-btn-id", btn.id)