From 50b957e6b5ff215230aacc50143c1613f88a96f6 Mon Sep 17 00:00:00 2001 From: Pluto Date: Mon, 7 Apr 2025 01:41:33 +0530 Subject: [PATCH 01/32] feat: add preferences to toggle the working set visibility --- src/nls/root/strings.js | 3 +++ src/project/SidebarView.js | 36 +++++++++++++++++++++++++++--------- src/styles/brackets.less | 5 +++++ 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js index 7b996f7da4..2c572819d3 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -1278,6 +1278,9 @@ define({ // Emmet "DESCRIPTION_EMMET": "true to enable Emmet, else false.", + // Hide/Show working set (that is displayed in the sidebar) + "DESCRIPTION_HIDE_WORKING_SET": "true to hide the working set, false to display it.", + // Tabbar "DESCRIPTION_TABBAR": "Set the tab bar settings.", "DESCRIPTION_SHOW_TABBAR": "true to show the tab bar, else false.", diff --git a/src/project/SidebarView.js b/src/project/SidebarView.js index fb609cfbe6..4622f49c4f 100644 --- a/src/project/SidebarView.js +++ b/src/project/SidebarView.js @@ -38,15 +38,16 @@ define(function (require, exports, module) { - var AppInit = require("utils/AppInit"), - ProjectManager = require("project/ProjectManager"), - WorkingSetView = require("project/WorkingSetView"), - MainViewManager = require("view/MainViewManager"), - CommandManager = require("command/CommandManager"), - Commands = require("command/Commands"), - Strings = require("strings"), - Resizer = require("utils/Resizer"), - _ = require("thirdparty/lodash"); + var AppInit = require("utils/AppInit"), + ProjectManager = require("project/ProjectManager"), + PreferencesManager = require("preferences/PreferencesManager"), + WorkingSetView = require("project/WorkingSetView"), + MainViewManager = require("view/MainViewManager"), + CommandManager = require("command/CommandManager"), + Commands = require("command/Commands"), + Strings = require("strings"), + Resizer = require("utils/Resizer"), + _ = require("thirdparty/lodash"); // These vars are initialized by the htmlReady handler // below since they refer to DOM elements @@ -244,6 +245,23 @@ define(function (require, exports, module) { // Tooltips $splitViewMenu.attr("title", Strings.GEAR_MENU_TOOLTIP); + + // Define the preference to decide whether to hide the working set or not + PreferencesManager.definePreference("hideWorkingSet", "boolean", false, { + description: Strings.DESCRIPTION_HIDE_WORKING_SET + }) + .on("change", function () { + // 'working-set-list-container' is the id of the main working set element which we need to hide/show + const $workingSet = $(document.getElementById("working-set-list-container")); + const getPref = PreferencesManager.get("hideWorkingSet"); + + if(getPref) { + // refer to brackets.less file for styles + $workingSet.addClass("working-set-hidden"); + } else { + $workingSet.removeClass("working-set-hidden"); + } + }); }); ProjectManager.on("projectOpen", _updateProjectTitle); diff --git a/src/styles/brackets.less b/src/styles/brackets.less index 6b4b43a611..86b69b1203 100644 --- a/src/styles/brackets.less +++ b/src/styles/brackets.less @@ -1167,12 +1167,17 @@ a, img { #working-set-list-container { background: @bc-sidebar-bg; + display: block; > div:last-child ul { padding-bottom: 21px; // Adds working set bottom padding to the last UL. } } +#working-set-list-container.working-set-hidden { + display: none !important; +} + .working-set-header { position: relative; height: 19px; From 680678e9e8b689c730667af03cbedf1febd76116 Mon Sep 17 00:00:00 2001 From: abose Date: Wed, 9 Apr 2025 16:15:10 +0000 Subject: [PATCH 02/32] ci: bump patch version to 4.1.1 --- package.json | 6 +++--- src-node/package.json | 4 ++-- src/config.json | 4 ++-- src/index.html | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 69496f4468..c121240959 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "phoenix", - "version": "4.1.0-0", - "apiVersion": "4.1.0", + "version": "4.1.1-0", + "apiVersion": "4.1.1", "homepage": "https://core.ai", "issues": { "url": "https://github.com/phcode-dev/phoenix/issues" @@ -117,4 +117,4 @@ "tinycolor2": "^1.4.2", "underscore": "^1.13.4" } -} +} \ No newline at end of file diff --git a/src-node/package.json b/src-node/package.json index e2abbf30f3..8bb880a30e 100644 --- a/src-node/package.json +++ b/src-node/package.json @@ -1,8 +1,8 @@ { "name": "@phcode/node-core", "description": "Phoenix Node Core", - "version": "4.1.0-0", - "apiVersion": "4.1.0", + "version": "4.1.1-0", + "apiVersion": "4.1.1", "keywords": [], "author": "arun@core.ai", "homepage": "https://github.com/phcode-dev/phoenix", diff --git a/src/config.json b/src/config.json index 9b39d0d024..a3c6c88410 100644 --- a/src/config.json +++ b/src/config.json @@ -38,8 +38,8 @@ "bugsnagEnv": "development" }, "name": "Phoenix Code", - "version": "4.1.0-0", - "apiVersion": "4.1.0", + "version": "4.1.1-0", + "apiVersion": "4.1.1", "homepage": "https://core.ai", "issues": { "url": "https://github.com/phcode-dev/phoenix/issues" diff --git a/src/index.html b/src/index.html index c13f595ff0..d147cf1df0 100644 --- a/src/index.html +++ b/src/index.html @@ -68,7 +68,7 @@ - + - \ No newline at end of file From 456314613b6149b1df5ea7d0803f10024c7ee1a1 Mon Sep 17 00:00:00 2001 From: abose Date: Fri, 11 Apr 2025 16:28:51 +0530 Subject: [PATCH 09/32] fix: github actions windows desktop tests fails after rust update, locked rust to 1.85.1 --- .github/workflows/desktop-linux-prod-test-pull.yml | 2 ++ .github/workflows/desktop-linux-test-pull.yml | 2 ++ .github/workflows/desktop-mac-m1-test-pull.yml | 2 ++ .github/workflows/desktop-mac-test-pull.yml | 2 ++ .github/workflows/desktop-windows-test-pull.yml | 2 ++ src/extensions/default/Git/src/ErrorHandler.js | 3 ++- 6 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/desktop-linux-prod-test-pull.yml b/.github/workflows/desktop-linux-prod-test-pull.yml index b18755b96c..57e41d7e3f 100644 --- a/.github/workflows/desktop-linux-prod-test-pull.yml +++ b/.github/workflows/desktop-linux-prod-test-pull.yml @@ -20,6 +20,8 @@ jobs: node-version: 20 - name: install Rust stable uses: dtolnay/rust-toolchain@stable + with: + toolchain: 1.85.1 - name: install dependencies (ubuntu only) run: | diff --git a/.github/workflows/desktop-linux-test-pull.yml b/.github/workflows/desktop-linux-test-pull.yml index 4e7efdb888..ac734955cf 100644 --- a/.github/workflows/desktop-linux-test-pull.yml +++ b/.github/workflows/desktop-linux-test-pull.yml @@ -19,6 +19,8 @@ jobs: node-version: 20 - name: install Rust stable uses: dtolnay/rust-toolchain@stable + with: + toolchain: 1.85.1 - name: install dependencies (ubuntu only) run: | diff --git a/.github/workflows/desktop-mac-m1-test-pull.yml b/.github/workflows/desktop-mac-m1-test-pull.yml index 5ae0ea9f87..15993c36d8 100644 --- a/.github/workflows/desktop-mac-m1-test-pull.yml +++ b/.github/workflows/desktop-mac-m1-test-pull.yml @@ -19,6 +19,8 @@ jobs: node-version: 20 - name: install Rust stable uses: dtolnay/rust-toolchain@stable + with: + toolchain: 1.85.1 - name: build phoenix dist-test run: | diff --git a/.github/workflows/desktop-mac-test-pull.yml b/.github/workflows/desktop-mac-test-pull.yml index ae9a92c5c7..a4b3fc8bc1 100644 --- a/.github/workflows/desktop-mac-test-pull.yml +++ b/.github/workflows/desktop-mac-test-pull.yml @@ -19,6 +19,8 @@ jobs: node-version: 20 - name: install Rust stable uses: dtolnay/rust-toolchain@stable + with: + toolchain: 1.85.1 - name: build phoenix dist-test run: | diff --git a/.github/workflows/desktop-windows-test-pull.yml b/.github/workflows/desktop-windows-test-pull.yml index aa02987ecd..504c101f08 100644 --- a/.github/workflows/desktop-windows-test-pull.yml +++ b/.github/workflows/desktop-windows-test-pull.yml @@ -19,6 +19,8 @@ jobs: node-version: 20 - name: install Rust stable uses: dtolnay/rust-toolchain@stable + with: + toolchain: 1.85.1 - name: build phoenix dist-test run: | diff --git a/src/extensions/default/Git/src/ErrorHandler.js b/src/extensions/default/Git/src/ErrorHandler.js index 9378171b17..c275cafc9b 100644 --- a/src/extensions/default/Git/src/ErrorHandler.js +++ b/src/extensions/default/Git/src/ErrorHandler.js @@ -46,7 +46,6 @@ define(function (require, exports) { exports.showError = function (err, title, options = {}) { const dontStripError = options.dontStripError; const errorMetric = options.errorMetric; - Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'dialogErr', errorMetric || "Show"); if (err.__shown) { return err; } exports.logError(err); @@ -71,6 +70,7 @@ define(function (require, exports) { errorBody = window.debugMode ? `${errorBody}\n${errorStack}` : errorBody; if(options.useNotification){ + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'notifyErr', errorMetric || "Show"); NotificationUI.createToastFromTemplate(title, ``, { toastStyle: NotificationUI.NOTIFICATION_STYLES_CSS_CLASS.ERROR, @@ -78,6 +78,7 @@ define(function (require, exports) { instantOpen: true }); } else { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'dialogErr', errorMetric || "Show"); const compiledTemplate = Mustache.render(errorDialogTemplate, { title: title, body: errorBody, From 42f9708f7bd0fcf56df3f0a40edc6f701be3936e Mon Sep 17 00:00:00 2001 From: Pluto Date: Thu, 15 May 2025 00:59:07 +0530 Subject: [PATCH 10/32] fix: always reserve space for dirty icon to prevent tabs shift --- package-lock.json | 4 ++-- src-node/package-lock.json | 4 ++-- src/styles/Extn-TabBar.less | 17 +++++++++++------ 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index b9a6b8e218..778cf7d205 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "phoenix", - "version": "4.1.0-0", + "version": "4.1.1-0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "phoenix", - "version": "4.1.0-0", + "version": "4.1.1-0", "dependencies": { "@bugsnag/js": "^7.18.0", "@floating-ui/dom": "^0.5.4", diff --git a/src-node/package-lock.json b/src-node/package-lock.json index 6177376876..6183efc95f 100644 --- a/src-node/package-lock.json +++ b/src-node/package-lock.json @@ -1,12 +1,12 @@ { "name": "@phcode/node-core", - "version": "4.1.0-0", + "version": "4.1.1-0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@phcode/node-core", - "version": "4.1.0-0", + "version": "4.1.1-0", "license": "GNU-AGPL3.0", "dependencies": { "@phcode/fs": "^3.0.1", diff --git a/src/styles/Extn-TabBar.less b/src/styles/Extn-TabBar.less index fb4288f541..5400a58ef0 100644 --- a/src/styles/Extn-TabBar.less +++ b/src/styles/Extn-TabBar.less @@ -80,7 +80,7 @@ .tab { display: inline-flex; align-items: center; - padding: 0 0.5rem 0 0.85rem; + padding: 0 0.5rem 0 0.6rem; height: 100%; background-color: #f1f1f1; border-right: 1px solid rgba(0, 0, 0, 0.05); @@ -108,6 +108,7 @@ display: flex; align-items: center; margin-bottom: -2px; + margin-left: 0.7rem; } .tab-name { @@ -191,21 +192,25 @@ background-color: #666; } -.tab.dirty::before { +.tab::before { content: "•"; - color: #888; + color: transparent; font-size: 1.6rem; position: absolute; - left: 0.75rem; + left: 0.4rem; top: 0.25rem; } +.tab.dirty::before { + color: #888; +} + .dark .tab.dirty::before { color: #8D8D8E; } .tab.dirty .tab-icon { - margin-left: 0.8rem; + margin-left: 0.7rem; } .tab-close { @@ -408,4 +413,4 @@ .dropdown-tab-item.placeholder-item .tab-name-container, .tab-name-container.placeholder-name { font-style: italic; -} \ No newline at end of file +} From 47b7b658c2f9ac81572eeb7b65222e42bef3d32a Mon Sep 17 00:00:00 2001 From: Pluto Date: Thu, 15 May 2025 01:31:01 +0530 Subject: [PATCH 11/32] feat: add rename and delete option in tab --- .../TabBar/more-options.js | 46 +++++++++++++++++++ src/nls/root/strings.js | 2 + 2 files changed, 48 insertions(+) diff --git a/src/extensionsIntegrated/TabBar/more-options.js b/src/extensionsIntegrated/TabBar/more-options.js index f734741f44..1156d3d4f2 100644 --- a/src/extensionsIntegrated/TabBar/more-options.js +++ b/src/extensionsIntegrated/TabBar/more-options.js @@ -40,6 +40,9 @@ define(function (require, exports, module) { Strings.CLOSE_ALL_TABS, Strings.CLOSE_UNMODIFIED_TABS, "---", + Strings.RENAME_TAB_FILE, + Strings.DELETE_TAB_FILE, + "---", Strings.REOPEN_CLOSED_FILE ]; @@ -219,6 +222,43 @@ define(function (require, exports, module) { } + /** + * "RENAME FILE" + * This function handles the renaming of the file that was right-clicked + * + * @param {String} filePath - path of the file to rename + */ + function handleFileRename(filePath) { + if (filePath) { + // First ensure the sidebar is visible so users can see the rename action + CommandManager.execute(Commands.SHOW_SIDEBAR); + + // Get the file object using FileSystem + const fileObj = FileSystem.getFileForPath(filePath); + + // Execute the rename command with the file object + CommandManager.execute(Commands.FILE_RENAME, {file: fileObj}); + } + } + + + /** + * "DELETE FILE" + * This function handles the deletion of the file that was right-clicked + * + * @param {String} filePath - path of the file to delete + */ + function handleFileDelete(filePath) { + if (filePath) { + // Get the file object using FileSystem + const fileObj = FileSystem.getFileForPath(filePath); + + // Execute the delete command with the file object + CommandManager.execute(Commands.FILE_DELETE, {file: fileObj}); + } + } + + /** * This function is called when a tab is right-clicked * This will show the more options context menu @@ -279,6 +319,12 @@ define(function (require, exports, module) { case Strings.CLOSE_UNMODIFIED_TABS: handleCloseUnmodifiedTabs(paneId); break; + case Strings.RENAME_TAB_FILE: + handleFileRename(filePath); + break; + case Strings.DELETE_TAB_FILE: + handleFileDelete(filePath); + break; case Strings.REOPEN_CLOSED_FILE: reopenClosedFile(); break; diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js index 2c572819d3..66eee920ed 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -433,6 +433,8 @@ define({ "CLOSE_ALL_TABS": "Close All Tabs", "CLOSE_UNMODIFIED_TABS": "Close Unmodified Tabs", "REOPEN_CLOSED_FILE": "Reopen Closed File", + "RENAME_TAB_FILE": "Rename File", + "DELETE_TAB_FILE": "Delete File", // CodeInspection: errors/warnings "ERRORS_NO_FILE": "No File Open", From 074043dcb458d92fd1c5d4f6d9aacede611cce88 Mon Sep 17 00:00:00 2001 From: Pluto Date: Thu, 15 May 2025 01:47:08 +0530 Subject: [PATCH 12/32] fix: scrollbar appearing in tab context menu --- src/extensionsIntegrated/TabBar/more-options.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/extensionsIntegrated/TabBar/more-options.js b/src/extensionsIntegrated/TabBar/more-options.js index 1156d3d4f2..0184969d10 100644 --- a/src/extensionsIntegrated/TabBar/more-options.js +++ b/src/extensionsIntegrated/TabBar/more-options.js @@ -282,8 +282,13 @@ define(function (require, exports, module) { zIndex: 1000 }); + // Add a custom class to override the max-height, not sure why a scroll bar was coming but this did the trick + dropdown.dropdownExtraClasses = "tabbar-context-menu"; + dropdown.showDropdown(); + $(".tabbar-context-menu").css("max-height", "200px"); + // handle the option selection dropdown.on("select", function (e, item) { _handleSelection(item, filePath, paneId); From 203ab9c1ca3f581d75e2f39cbe4e649459ce39df Mon Sep 17 00:00:00 2001 From: Pluto Date: Thu, 15 May 2025 01:48:39 +0530 Subject: [PATCH 13/32] fix: remove unused code --- src/extensionsIntegrated/TabBar/more-options.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/extensionsIntegrated/TabBar/more-options.js b/src/extensionsIntegrated/TabBar/more-options.js index 0184969d10..a9bd4547b0 100644 --- a/src/extensionsIntegrated/TabBar/more-options.js +++ b/src/extensionsIntegrated/TabBar/more-options.js @@ -68,18 +68,6 @@ define(function (require, exports, module) { } - /** - * "CLOSE ACTIVE TAB" - * this closes the currently active tab - * doesn't matter if the context menu is opened from this tab or some other tab - */ - function handleCloseActiveTab() { - // This simply executes the FILE_CLOSE command without parameters - // which will close the currently active file - CommandManager.execute(Commands.FILE_CLOSE); - } - - /** * "CLOSE ALL TABS" * This will close all tabs in the specified pane From 18552c1a39748191face7d620afcfa352398fe48 Mon Sep 17 00:00:00 2001 From: Pluto Date: Thu, 15 May 2025 02:14:56 +0530 Subject: [PATCH 14/32] feat: add show in file tree option in the tab context menu --- .../TabBar/more-options.js | 68 +++++++++---------- src/nls/root/strings.js | 1 + 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/src/extensionsIntegrated/TabBar/more-options.js b/src/extensionsIntegrated/TabBar/more-options.js index a9bd4547b0..f69cd847d6 100644 --- a/src/extensionsIntegrated/TabBar/more-options.js +++ b/src/extensionsIntegrated/TabBar/more-options.js @@ -42,11 +42,11 @@ define(function (require, exports, module) { "---", Strings.RENAME_TAB_FILE, Strings.DELETE_TAB_FILE, + Strings.SHOW_IN_FILE_TREE, "---", Strings.REOPEN_CLOSED_FILE ]; - /** * "CLOSE TAB" * this function handles the closing of the tab that was right-clicked @@ -60,14 +60,10 @@ define(function (require, exports, module) { const fileObj = FileSystem.getFileForPath(filePath); // Execute close command with file object and pane ID - CommandManager.execute( - Commands.FILE_CLOSE, - { file: fileObj, paneId: paneId } - ); + CommandManager.execute(Commands.FILE_CLOSE, { file: fileObj, paneId: paneId }); } } - /** * "CLOSE ALL TABS" * This will close all tabs in the specified pane @@ -88,14 +84,10 @@ define(function (require, exports, module) { // close each file in the pane, start from the rightmost [to avoid index shifts] for (let i = workingSet.length - 1; i >= 0; i--) { const fileObj = FileSystem.getFileForPath(workingSet[i].path); - CommandManager.execute( - Commands.FILE_CLOSE, - { file: fileObj, paneId: paneId } - ); + CommandManager.execute(Commands.FILE_CLOSE, { file: fileObj, paneId: paneId }); } } - /** * "CLOSE UNMODIFIED TABS" * This will close all tabs that are not modified in the specified pane @@ -114,19 +106,15 @@ define(function (require, exports, module) { } // get all those entries that are not dirty - const unmodifiedEntries = workingSet.filter(entry => !entry.isDirty); + const unmodifiedEntries = workingSet.filter((entry) => !entry.isDirty); // close each unmodified file in the pane for (let i = unmodifiedEntries.length - 1; i >= 0; i--) { const fileObj = FileSystem.getFileForPath(unmodifiedEntries[i].path); - CommandManager.execute( - Commands.FILE_CLOSE, - { file: fileObj, paneId: paneId } - ); + CommandManager.execute(Commands.FILE_CLOSE, { file: fileObj, paneId: paneId }); } } - /** * "CLOSE TABS TO THE LEFT" * This function is responsible for closing all tabs to the left of the right-clicked tab @@ -146,24 +134,21 @@ define(function (require, exports, module) { } // find the index of the current file in the working set - const currentIndex = workingSet.findIndex(entry => entry.path === filePath); + const currentIndex = workingSet.findIndex((entry) => entry.path === filePath); - if (currentIndex > 0) { // we only proceed if there are tabs to the left + if (currentIndex > 0) { + // we only proceed if there are tabs to the left // get all files to the left of the current file const filesToClose = workingSet.slice(0, currentIndex); // Close each file, starting from the rightmost [to avoid index shifts] for (let i = filesToClose.length - 1; i >= 0; i--) { const fileObj = FileSystem.getFileForPath(filesToClose[i].path); - CommandManager.execute( - Commands.FILE_CLOSE, - { file: fileObj, paneId: paneId } - ); + CommandManager.execute(Commands.FILE_CLOSE, { file: fileObj, paneId: paneId }); } } } - /** * "CLOSE TABS TO THE RIGHT" * This function is responsible for closing all tabs to the right of the right-clicked tab @@ -183,7 +168,7 @@ define(function (require, exports, module) { } // get the index of the current file in the working set - const currentIndex = workingSet.findIndex(entry => entry.path === filePath); + const currentIndex = workingSet.findIndex((entry) => entry.path === filePath); // only proceed if there are tabs to the right if (currentIndex !== -1 && currentIndex < workingSet.length - 1) { // get all files to the right of the current file @@ -191,15 +176,11 @@ define(function (require, exports, module) { for (let i = filesToClose.length - 1; i >= 0; i--) { const fileObj = FileSystem.getFileForPath(filesToClose[i].path); - CommandManager.execute( - Commands.FILE_CLOSE, - { file: fileObj, paneId: paneId } - ); + CommandManager.execute(Commands.FILE_CLOSE, { file: fileObj, paneId: paneId }); } } } - /** * "REOPEN CLOSED FILE" * This just calls the reopen closed file command. everthing else is handled there @@ -209,7 +190,6 @@ define(function (require, exports, module) { CommandManager.execute(Commands.FILE_REOPEN_CLOSED); } - /** * "RENAME FILE" * This function handles the renaming of the file that was right-clicked @@ -225,11 +205,10 @@ define(function (require, exports, module) { const fileObj = FileSystem.getFileForPath(filePath); // Execute the rename command with the file object - CommandManager.execute(Commands.FILE_RENAME, {file: fileObj}); + CommandManager.execute(Commands.FILE_RENAME, { file: fileObj }); } } - /** * "DELETE FILE" * This function handles the deletion of the file that was right-clicked @@ -242,10 +221,28 @@ define(function (require, exports, module) { const fileObj = FileSystem.getFileForPath(filePath); // Execute the delete command with the file object - CommandManager.execute(Commands.FILE_DELETE, {file: fileObj}); + CommandManager.execute(Commands.FILE_DELETE, { file: fileObj }); } } + /** + * "SHOW IN FILE TREE" + * This function handles showing the file in the file tree + * + * @param {String} filePath - path of the file to show in file tree + */ + function handleShowInFileTree(filePath) { + if (filePath) { + // First ensure the sidebar is visible so users can see the file in the tree + CommandManager.execute(Commands.SHOW_SIDEBAR); + + // Get the file object using FileSystem + const fileObj = FileSystem.getFileForPath(filePath); + + // Execute the show in tree command with the file object + CommandManager.execute(Commands.NAVIGATE_SHOW_IN_FILE_TREE, { file: fileObj }); + } + } /** * This function is called when a tab is right-clicked @@ -318,6 +315,9 @@ define(function (require, exports, module) { case Strings.DELETE_TAB_FILE: handleFileDelete(filePath); break; + case Strings.SHOW_IN_FILE_TREE: + handleShowInFileTree(filePath); + break; case Strings.REOPEN_CLOSED_FILE: reopenClosedFile(); break; diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js index 66eee920ed..418eecb478 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -435,6 +435,7 @@ define({ "REOPEN_CLOSED_FILE": "Reopen Closed File", "RENAME_TAB_FILE": "Rename File", "DELETE_TAB_FILE": "Delete File", + "SHOW_IN_FILE_TREE": "Show in File Tree", // CodeInspection: errors/warnings "ERRORS_NO_FILE": "No File Open", From 577e3adc1b593063a7c126317183555048dbc9aa Mon Sep 17 00:00:00 2001 From: Pluto Date: Thu, 15 May 2025 14:46:22 +0530 Subject: [PATCH 15/32] fix: scroll bar appearing in commit dropdown --- src/extensionsIntegrated/TabBar/more-options.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extensionsIntegrated/TabBar/more-options.js b/src/extensionsIntegrated/TabBar/more-options.js index f69cd847d6..8523b30b28 100644 --- a/src/extensionsIntegrated/TabBar/more-options.js +++ b/src/extensionsIntegrated/TabBar/more-options.js @@ -272,7 +272,7 @@ define(function (require, exports, module) { dropdown.showDropdown(); - $(".tabbar-context-menu").css("max-height", "200px"); + $(".tabbar-context-menu").css("max-height", "300px"); // handle the option selection dropdown.on("select", function (e, item) { From 31b601d25ab361482aea0ecc9aff621dbb8bcd3c Mon Sep 17 00:00:00 2001 From: Pluto Date: Sat, 17 May 2025 14:51:36 +0530 Subject: [PATCH 16/32] fix: drag-drop bugs that was preventing smooth scroll --- src/extensionsIntegrated/TabBar/drag-drop.js | 247 ++++++++++++++----- src/styles/Extn-TabBar.less | 22 +- 2 files changed, 204 insertions(+), 65 deletions(-) diff --git a/src/extensionsIntegrated/TabBar/drag-drop.js b/src/extensionsIntegrated/TabBar/drag-drop.js index 7ece013fe9..ef0d912653 100644 --- a/src/extensionsIntegrated/TabBar/drag-drop.js +++ b/src/extensionsIntegrated/TabBar/drag-drop.js @@ -18,7 +18,6 @@ * */ - /* This file houses the functionality for dragging and dropping tabs */ /* eslint-disable no-invalid-this */ define(function (require, exports, module) { @@ -40,7 +39,6 @@ define(function (require, exports, module) { let scrollInterval = null; let dragSourcePane = null; - /** * Initialize drag and drop functionality for tab bars * This is called from `main.js` @@ -59,14 +57,13 @@ define(function (require, exports, module) { // Create drag indicator element if it doesn't exist if (!dragIndicator) { dragIndicator = $('
'); - $('body').append(dragIndicator); + $("body").append(dragIndicator); } // add initialization for empty panes initEmptyPaneDropTargets(); } - /** * Setup drag and drop for a specific tab bar * Makes tabs draggable and adds all the necessary event listeners @@ -93,7 +90,6 @@ define(function (require, exports, module) { $tabs.on("dragend", handleDragEnd); } - /** * Setup container-level drag events * This enables dropping tabs in empty spaces and auto-scrolling @@ -103,6 +99,54 @@ define(function (require, exports, module) { */ function setupContainerDrag(containerSelector) { const $container = $(containerSelector); + let lastKnownMousePosition = { x: 0 }; + const boundaryTolerance = 50; // px tolerance outside the container that still allows dropping + + // create a larger drop zone around the container + // this is done to make sure that even if the tab is not exactly over the tab bar, we still allow drag-drop + const createOuterDropZone = () => { + if (draggedTab && !$("#tab-drag-extended-zone").length) { + // an invisible larger zone around the container that can still receive drops + const containerRect = $container[0].getBoundingClientRect(); + const $outerZone = $('
').css({ + position: "fixed", + top: containerRect.top - boundaryTolerance, + left: containerRect.left - boundaryTolerance, + width: containerRect.width + boundaryTolerance * 2, + height: containerRect.height + boundaryTolerance * 2, + zIndex: 9999, + pointerEvents: "all" + }); + + $("body").append($outerZone); + + $outerZone.on("dragover", function (e) { + e.preventDefault(); + e.stopPropagation(); + lastKnownMousePosition.x = e.originalEvent.clientX; + + autoScrollContainer($container[0], lastKnownMousePosition.x); + + updateDragIndicatorFromOuterZone($container, lastKnownMousePosition.x); + + return false; + }); + + $outerZone.on("drop", function (e) { + e.preventDefault(); + e.stopPropagation(); + + // to handle drop the same way as if it happened in the container + handleOuterZoneDrop($container, lastKnownMousePosition.x); + + return false; + }); + } + }; + + const removeOuterDropZone = () => { + $("#tab-drag-extended-zone").remove(); + }; // When dragging over the container but not directly over a tab element $container.on("dragover", function (e) { @@ -110,6 +154,8 @@ define(function (require, exports, module) { e.preventDefault(); } + lastKnownMousePosition.x = e.originalEvent.clientX; + // Clear any existing scroll interval if (scrollInterval) { clearInterval(scrollInterval); @@ -120,30 +166,35 @@ define(function (require, exports, module) { // Set up interval for continuous scrolling while dragging near the edge scrollInterval = setInterval(() => { - if (draggedTab) { // Only continue scrolling if still dragging - autoScrollContainer(this, e.originalEvent.clientX); + if (draggedTab) { + // Only continue scrolling if still dragging + autoScrollContainer(this, lastKnownMousePosition.x); } else { clearInterval(scrollInterval); scrollInterval = null; } }, 16); // this is almost about 60fps - // if the target is not a tab, update the drag indicator using the container bounds - if ($(e.target).closest('.tab').length === 0) { + if ($(e.target).closest(".tab").length === 0) { const containerRect = this.getBoundingClientRect(); const mouseX = e.originalEvent.clientX; // determine if dropping on left or right half of container - const onLeftSide = mouseX < (containerRect.left + containerRect.width / 2); + const onLeftSide = mouseX < containerRect.left + containerRect.width / 2; - const $tabs = $container.find('.tab'); + const $tabs = $container.find(".tab"); if ($tabs.length) { // choose the first tab for left drop, last tab for right drop const targetTab = onLeftSide ? $tabs.first()[0] : $tabs.last()[0]; updateDragIndicator(targetTab, onLeftSide); } } + + // Create the extended drop zone if we're actively dragging + if (draggedTab) { + createOuterDropZone(); + } }); // handle drop on the container (empty space) @@ -153,14 +204,15 @@ define(function (require, exports, module) { } // hide the drag indicator updateDragIndicator(null); + removeOuterDropZone(); // get container dimensions to determine drop position const containerRect = this.getBoundingClientRect(); const mouseX = e.originalEvent.clientX; // determine if dropping on left or right half of container - const onLeftSide = mouseX < (containerRect.left + containerRect.width / 2); + const onLeftSide = mouseX < containerRect.left + containerRect.width / 2; - const $tabs = $container.find('.tab'); + const $tabs = $container.find(".tab"); if ($tabs.length) { // If dropping on left half, target the first tab; otherwise, target the last tab const targetTab = onLeftSide ? $tabs.first()[0] : $tabs.last()[0]; @@ -184,8 +236,86 @@ define(function (require, exports, module) { } } }); - } + /** + * Updates the drag indicator when mouse is in the extended zone (outside actual tab bar) + * @param {jQuery} $container - The tab bar container + * @param {number} mouseX - Current mouse X position + */ + function updateDragIndicatorFromOuterZone($container, mouseX) { + const containerRect = $container[0].getBoundingClientRect(); + const $tabs = $container.find(".tab"); + + if ($tabs.length) { + // Determine if dropping on left half or right half + let onLeftSide = true; + let targetTab; + + // If beyond the right edge, use the last tab + if (mouseX > containerRect.right) { + targetTab = $tabs.last()[0]; + onLeftSide = false; + } + // If beyond the left edge, use the first tab + else if (mouseX < containerRect.left) { + targetTab = $tabs.first()[0]; + onLeftSide = true; + } + // If within bounds, find the closest tab + else { + onLeftSide = mouseX < containerRect.left + containerRect.width / 2; + targetTab = onLeftSide ? $tabs.first()[0] : $tabs.last()[0]; + } + + updateDragIndicator(targetTab, onLeftSide); + } + } + + /** + * Handles drops that occur in the extended drop zone + * @param {jQuery} $container - The tab bar container + * @param {number} mouseX - Current mouse X position + */ + function handleOuterZoneDrop($container, mouseX) { + const containerRect = $container[0].getBoundingClientRect(); + const $tabs = $container.find(".tab"); + + if ($tabs.length && draggedTab) { + // Determine drop position similar to updateDragIndicatorFromOuterZone + let onLeftSide = true; + let targetTab; + + if (mouseX > containerRect.right) { + targetTab = $tabs.last()[0]; + onLeftSide = false; + } else if (mouseX < containerRect.left) { + targetTab = $tabs.first()[0]; + onLeftSide = true; + } else { + onLeftSide = mouseX < containerRect.left + containerRect.width / 2; + targetTab = onLeftSide ? $tabs.first()[0] : $tabs.last()[0]; + } + + // Process the drop + const isSecondPane = $container.attr("id") === "phoenix-tab-bar-2"; + const targetPaneId = isSecondPane ? "second-pane" : "first-pane"; + const draggedPath = $(draggedTab).attr("data-path"); + const targetPath = $(targetTab).attr("data-path"); + + if (dragSourcePane !== targetPaneId) { + // cross-pane drop + moveTabBetweenPanes(dragSourcePane, targetPaneId, draggedPath, targetPath, onLeftSide); + } else { + // same pane drop + moveWorkingSetItem(targetPaneId, draggedPath, targetPath, onLeftSide); + } + } + + // Clean up + updateDragIndicator(null); + removeOuterDropZone(); + } + } /** * enhanced auto-scroll function for container when the mouse is near its left or right edge @@ -196,35 +326,38 @@ define(function (require, exports, module) { */ function autoScrollContainer(container, mouseX) { const rect = container.getBoundingClientRect(); - const edgeThreshold = 50; // teh threshold distance from the edge + const edgeThreshold = 100; // Increased threshold for edge detection (was 50) + const outerThreshold = 50; // Distance outside the container that still triggers scrolling - // Calculate distance from edges - const distanceFromLeft = mouseX - rect.left; - const distanceFromRight = rect.right - mouseX; + // Calculate distance from edges, allowing for mouse to be slightly outside bounds + const distanceFromLeft = mouseX - (rect.left - outerThreshold); + const distanceFromRight = rect.right + outerThreshold - mouseX; // Determine scroll speed based on distance from edge (closer = faster scroll) let scrollSpeed = 0; - if (distanceFromLeft < edgeThreshold) { - // exponential scroll speed: faster as you get closer to the edge - scrollSpeed = -Math.pow(1 - (distanceFromLeft / edgeThreshold), 2) * 15; - } else if (distanceFromRight < edgeThreshold) { - scrollSpeed = Math.pow(1 - (distanceFromRight / edgeThreshold), 2) * 15; + // Only activate scrolling when within the threshold (including the outer buffer) + if (distanceFromLeft < edgeThreshold + outerThreshold && mouseX < rect.right) { + // Non-linear scroll speed: faster as you get closer to the edge + scrollSpeed = -Math.pow(1 - distanceFromLeft / (edgeThreshold + outerThreshold), 2) * 25; + } else if (distanceFromRight < edgeThreshold + outerThreshold && mouseX > rect.left) { + scrollSpeed = Math.pow(1 - distanceFromRight / (edgeThreshold + outerThreshold), 2) * 25; } - // apply scrolling if needed + // Apply scrolling if needed if (scrollSpeed !== 0) { container.scrollLeft += scrollSpeed; // If we're already at the edge, don't keep trying to scroll - if ((scrollSpeed < 0 && container.scrollLeft <= 0) || - (scrollSpeed > 0 && container.scrollLeft >= container.scrollWidth - container.clientWidth)) { + if ( + (scrollSpeed < 0 && container.scrollLeft <= 0) || + (scrollSpeed > 0 && container.scrollLeft >= container.scrollWidth - container.clientWidth) + ) { return; } } } - /** * Handle the start of a drag operation * Stores the tab being dragged and adds visual styling @@ -237,14 +370,14 @@ define(function (require, exports, module) { // set data transfer (required for Firefox) // Firefox requires data to be set for the drag operation to work - e.originalEvent.dataTransfer.effectAllowed = 'move'; - e.originalEvent.dataTransfer.setData('text/html', this.innerHTML); + e.originalEvent.dataTransfer.effectAllowed = "move"; + e.originalEvent.dataTransfer.setData("text/html", this.innerHTML); // Store which pane this tab came from dragSourcePane = $(this).closest("#phoenix-tab-bar-2").length > 0 ? "second-pane" : "first-pane"; // Add dragging class for styling - $(this).addClass('dragging'); + $(this).addClass("dragging"); // Use a timeout to let the dragging class apply before taking measurements // This ensures visual updates are applied before we calculate positions @@ -253,7 +386,6 @@ define(function (require, exports, module) { }, 0); } - /** * Handle the dragover event to enable drop * Updates the visual indicator showing where the tab will be dropped @@ -264,13 +396,13 @@ define(function (require, exports, module) { if (e.preventDefault) { e.preventDefault(); // Allows us to drop } - e.originalEvent.dataTransfer.dropEffect = 'move'; + e.originalEvent.dataTransfer.dropEffect = "move"; // Update the drag indicator position // We need to determine if it should be on the left or right side of the target tab const targetRect = this.getBoundingClientRect(); const mouseX = e.originalEvent.clientX; - const midPoint = targetRect.left + (targetRect.width / 2); + const midPoint = targetRect.left + targetRect.width / 2; const onLeftSide = mouseX < midPoint; updateDragIndicator(this, onLeftSide); @@ -278,7 +410,6 @@ define(function (require, exports, module) { return false; } - /** * Handle entering a potential drop target * Applies styling to indicate the current drop target @@ -287,10 +418,9 @@ define(function (require, exports, module) { */ function handleDragEnter(e) { dragOverTab = this; - $(this).addClass('drag-target'); + $(this).addClass("drag-target"); } - /** * Handle leaving a potential drop target * Removes styling when no longer hovering over a drop target @@ -302,14 +432,13 @@ define(function (require, exports, module) { // Only remove the class if we're truly leaving this tab // This prevents flickering when moving over child elements if (!$(this).is(relatedTarget) && !$(this).has(relatedTarget).length) { - $(this).removeClass('drag-target'); + $(this).removeClass("drag-target"); if (dragOverTab === this) { dragOverTab = null; } } } - /** * Handle dropping a tab onto a target * Moves the file in the working set to the new position @@ -333,7 +462,7 @@ define(function (require, exports, module) { // Determine if we're dropping to the left or right of the target const targetRect = this.getBoundingClientRect(); const mouseX = e.originalEvent.clientX; - const midPoint = targetRect.left + (targetRect.width / 2); + const midPoint = targetRect.left + targetRect.width / 2; const onLeftSide = mouseX < midPoint; // Check if dragging between different panes @@ -348,7 +477,6 @@ define(function (require, exports, module) { return false; } - /** * Handle the end of a drag operation * Cleans up classes and resets state variables @@ -356,7 +484,7 @@ define(function (require, exports, module) { * @param {Event} e - The event object */ function handleDragEnd(e) { - $(".tab").removeClass('dragging drag-target'); + $(".tab").removeClass("dragging drag-target"); updateDragIndicator(null); draggedTab = null; dragOverTab = null; @@ -367,12 +495,15 @@ define(function (require, exports, module) { clearInterval(scrollInterval); scrollInterval = null; } - } + // Remove the extended drop zone if it exists + $("#tab-drag-extended-zone").remove(); + } /** * Update the drag indicator position and visibility * The indicator shows where the tab will be dropped + * Ensures the indicator stays within the bounds of the tab bar * * @param {HTMLElement} targetTab - The tab being dragged over, or null to hide * @param {Boolean} onLeftSide - Whether the indicator should be on the left or right side @@ -382,20 +513,30 @@ define(function (require, exports, module) { dragIndicator.hide(); return; } + // Get the target tab's position and size const targetRect = targetTab.getBoundingClientRect(); + + // Find the containing tab bar to ensure the indicator stays within bounds + const $tabBar = $(targetTab).closest("#phoenix-tab-bar, #phoenix-tab-bar-2"); + const tabBarRect = $tabBar[0] ? $tabBar[0].getBoundingClientRect() : null; + if (onLeftSide) { // Position indicator at the left edge of the target tab + // Ensure it doesn't go beyond the tab bar's left edge + const leftPos = tabBarRect ? Math.max(targetRect.left, tabBarRect.left) : targetRect.left; dragIndicator.css({ top: targetRect.top, - left: targetRect.left, + left: leftPos, height: targetRect.height }); } else { // Position indicator at the right edge of the target tab + // Ensure it doesn't go beyond the tab bar's right edge + const rightPos = tabBarRect ? Math.min(targetRect.right, tabBarRect.right) : targetRect.right; dragIndicator.css({ top: targetRect.top, - left: targetRect.right, + left: rightPos, height: targetRect.height }); } @@ -478,10 +619,7 @@ define(function (require, exports, module) { // Only continue if we found the dragged file if (draggedIndex !== -1 && draggedFile) { // Remove the file from source pane - CommandManager.execute( - Commands.FILE_CLOSE, - { file: draggedFile, paneId: sourcePaneId } - ); + CommandManager.execute(Commands.FILE_CLOSE, { file: draggedFile, paneId: sourcePaneId }); // Calculate where to add it in the target pane let targetInsertIndex; @@ -521,7 +659,6 @@ define(function (require, exports, module) { setupEmptyPaneDropTarget($secondPaneHolder, "second-pane"); } - /** * sets up the whole pane as a drop target when it has no tabs * @@ -546,7 +683,7 @@ define(function (require, exports, module) { $(this).addClass("empty-pane-drop-target"); // set the drop effect - e.originalEvent.dataTransfer.dropEffect = 'move'; + e.originalEvent.dataTransfer.dropEffect = "move"; } }); @@ -571,8 +708,8 @@ define(function (require, exports, module) { const draggedPath = $(draggedTab).attr("data-path"); // Determine source pane - const sourcePaneId = $(draggedTab) - .closest("#phoenix-tab-bar-2").length > 0 ? "second-pane" : "first-pane"; + const sourcePaneId = + $(draggedTab).closest("#phoenix-tab-bar-2").length > 0 ? "second-pane" : "first-pane"; // we don't want to do anything if dropping in the same pane if (sourcePaneId !== paneId) { @@ -589,10 +726,7 @@ define(function (require, exports, module) { if (draggedFile) { // close in the source pane - CommandManager.execute( - Commands.FILE_CLOSE, - { file: draggedFile, paneId: sourcePaneId } - ); + CommandManager.execute(Commands.FILE_CLOSE, { file: draggedFile, paneId: sourcePaneId }); // and open in the target pane MainViewManager.addToWorkingSet(paneId, draggedFile); @@ -609,7 +743,6 @@ define(function (require, exports, module) { }); } - module.exports = { init }; diff --git a/src/styles/Extn-TabBar.less b/src/styles/Extn-TabBar.less index 5400a58ef0..b458305207 100644 --- a/src/styles/Extn-TabBar.less +++ b/src/styles/Extn-TabBar.less @@ -307,30 +307,30 @@ .tab-drag-indicator { position: fixed; width: 2px; - background-color: #0078D7; + background-color: rgba(0, 120, 215, 0.7); pointer-events: none; z-index: 10001; - box-shadow: 0 0 3px rgba(0, 120, 215, 0.5); + box-shadow: 0 0 3px rgba(0, 120, 215, 0.4); display: none; - animation: pulse 1.5s infinite; + animation: pulse 2s infinite; } .dark .tab-drag-indicator { - background-color: #75BEFF; - box-shadow: 0 0 3px rgba(117, 190, 255, 0.5); + background-color: rgba(117, 190, 255, 0.7); + box-shadow: 0 0 3px rgba(117, 190, 255, 0.4); } @keyframes pulse { 0% { - opacity: 0.7; + opacity: 0.5; } 50% { - opacity: 1; + opacity: 0.7; } 100% { - opacity: 0.7; + opacity: 0.5; } } @@ -414,3 +414,9 @@ .tab-name-container.placeholder-name { font-style: italic; } + +/* this is for invisible extended drag zone for tabs */ +#tab-drag-extended-zone { + background-color: transparent; + pointer-events: all; +} From cebaa9bc022f69e3aa587ff8d1b5baf89f4e6f30 Mon Sep 17 00:00:00 2001 From: Pluto Date: Sat, 17 May 2025 19:03:55 +0530 Subject: [PATCH 17/32] fix: show active tab at all cases by replacing the last tab from the preference. numberOfTabs --- src/extensionsIntegrated/TabBar/main.js | 173 +++++++++++++----------- 1 file changed, 95 insertions(+), 78 deletions(-) diff --git a/src/extensionsIntegrated/TabBar/main.js b/src/extensionsIntegrated/TabBar/main.js index eaec425696..a59c4f7733 100644 --- a/src/extensionsIntegrated/TabBar/main.js +++ b/src/extensionsIntegrated/TabBar/main.js @@ -42,8 +42,6 @@ define(function (require, exports, module) { const TabBarHTML = require("text!./html/tabbar-pane.html"); const TabBarHTML2 = require("text!./html/tabbar-second-pane.html"); - - /** * This holds the tab bar element * For tab bar structure, refer to `./html/tabbar-pane.html` and `./html/tabbar-second-pane.html` @@ -54,7 +52,6 @@ define(function (require, exports, module) { let $tabBar = null; let $tabBar2 = null; - /** * This function is responsible to take all the files from the working set and gets the working sets ready * This is placed here instead of helper.js because it modifies the working sets @@ -68,7 +65,6 @@ define(function (require, exports, module) { // to make sure atleast one pane is open if (paneList && paneList.length > 0) { - // this gives the working set of the first pane const currFirstPaneWorkingSet = MainViewManager.getWorkingSet(paneList[0]); @@ -108,8 +104,6 @@ define(function (require, exports, module) { } } - - /** * Responsible for creating the tab element * Note: this creates a tab (for a single file) not the tab bar @@ -128,11 +122,11 @@ define(function (require, exports, module) { const activePathInPane = activeFileInPane ? activeFileInPane.fullPath : null; // Check if this file is active in its pane - const isActive = (entry.path === activePathInPane); + const isActive = entry.path === activePathInPane; // Current active pane (used to determine whether to add the blue underline) const currentActivePane = MainViewManager.getActivePaneId(); - const isPaneActive = (paneId === currentActivePane); + const isPaneActive = paneId === currentActivePane; const isDirty = Helper._isFileModified(FileSystem.getFileForPath(entry.path)); const isPlaceholder = entry.isPlaceholder === true; @@ -140,9 +134,9 @@ define(function (require, exports, module) { // create tab with all the appropriate classes const $tab = $( `
@@ -153,13 +147,13 @@ define(function (require, exports, module) { // Add the file icon const $icon = Helper._getFileIcon(entry); - $tab.find('.tab-icon').append($icon); + $tab.find(".tab-icon").append($icon); // Check if we have a directory part in the displayName - const $tabName = $tab.find('.tab-name'); + const $tabName = $tab.find(".tab-name"); if (entry.displayName && entry.displayName !== entry.name) { // Split the displayName into directory and filename parts - const parts = entry.displayName.split('/'); + const parts = entry.displayName.split("/"); const dirName = parts[0]; const fileName = parts[1]; @@ -174,39 +168,38 @@ define(function (require, exports, module) { if (isActive && !isPaneActive) { // if it's active but in a non-active pane, we add a special class // to style differently in CSS to indicate that it's active but not in the active pane - $tab.addClass('active-in-inactive-pane'); + $tab.addClass("active-in-inactive-pane"); } // if this is a placeholder tab in inactive pane, we need to use the brown styling // instead of the blue one for active tabs if (isPlaceholder && isActive && !isPaneActive) { - $tab.removeClass('active'); - $tab.addClass('active-in-inactive-pane'); + $tab.removeClass("active"); + $tab.addClass("active-in-inactive-pane"); } return $tab; } - /** * Creates the tab bar and adds it to the DOM */ function createTabBar() { - if (!Preference.tabBarEnabled || Preference.numberOfTabs === 0) { + if (!Preference.tabBarEnabled || Preference.tabBarNumberOfTabs === 0) { + cleanupTabBar(); return; } // clean up any existing tab bars first and start fresh cleanupTabBar(); - const $paneHeader = $('.pane-header'); + const $paneHeader = $(".pane-header"); if ($paneHeader.length === 1) { $tabBar = $(TabBarHTML); // since we need to add the tab bar before the editor which has .not-editor class $(".pane-header").after($tabBar); WorkspaceManager.recomputeLayout(true); updateTabs(); - } else if ($paneHeader.length === 2) { $tabBar = $(TabBarHTML); $tabBar2 = $(TabBarHTML2); @@ -220,7 +213,6 @@ define(function (require, exports, module) { } } - /** * This function updates the tabs in the tab bar * It is called when the working set changes. So instead of creating a new tab bar, we just update the existing one @@ -229,6 +221,12 @@ define(function (require, exports, module) { // Get all files from the working set. refer to `global.js` getAllFilesFromWorkingSet(); + // just to make sure that the number of tabs is not set to 0 + if (Preference.tabBarNumberOfTabs === 0) { + cleanupTabBar(); + return; + } + // Check for active files not in working set in any pane const activePane = MainViewManager.getActivePaneId(); const firstPaneFile = MainViewManager.getCurrentlyViewedFile("first-pane"); @@ -239,8 +237,7 @@ define(function (require, exports, module) { let secondPanePlaceholder = null; // Check if active file in first pane is not in the working set - if (firstPaneFile && - !Global.firstPaneWorkingSet.some(entry => entry.path === firstPaneFile.fullPath)) { + if (firstPaneFile && !Global.firstPaneWorkingSet.some((entry) => entry.path === firstPaneFile.fullPath)) { firstPanePlaceholder = { path: firstPaneFile.fullPath, name: firstPaneFile.name, @@ -250,8 +247,7 @@ define(function (require, exports, module) { } // Check if active file in second pane is not in the working set - if (secondPaneFile && - !Global.secondPaneWorkingSet.some(entry => entry.path === secondPaneFile.fullPath)) { + if (secondPaneFile && !Global.secondPaneWorkingSet.some((entry) => entry.path === secondPaneFile.fullPath)) { secondPanePlaceholder = { path: secondPaneFile.fullPath, name: secondPaneFile.name, @@ -263,9 +259,7 @@ define(function (require, exports, module) { // check for duplicate file names between placeholder tabs and working set entries if (firstPanePlaceholder) { // if any working set file has the same name as the placeholder - const hasDuplicate = Global.firstPaneWorkingSet.some(entry => - entry.name === firstPanePlaceholder.name - ); + const hasDuplicate = Global.firstPaneWorkingSet.some((entry) => entry.name === firstPanePlaceholder.name); if (hasDuplicate) { // extract directory name from path @@ -280,9 +274,7 @@ define(function (require, exports, module) { } if (secondPanePlaceholder) { - const hasDuplicate = Global.secondPaneWorkingSet.some(entry => - entry.name === secondPanePlaceholder.name - ); + const hasDuplicate = Global.secondPaneWorkingSet.some((entry) => entry.name === secondPanePlaceholder.name); if (hasDuplicate) { const path = secondPanePlaceholder.path; @@ -295,22 +287,25 @@ define(function (require, exports, module) { } // Create tab bar if there's a placeholder or a file in the working set - if ((Global.firstPaneWorkingSet.length > 0 || firstPanePlaceholder) && - (!$('#phoenix-tab-bar').length || $('#phoenix-tab-bar').is(':hidden'))) { + if ( + (Global.firstPaneWorkingSet.length > 0 || firstPanePlaceholder) && + (!$("#phoenix-tab-bar").length || $("#phoenix-tab-bar").is(":hidden")) + ) { createTabBar(); } - if ((Global.secondPaneWorkingSet.length > 0 || secondPanePlaceholder) && - (!$('#phoenix-tab-bar-2').length || $('#phoenix-tab-bar-2').is(':hidden'))) { + if ( + (Global.secondPaneWorkingSet.length > 0 || secondPanePlaceholder) && + (!$("#phoenix-tab-bar-2").length || $("#phoenix-tab-bar-2").is(":hidden")) + ) { createTabBar(); } - const $firstTabBar = $('#phoenix-tab-bar'); + const $firstTabBar = $("#phoenix-tab-bar"); // Update first pane's tabs if ($firstTabBar.length) { $firstTabBar.empty(); if (Global.firstPaneWorkingSet.length > 0 || firstPanePlaceholder) { - // get the count of tabs that we want to display in the tab bar (from preference settings) // from preference settings or working set whichever smaller let tabsCountP1 = Math.min(Global.firstPaneWorkingSet.length, Preference.tabBarNumberOfTabs); @@ -321,7 +316,28 @@ define(function (require, exports, module) { tabsCountP1 = Global.firstPaneWorkingSet.length; } - let displayedEntries = Global.firstPaneWorkingSet.slice(0, tabsCountP1); + let displayedEntries = []; + + // check if active file is in the working set but would be excluded by tab count + if (firstPaneFile && Preference.tabBarNumberOfTabs > 0) { + const activeFileIndex = Global.firstPaneWorkingSet.findIndex( + (entry) => entry.path === firstPaneFile.fullPath + ); + + if (activeFileIndex >= 0 && activeFileIndex >= Preference.tabBarNumberOfTabs) { + // active file is in working set but would be excluded by tab count + // Show active file and one less from the top N files + displayedEntries = [ + ...Global.firstPaneWorkingSet.slice(0, Preference.tabBarNumberOfTabs - 1), + Global.firstPaneWorkingSet[activeFileIndex] + ]; + } else { + // Active file is either not in working set or already included in top N files + displayedEntries = Global.firstPaneWorkingSet.slice(0, tabsCountP1); + } + } else { + displayedEntries = Global.firstPaneWorkingSet.slice(0, tabsCountP1); + } // Add working set tabs displayedEntries.forEach(function (entry) { @@ -335,18 +351,34 @@ define(function (require, exports, module) { } } - const $secondTabBar = $('#phoenix-tab-bar-2'); + const $secondTabBar = $("#phoenix-tab-bar-2"); // Update second pane's tabs if ($secondTabBar.length) { $secondTabBar.empty(); if (Global.secondPaneWorkingSet.length > 0 || secondPanePlaceholder) { - let tabsCountP2 = Math.min(Global.secondPaneWorkingSet.length, Preference.tabBarNumberOfTabs); if (Preference.tabBarNumberOfTabs < 0) { tabsCountP2 = Global.secondPaneWorkingSet.length; } - let displayedEntries2 = Global.secondPaneWorkingSet.slice(0, tabsCountP2); + let displayedEntries2 = []; + + if (secondPaneFile && Preference.tabBarNumberOfTabs > 0) { + const activeFileIndex = Global.secondPaneWorkingSet.findIndex( + (entry) => entry.path === secondPaneFile.fullPath + ); + + if (activeFileIndex >= 0 && activeFileIndex >= Preference.tabBarNumberOfTabs) { + displayedEntries2 = [ + ...Global.secondPaneWorkingSet.slice(0, Preference.tabBarNumberOfTabs - 1), + Global.secondPaneWorkingSet[activeFileIndex] + ]; + } else { + displayedEntries2 = Global.secondPaneWorkingSet.slice(0, tabsCountP2); + } + } else { + displayedEntries2 = Global.secondPaneWorkingSet.slice(0, tabsCountP2); + } // Add working set tabs displayedEntries2.forEach(function (entry) { @@ -361,12 +393,12 @@ define(function (require, exports, module) { } // if no files are present in a pane and no placeholder, we want to hide the tab bar for that pane - if (Global.firstPaneWorkingSet.length === 0 && !firstPanePlaceholder && ($('#phoenix-tab-bar'))) { - Helper._hideTabBar($('#phoenix-tab-bar'), $('#overflow-button')); + if (Global.firstPaneWorkingSet.length === 0 && !firstPanePlaceholder && $("#phoenix-tab-bar")) { + Helper._hideTabBar($("#phoenix-tab-bar"), $("#overflow-button")); } - if (Global.secondPaneWorkingSet.length === 0 && !secondPanePlaceholder && ($('#phoenix-tab-bar-2'))) { - Helper._hideTabBar($('#phoenix-tab-bar-2'), $('#overflow-button-2')); + if (Global.secondPaneWorkingSet.length === 0 && !secondPanePlaceholder && $("#phoenix-tab-bar-2")) { + Helper._hideTabBar($("#phoenix-tab-bar-2"), $("#overflow-button-2")); } // Now that tabs are updated, scroll to the active tab if necessary. @@ -394,10 +426,9 @@ define(function (require, exports, module) { } // handle drag and drop - DragDrop.init($('#phoenix-tab-bar'), $('#phoenix-tab-bar-2')); + DragDrop.init($("#phoenix-tab-bar"), $("#phoenix-tab-bar-2")); } - /** * Removes existing tab bar and cleans up */ @@ -415,16 +446,14 @@ define(function (require, exports, module) { WorkspaceManager.recomputeLayout(true); } - /** * handle click events on the tabs to open the file */ function handleTabClick() { - // delegate event handling for both tab bars $(document).on("click", ".phoenix-tab-bar .tab", function (event) { // check if the clicked element is the close button - if ($(event.target).hasClass('fa-times') || $(event.target).closest('.tab-close').length) { + if ($(event.target).hasClass("fa-times") || $(event.target).closest(".tab-close").length) { // Get the file path from the data-path attribute of the parent tab const filePath = $(this).attr("data-path"); @@ -436,10 +465,7 @@ define(function (require, exports, module) { // get the file object const fileObj = FileSystem.getFileForPath(filePath); // close the file - CommandManager.execute( - Commands.FILE_CLOSE, - { file: fileObj, paneId: paneId } - ); + CommandManager.execute(Commands.FILE_CLOSE, { file: fileObj, paneId: paneId }); // Prevent default behavior event.preventDefault(); @@ -450,7 +476,7 @@ define(function (require, exports, module) { // delegate event handling for both tab bars $(document).on("mousedown", ".phoenix-tab-bar .tab", function (event) { - if ($(event.target).hasClass('fa-times') || $(event.target).closest('.tab-close').length) { + if ($(event.target).hasClass("fa-times") || $(event.target).closest(".tab-close").length) { return; } // Get the file path from the data-path attribute @@ -461,11 +487,11 @@ define(function (require, exports, module) { const isSecondPane = $(this).closest("#phoenix-tab-bar-2").length > 0; const paneId = isSecondPane ? "second-pane" : "first-pane"; const currentActivePane = MainViewManager.getActivePaneId(); - const isPaneActive = (paneId === currentActivePane); + const isPaneActive = paneId === currentActivePane; const currentFile = MainViewManager.getCurrentlyViewedFile(currentActivePane); // Check if this is a placeholder tab - if ($(this).hasClass('placeholder')) { + if ($(this).hasClass("placeholder")) { // Add the file to the working set when placeholder tab is clicked const fileObj = FileSystem.getFileForPath(filePath); MainViewManager.addToWorkingSet(paneId, fileObj); @@ -497,7 +523,6 @@ define(function (require, exports, module) { }); } - // debounce is used to prevent rapid consecutive calls to updateTabs, // which was causing integration tests to fail in Firefox. Without it, // the event fires too frequently when switching editors, leading to unexpected behavior @@ -545,8 +570,7 @@ define(function (require, exports, module) { // Update UI if ($tabBar) { const $tab = $tabBar.find(`.tab[data-path="${filePath}"]`); - $tab.toggleClass('dirty', doc.isDirty); - + $tab.toggleClass("dirty", doc.isDirty); // Update the working set data // First pane @@ -558,11 +582,10 @@ define(function (require, exports, module) { } } - // Also update the $tab2 if it exists if ($tabBar2) { const $tab2 = $tabBar2.find(`.tab[data-path="${filePath}"]`); - $tab2.toggleClass('dirty', doc.isDirty); + $tab2.toggleClass("dirty", doc.isDirty); // Second pane for (let i = 0; i < Global.secondPaneWorkingSet.length; i++) { @@ -575,7 +598,6 @@ define(function (require, exports, module) { }); } - /** * This is called when the tab bar preference is changed either, * from the preferences file or the menu bar @@ -601,17 +623,13 @@ define(function (require, exports, module) { * for toggling the tab bar from the menu bar */ function _registerCommands() { - CommandManager.register( - Strings.CMD_TOGGLE_TABBAR, - Commands.TOGGLE_TABBAR, - () => { - const currentPref = PreferencesManager.get(Preference.PREFERENCES_TAB_BAR); - PreferencesManager.set(Preference.PREFERENCES_TAB_BAR, { - ...currentPref, - showTabBar: !currentPref.showTabBar - }); - } - ); + CommandManager.register(Strings.CMD_TOGGLE_TABBAR, Commands.TOGGLE_TABBAR, () => { + const currentPref = PreferencesManager.get(Preference.PREFERENCES_TAB_BAR); + PreferencesManager.set(Preference.PREFERENCES_TAB_BAR, { + ...currentPref, + showTabBar: !currentPref.showTabBar + }); + }); } /** @@ -620,7 +638,6 @@ define(function (require, exports, module) { * when its scrolled down, the tab bar will scroll to the right */ function setupTabBarScrolling() { - // common handler for both the tab bars function handleMouseWheel(e) { // get the tab bar element that is being scrolled @@ -640,7 +657,7 @@ define(function (require, exports, module) { } // attach the wheel event handler to both tab bars - $(document).on('wheel', '#phoenix-tab-bar, #phoenix-tab-bar-2', handleMouseWheel); + $(document).on("wheel", "#phoenix-tab-bar, #phoenix-tab-bar-2", handleMouseWheel); } AppInit.appReady(function () { @@ -662,7 +679,7 @@ define(function (require, exports, module) { handleTabClick(); Overflow.init(); - DragDrop.init($('#phoenix-tab-bar'), $('#phoenix-tab-bar-2')); + DragDrop.init($("#phoenix-tab-bar"), $("#phoenix-tab-bar-2")); // setup the mouse wheel scrolling setupTabBarScrolling(); From 29e404b85cd1e93c81d575d96893b63d92b33a80 Mon Sep 17 00:00:00 2001 From: Pluto Date: Sun, 18 May 2025 00:04:12 +0530 Subject: [PATCH 18/32] fix: show display path in tabs instead of tauri path --- src/extensionsIntegrated/TabBar/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extensionsIntegrated/TabBar/main.js b/src/extensionsIntegrated/TabBar/main.js index a59c4f7733..20bd8a4149 100644 --- a/src/extensionsIntegrated/TabBar/main.js +++ b/src/extensionsIntegrated/TabBar/main.js @@ -138,7 +138,7 @@ define(function (require, exports, module) { ${isDirty ? "dirty" : ""} ${isPlaceholder ? "placeholder" : ""}" data-path="${entry.path}" - title="${entry.path}"> + title="${Phoenix.app.getDisplayPath(entry.path)}">
From 8426afe6e8b392fa21f01cdfe170b270f21c473d Mon Sep 17 00:00:00 2001 From: Pluto Date: Sun, 18 May 2025 15:19:29 +0530 Subject: [PATCH 19/32] feat: show git change markers on tabs --- src/extensions/default/Git/main.js | 7 +- .../default/Git/src/TabBarIntegration.js | 88 +++++++++++++++++++ src/extensionsIntegrated/TabBar/main.js | 33 ++++++- src/styles/Extn-TabBar.less | 34 ++++++- 4 files changed, 155 insertions(+), 7 deletions(-) create mode 100644 src/extensions/default/Git/src/TabBarIntegration.js diff --git a/src/extensions/default/Git/main.js b/src/extensions/default/Git/main.js index e1921ca047..aaf38e8458 100644 --- a/src/extensions/default/Git/main.js +++ b/src/extensions/default/Git/main.js @@ -27,7 +27,8 @@ define(function (require, exports, module) { "src/History", "src/NoRepo", "src/ProjectTreeMarks", - "src/Remotes" + "src/Remotes", + "src/TabBarIntegration" ]; require(modules); @@ -48,10 +49,12 @@ define(function (require, exports, module) { // export API's for other extensions if (typeof window === "object") { + const TabBarIntegration = require("src/TabBarIntegration"); window.phoenixGitEvents = { EventEmitter: EventEmitter, Events: Events, - Git + Git, + TabBarIntegration }; } }); diff --git a/src/extensions/default/Git/src/TabBarIntegration.js b/src/extensions/default/Git/src/TabBarIntegration.js new file mode 100644 index 0000000000..e904922542 --- /dev/null +++ b/src/extensions/default/Git/src/TabBarIntegration.js @@ -0,0 +1,88 @@ +define(function (require) { + const EventEmitter = require("src/EventEmitter"); + const Events = require("src/Events"); + const Git = require("src/git/Git"); + const Preferences = require("src/Preferences"); + + // the cache of file statuses by path + let fileStatusCache = {}; + + /** + * this function is responsible to get the Git status for a file path + * + * @param {string} fullPath - the file path + * @returns {Array|null} - Array of status strings or null if no status + */ + function getFileStatus(fullPath) { + return fileStatusCache[fullPath] || null; + } + + /** + * whether the file is modified or not + * + * @param {string} fullPath - the file path + * @returns {boolean} - True if the file is modified otherwise false + */ + function isModified(fullPath) { + const status = getFileStatus(fullPath); + if (!status) { + return false; + } + return ( + status.some( + (statusType) => + statusType === Git.FILE_STATUS.MODIFIED || + statusType === Git.FILE_STATUS.RENAMED || + statusType === Git.FILE_STATUS.COPIED + ) && !status.includes(Git.FILE_STATUS.STAGED) + ); + } + + /** + * whether the file is untracked or not + * + * @param {string} fullPath - the file path + * @returns {boolean} - True if the file is untracked otherwise false + */ + function isUntracked(fullPath) { + const status = getFileStatus(fullPath); + if (!status) { + return false; + } + + return status.includes(Git.FILE_STATUS.UNTRACKED); + } + + + // Update file status cache when Git status results are received + EventEmitter.on(Events.GIT_STATUS_RESULTS, function (files) { + // reset the cache + fileStatusCache = {}; + + const gitRoot = Preferences.get("currentGitRoot"); + if (!gitRoot) { + return; + } + + // we need to update cache with new status results + files.forEach(function (entry) { + const fullPath = gitRoot + entry.file; + fileStatusCache[fullPath] = entry.status; + }); + + // notify that file statuses have been updated + EventEmitter.emit("GIT_FILE_STATUS_CHANGED", fileStatusCache); + }); + + // clear cache when Git is disabled + EventEmitter.on(Events.GIT_DISABLED, function () { + fileStatusCache = {}; + EventEmitter.emit("GIT_FILE_STATUS_CHANGED", fileStatusCache); + }); + + return { + getFileStatus: getFileStatus, + isModified: isModified, + isUntracked: isUntracked + }; +}); diff --git a/src/extensionsIntegrated/TabBar/main.js b/src/extensionsIntegrated/TabBar/main.js index 20bd8a4149..1eac0140fb 100644 --- a/src/extensionsIntegrated/TabBar/main.js +++ b/src/extensionsIntegrated/TabBar/main.js @@ -131,16 +131,39 @@ define(function (require, exports, module) { const isDirty = Helper._isFileModified(FileSystem.getFileForPath(entry.path)); const isPlaceholder = entry.isPlaceholder === true; + let gitStatus = ""; // this will be shown in the tooltip when a tab is hovered + let gitStatusClass = ""; // for styling + let gitStatusLetter = ""; // shown in the tab, either U or M + + if (window.phoenixGitEvents && window.phoenixGitEvents.TabBarIntegration) { + const TabBarIntegration = window.phoenixGitEvents.TabBarIntegration; + + // find the Git status + // if untracked we add the git-new class and U char + // if modified we add the git-modified class and M char + if (TabBarIntegration.isUntracked(entry.path)) { + gitStatus = "Untracked"; + gitStatusClass = "git-new"; + gitStatusLetter = "U"; + } else if (TabBarIntegration.isModified(entry.path)) { + gitStatus = "Modified"; + gitStatusClass = "git-modified"; + gitStatusLetter = "M"; + } + } + // create tab with all the appropriate classes const $tab = $( `
+ title="${Phoenix.app.getDisplayPath(entry.path)}${gitStatus ? " (" + gitStatus + ")" : ""}">
+ ${gitStatusLetter ? `
${gitStatusLetter}
` : ""}
` ); @@ -541,6 +564,12 @@ define(function (require, exports, module) { // For editor changes, update only the tabs. MainViewManager.on(MainViewManager.EVENT_CURRENT_FILE_CHANGE, debounceUpdateTabs); + // to listen for the Git status changes + // make sure that the git extension is available + if (window.phoenixGitEvents && window.phoenixGitEvents.EventEmitter) { + window.phoenixGitEvents.EventEmitter.on("GIT_FILE_STATUS_CHANGED", debounceUpdateTabs); + } + // For working set changes, update only the tabs. const events = [ "workingSetAdd", diff --git a/src/styles/Extn-TabBar.less b/src/styles/Extn-TabBar.less index b458305207..1d0f850cfc 100644 --- a/src/styles/Extn-TabBar.less +++ b/src/styles/Extn-TabBar.less @@ -137,6 +137,8 @@ font-size: 0.7rem; opacity: 0.7; font-weight: normal; + position: relative; + top: 0.1rem; } .tab.active { @@ -195,10 +197,10 @@ .tab::before { content: "•"; color: transparent; - font-size: 1.6rem; + font-size: 1.5rem; position: absolute; left: 0.4rem; - top: 0.25rem; + top: 0.33rem; } .tab.dirty::before { @@ -215,7 +217,7 @@ .tab-close { font-size: 0.75rem; - padding: 0.08rem 0.4rem; + padding: 0.1rem 0.4rem; margin-left: 0.25rem; color: #666; transition: all 0.2s ease; @@ -420,3 +422,29 @@ background-color: transparent; pointer-events: all; } + +/* ------ for the git status markers -------------- */ +.tab-git-status { + font-size: 0.9rem; + font-weight: bold; + display: flex; + align-items: center; + justify-content: center; + margin-left: 0.33rem; +} + +.git-modified .tab-git-status { + color: #E3B551; +} + +.dark .git-modified .tab-git-status { + color: #E3B551; +} + +.git-new .tab-git-status { + color: #91CC41; +} + +.dark .git-new .tab-git-status { + color: #91CC41; +} From 88dc4c8f51d96cd774032719533570b827ea65b3 Mon Sep 17 00:00:00 2001 From: Pluto Date: Sun, 18 May 2025 20:50:06 +0530 Subject: [PATCH 20/32] refactor: git markers styles in tabs --- src/extensionsIntegrated/TabBar/main.js | 1 - src/styles/Extn-TabBar.less | 25 ++++++++++++------------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/extensionsIntegrated/TabBar/main.js b/src/extensionsIntegrated/TabBar/main.js index 1eac0140fb..228c42ddc1 100644 --- a/src/extensionsIntegrated/TabBar/main.js +++ b/src/extensionsIntegrated/TabBar/main.js @@ -163,7 +163,6 @@ define(function (require, exports, module) { title="${Phoenix.app.getDisplayPath(entry.path)}${gitStatus ? " (" + gitStatus + ")" : ""}">
- ${gitStatusLetter ? `
${gitStatusLetter}
` : ""}
` ); diff --git a/src/styles/Extn-TabBar.less b/src/styles/Extn-TabBar.less index 1d0f850cfc..e75cfc61ab 100644 --- a/src/styles/Extn-TabBar.less +++ b/src/styles/Extn-TabBar.less @@ -424,27 +424,26 @@ } /* ------ for the git status markers -------------- */ -.tab-git-status { - font-size: 0.9rem; - font-weight: bold; - display: flex; - align-items: center; - justify-content: center; - margin-left: 0.33rem; -} - -.git-modified .tab-git-status { +.tab.git-modified > .tab-icon:before { + content: "|"; color: #E3B551; + position: absolute; + margin-left: -4px; + margin-bottom: 5px; } -.dark .git-modified .tab-git-status { +.dark .tab.git-modified > .tab-icon:before { color: #E3B551; } -.git-new .tab-git-status { +.tab.git-new > .tab-icon:before { + content: "|"; color: #91CC41; + position: absolute; + margin-left: -4px; + margin-bottom: 5px; } -.dark .git-new .tab-git-status { +.dark .tab.git-new > .tab-icon:before { color: #91CC41; } From af979da90e9ad1a5ef36e6a52917b9ee17e842d5 Mon Sep 17 00:00:00 2001 From: Pluto Date: Sun, 18 May 2025 21:13:36 +0530 Subject: [PATCH 21/32] fix: git markers disappearing from tabs when files are staged --- .../default/Git/src/TabBarIntegration.js | 18 ++++++++++-------- src/extensionsIntegrated/TabBar/main.js | 3 --- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/extensions/default/Git/src/TabBarIntegration.js b/src/extensions/default/Git/src/TabBarIntegration.js index e904922542..24453f3166 100644 --- a/src/extensions/default/Git/src/TabBarIntegration.js +++ b/src/extensions/default/Git/src/TabBarIntegration.js @@ -28,13 +28,11 @@ define(function (require) { if (!status) { return false; } - return ( - status.some( - (statusType) => - statusType === Git.FILE_STATUS.MODIFIED || - statusType === Git.FILE_STATUS.RENAMED || - statusType === Git.FILE_STATUS.COPIED - ) && !status.includes(Git.FILE_STATUS.STAGED) + return status.some( + (statusType) => + statusType === Git.FILE_STATUS.MODIFIED || + statusType === Git.FILE_STATUS.RENAMED || + statusType === Git.FILE_STATUS.COPIED ); } @@ -50,7 +48,11 @@ define(function (require) { return false; } - return status.includes(Git.FILE_STATUS.UNTRACKED); + // return true if it's untracked or if it's newly added (which means it was untracked before staging) + return ( + status.includes(Git.FILE_STATUS.UNTRACKED) || + (status.includes(Git.FILE_STATUS.ADDED) && status.includes(Git.FILE_STATUS.STAGED)) + ); } diff --git a/src/extensionsIntegrated/TabBar/main.js b/src/extensionsIntegrated/TabBar/main.js index 228c42ddc1..9c43888f3f 100644 --- a/src/extensionsIntegrated/TabBar/main.js +++ b/src/extensionsIntegrated/TabBar/main.js @@ -133,7 +133,6 @@ define(function (require, exports, module) { let gitStatus = ""; // this will be shown in the tooltip when a tab is hovered let gitStatusClass = ""; // for styling - let gitStatusLetter = ""; // shown in the tab, either U or M if (window.phoenixGitEvents && window.phoenixGitEvents.TabBarIntegration) { const TabBarIntegration = window.phoenixGitEvents.TabBarIntegration; @@ -144,11 +143,9 @@ define(function (require, exports, module) { if (TabBarIntegration.isUntracked(entry.path)) { gitStatus = "Untracked"; gitStatusClass = "git-new"; - gitStatusLetter = "U"; } else if (TabBarIntegration.isModified(entry.path)) { gitStatus = "Modified"; gitStatusClass = "git-modified"; - gitStatusLetter = "M"; } } From 289dadaed9892a7d0e7f35e3a128fb11c0c0c284 Mon Sep 17 00:00:00 2001 From: Pluto Date: Sun, 18 May 2025 21:37:15 +0530 Subject: [PATCH 22/32] fix: git change markers color not properly visible in light theme --- src/styles/Extn-TabBar.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/styles/Extn-TabBar.less b/src/styles/Extn-TabBar.less index e75cfc61ab..6f17f686d4 100644 --- a/src/styles/Extn-TabBar.less +++ b/src/styles/Extn-TabBar.less @@ -426,7 +426,7 @@ /* ------ for the git status markers -------------- */ .tab.git-modified > .tab-icon:before { content: "|"; - color: #E3B551; + color: #f1ae1c; position: absolute; margin-left: -4px; margin-bottom: 5px; @@ -438,7 +438,7 @@ .tab.git-new > .tab-icon:before { content: "|"; - color: #91CC41; + color: #69a01f; position: absolute; margin-left: -4px; margin-bottom: 5px; From b166b7172a8df2e378f1f3e6b5d18d89b553edeb Mon Sep 17 00:00:00 2001 From: abose Date: Wed, 21 May 2025 11:22:36 +0530 Subject: [PATCH 23/32] refactor: file tab strings for delete, rename, show in file tree --- src/extensionsIntegrated/TabBar/more-options.js | 12 ++++++------ src/nls/root/strings.js | 3 --- src/styles/brackets_core_ui_variables.less | 5 ++++- src/styles/brackets_patterns_override.less | 12 ++++++++++++ 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/extensionsIntegrated/TabBar/more-options.js b/src/extensionsIntegrated/TabBar/more-options.js index 8523b30b28..a1b5a37d80 100644 --- a/src/extensionsIntegrated/TabBar/more-options.js +++ b/src/extensionsIntegrated/TabBar/more-options.js @@ -40,9 +40,9 @@ define(function (require, exports, module) { Strings.CLOSE_ALL_TABS, Strings.CLOSE_UNMODIFIED_TABS, "---", - Strings.RENAME_TAB_FILE, - Strings.DELETE_TAB_FILE, - Strings.SHOW_IN_FILE_TREE, + Strings.CMD_FILE_RENAME, + Strings.CMD_FILE_DELETE, + Strings.CMD_SHOW_IN_TREE, "---", Strings.REOPEN_CLOSED_FILE ]; @@ -309,13 +309,13 @@ define(function (require, exports, module) { case Strings.CLOSE_UNMODIFIED_TABS: handleCloseUnmodifiedTabs(paneId); break; - case Strings.RENAME_TAB_FILE: + case Strings.CMD_FILE_RENAME: handleFileRename(filePath); break; - case Strings.DELETE_TAB_FILE: + case Strings.CMD_FILE_DELETE: handleFileDelete(filePath); break; - case Strings.SHOW_IN_FILE_TREE: + case Strings.CMD_SHOW_IN_TREE: handleShowInFileTree(filePath); break; case Strings.REOPEN_CLOSED_FILE: diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js index 418eecb478..2c572819d3 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -433,9 +433,6 @@ define({ "CLOSE_ALL_TABS": "Close All Tabs", "CLOSE_UNMODIFIED_TABS": "Close Unmodified Tabs", "REOPEN_CLOSED_FILE": "Reopen Closed File", - "RENAME_TAB_FILE": "Rename File", - "DELETE_TAB_FILE": "Delete File", - "SHOW_IN_FILE_TREE": "Show in File Tree", // CodeInspection: errors/warnings "ERRORS_NO_FILE": "No File Open", diff --git a/src/styles/brackets_core_ui_variables.less b/src/styles/brackets_core_ui_variables.less index 49dee4f002..71fc73aa40 100644 --- a/src/styles/brackets_core_ui_variables.less +++ b/src/styles/brackets_core_ui_variables.less @@ -205,6 +205,9 @@ @dark-bc-warning-text-low: #333333; @notification-text: #333333; +// success +@dark-bc-success-bg: #1e3a1e; + // Text @dark-bc-text: #ccc; @dark-bc-text-alt: #fff; @@ -275,4 +278,4 @@ // CSS Codehint icon @css-codehint-icon: #2ea56c; -@dark-css-codehint-icon: #146a41; \ No newline at end of file +@dark-css-codehint-icon: #146a41; diff --git a/src/styles/brackets_patterns_override.less b/src/styles/brackets_patterns_override.less index 66aff91962..4e5db00b9f 100644 --- a/src/styles/brackets_patterns_override.less +++ b/src/styles/brackets_patterns_override.less @@ -2286,6 +2286,18 @@ dt, } } +.alert.alert-success { + text-shadow: none; + background-color: @successBackground; + border: none; + + .dark & { + background: @dark-bc-success-bg; + border-radius: 2px; + color: @dark-bc-text; + } +} + .alert h4 { // Specified for the h4 to prevent conflicts of changing @headingsColor color: @bc-warning-bg; From 6520fd7964a3b64fd33c1e8f2ca66d6ae772b205 Mon Sep 17 00:00:00 2001 From: Pluto Date: Fri, 16 May 2025 13:32:49 +0530 Subject: [PATCH 24/32] feat: replace phoenix code support button with user profile button --- src/extensionsIntegrated/Phoenix/main.js | 8 ++++---- src/nls/root/strings.js | 1 + src/styles/brackets.less | 4 ++++ src/styles/images/circle-user-solid.svg | 1 + 4 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 src/styles/images/circle-user-solid.svg diff --git a/src/extensionsIntegrated/Phoenix/main.js b/src/extensionsIntegrated/Phoenix/main.js index 94f0015324..ff6ad241d6 100644 --- a/src/extensionsIntegrated/Phoenix/main.js +++ b/src/extensionsIntegrated/Phoenix/main.js @@ -38,17 +38,17 @@ define(function (require, exports, module) { let $icon; function _addToolbarIcon() { - const helpButtonID = "help-button"; + const helpButtonID = "user-profile-button"; $icon = $("") .attr({ id: helpButtonID, href: "#", - class: "help", - title: Strings.CMD_SUPPORT + class: "user", + title: Strings.CMD_USER_PROFILE }) .appendTo($("#main-toolbar .bottom-buttons")); $icon.on('click', ()=>{ - Phoenix.app.openURLInDefaultBrowser(brackets.config.support_url); + console.log("User profile button was clicked"); }); } function _showUnSupportedBrowserDialogue() { diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js index 2c572819d3..7f464c05de 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -626,6 +626,7 @@ define({ "CMD_AUTO_UPDATE": "Auto Update", "CMD_HOW_TO_USE_BRACKETS": "How to Use {APP_NAME}", "CMD_SUPPORT": "{APP_NAME} Support", + "CMD_USER_PROFILE": "User Profile", "CMD_DOCS": "Help, Getting Started", "CMD_SUGGEST": "Suggest a Feature", "CMD_REPORT_ISSUE": "Report Issue", diff --git a/src/styles/brackets.less b/src/styles/brackets.less index 86b69b1203..376ddd673b 100644 --- a/src/styles/brackets.less +++ b/src/styles/brackets.less @@ -672,6 +672,10 @@ a, img { background-image: url("images/discuss-icon.svg"); } +.user { + background-image: url("images/circle-user-solid.svg"); +} + #editor-holder { position: relative; diff --git a/src/styles/images/circle-user-solid.svg b/src/styles/images/circle-user-solid.svg new file mode 100644 index 0000000000..836691525d --- /dev/null +++ b/src/styles/images/circle-user-solid.svg @@ -0,0 +1 @@ + From d775dc6cd6e21e9bfb56bb3bcf1bb047ab67cca6 Mon Sep 17 00:00:00 2001 From: Pluto Date: Fri, 16 May 2025 18:01:03 +0530 Subject: [PATCH 25/32] feat: user profile implementation --- .../Phoenix/html/login-dialog.html | 17 ++ .../Phoenix/html/profile-panel.html | 39 ++++ src/extensionsIntegrated/Phoenix/main.js | 5 +- .../Phoenix/profile-menu.js | 190 ++++++++++++++++++ src/styles/brackets.less | 1 + src/styles/user-profile.less | 29 +++ 6 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 src/extensionsIntegrated/Phoenix/html/login-dialog.html create mode 100644 src/extensionsIntegrated/Phoenix/html/profile-panel.html create mode 100644 src/extensionsIntegrated/Phoenix/profile-menu.js create mode 100644 src/styles/user-profile.less diff --git a/src/extensionsIntegrated/Phoenix/html/login-dialog.html b/src/extensionsIntegrated/Phoenix/html/login-dialog.html new file mode 100644 index 0000000000..a06d7dfdce --- /dev/null +++ b/src/extensionsIntegrated/Phoenix/html/login-dialog.html @@ -0,0 +1,17 @@ +
+ + +
diff --git a/src/extensionsIntegrated/Phoenix/html/profile-panel.html b/src/extensionsIntegrated/Phoenix/html/profile-panel.html new file mode 100644 index 0000000000..9eb2cda00d --- /dev/null +++ b/src/extensionsIntegrated/Phoenix/html/profile-panel.html @@ -0,0 +1,39 @@ +
+ + +
diff --git a/src/extensionsIntegrated/Phoenix/main.js b/src/extensionsIntegrated/Phoenix/main.js index ff6ad241d6..5bb8dcacbc 100644 --- a/src/extensionsIntegrated/Phoenix/main.js +++ b/src/extensionsIntegrated/Phoenix/main.js @@ -32,7 +32,8 @@ define(function (require, exports, module) { Strings = require("strings"), Dialogs = require("widgets/Dialogs"), NotificationUI = require("widgets/NotificationUI"), - DefaultDialogs = require("widgets/DefaultDialogs"); + DefaultDialogs = require("widgets/DefaultDialogs"), + ProfileMenu = require("./profile-menu"); const PERSIST_STORAGE_DIALOG_DELAY_SECS = 60000; let $icon; @@ -48,7 +49,7 @@ define(function (require, exports, module) { }) .appendTo($("#main-toolbar .bottom-buttons")); $icon.on('click', ()=>{ - console.log("User profile button was clicked"); + ProfileMenu.init(); }); } function _showUnSupportedBrowserDialogue() { diff --git a/src/extensionsIntegrated/Phoenix/profile-menu.js b/src/extensionsIntegrated/Phoenix/profile-menu.js new file mode 100644 index 0000000000..5b8a83d62b --- /dev/null +++ b/src/extensionsIntegrated/Phoenix/profile-menu.js @@ -0,0 +1,190 @@ +define(function (require, exports, module) { + const loginTemplate = require("text!./html/login-dialog.html"); + const profileTemplate = require("text!./html/profile-panel.html"); + + // for the popup DOM element + let $popup = null; + + // this is to track whether the popup is visible or not + let isPopupVisible = false; + + // if user is logged in we show the profile menu, otherwise we show the login menu + const isLoggedIn = false; + + function _handleSignInBtnClick() { + console.log("User clicked sign in button"); + } + + function _handleSignOutBtnClick() { + console.log("User clicked sign out"); + } + + function _handleContactSupportBtnClick() { + Phoenix.app.openURLInDefaultBrowser(brackets.config.support_url); + } + + function _handleAccountDetailsBtnClick() { + console.log("User clicked account details"); + } + + /** + * Close the popup if it's open + * this is called at various instances like when the user click on the profile icon even if the popup is open + * or when user clicks somewhere else on the document + */ + function closePopup() { + if ($popup) { + $popup.remove(); + $popup = null; + isPopupVisible = false; + + // remove global click handler + $(document).off("click.profilePopup"); + } + } + + /** + * handle clicks outside the popup to close it + */ + function handleDocumentClick(e) { + // If popup exists and click is outside the popup + if ($popup && $popup.length && !$popup[0].contains(e.target)) { + // If the click is not on the user-profile-button (which would toggle the popup) + if (e.target.id !== "user-profile-button" && !$(e.target).closest("#user-profile-button").length) { + closePopup(); + } + } + } + + /** + * this function is to position the popup near the profile button + */ + function positionPopup() { + const $profileButton = $("#user-profile-button"); + + if ($profileButton.length && $popup) { + const buttonPos = $profileButton.offset(); + const popupWidth = $popup.outerWidth(); + const windowWidth = $(window).width(); + + // pos above the profile button + let top = buttonPos.top - $popup.outerHeight() - 10; + + // If popup would go off the right edge of the window, align right edge of popup with right edge of button + let left = Math.min( + buttonPos.left - popupWidth + $profileButton.outerWidth(), + windowWidth - popupWidth - 10 + ); + + // never go off left edge + left = Math.max(10, left); + + $popup.css({ + top: top + "px", + left: left + "px" + }); + } + } + + /** + * Shows the sign-in popup when the user is not logged in + */ + function showLoginPopup() { + // If popup is already visible, just close it + if (isPopupVisible) { + closePopup(); + return; + } + + // create the popup element + closePopup(); // close any existing popup first + $popup = $(loginTemplate); + $("body").append($popup); + isPopupVisible = true; + + // Position the popup + positionPopup(); + + // event handlers for buttons + $popup.find("#phoenix-signin-btn").on("click", function () { + _handleSignInBtnClick(); + closePopup(); + }); + + $popup.find("#phoenix-support-btn").on("click", function () { + _handleContactSupportBtnClick(); + closePopup(); + }); + + // Set up global click handler to close popup when clicking outside + // Delay attaching to avoid immediate closing + setTimeout(function () { + $(document).on("click.profilePopup", handleDocumentClick); + }, 0); + } + + /** + * Shows the user profile popup when the user is logged in + */ + function showProfilePopup() { + // If popup is already visible, just close it + if (isPopupVisible) { + closePopup(); + return; + } + + closePopup(); + $popup = $(profileTemplate); + $("body").append($popup); + isPopupVisible = true; + positionPopup(); + + $popup.find("#phoenix-account-btn").on("click", function () { + _handleAccountDetailsBtnClick(); + closePopup(); + }); + + $popup.find("#phoenix-support-btn").on("click", function () { + _handleContactSupportBtnClick(); + closePopup(); + }); + + $popup.find("#phoenix-signout-btn").on("click", function () { + _handleSignOutBtnClick(); + closePopup(); + }); + + // Set up global click handler to close popup when clicking outside + // Delay attaching to avoid immediate closing + setTimeout(function () { + $(document).on("click.profilePopup", handleDocumentClick); + }, 0); + } + + /** + * Toggle the profile popup based on the user's login status + * this function is called inside the src/extensionsIntegrated/Phoenix/main.js when user clicks on the profile icon + */ + function init() { + // check if the popup is already visible or not. if visible close it + if (isPopupVisible) { + closePopup(); + return; + } + + if (isLoggedIn) { + showProfilePopup(); + } else { + showLoginPopup(); + } + + // handle window resize to reposition popup + $(window).on("resize.profilePopup", function () { + if (isPopupVisible) { + positionPopup(); + } + }); + } + + exports.init = init; +}); diff --git a/src/styles/brackets.less b/src/styles/brackets.less index 376ddd673b..9f76a98065 100644 --- a/src/styles/brackets.less +++ b/src/styles/brackets.less @@ -44,6 +44,7 @@ @import "Extn-TabBar.less"; @import "Extn-DisplayShortcuts.less"; @import "Extn-CSSColorPreview.less"; +@import "user-profile.less"; /* Overall layout */ diff --git a/src/styles/user-profile.less b/src/styles/user-profile.less new file mode 100644 index 0000000000..b6ce9f2f46 --- /dev/null +++ b/src/styles/user-profile.less @@ -0,0 +1,29 @@ +.profile-popup { + background-color: #2c2c2c; + color: #ffffff; + border-radius: 6px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); + width: 300px; + overflow: hidden; + position: absolute; + z-index: 9999; + + .popup-header { + padding: 15px; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + } + + .popup-body { + padding: 15px; + } + + .popup-title { + margin: 0; + font-size: 18px; + font-weight: normal; + } +} + +.dark .profile-popup { + background-color: #1d1f21; +} From 691f168a46835c27e3987bfb21342bddf6fdbd12 Mon Sep 17 00:00:00 2001 From: Pluto Date: Fri, 16 May 2025 19:02:12 +0530 Subject: [PATCH 26/32] fix: remove hard-coded values for easier integration --- .../Phoenix/html/login-dialog.html | 6 +- .../Phoenix/html/profile-panel.html | 18 +++--- .../Phoenix/profile-menu.js | 57 ++++++++++++++++--- 3 files changed, 60 insertions(+), 21 deletions(-) diff --git a/src/extensionsIntegrated/Phoenix/html/login-dialog.html b/src/extensionsIntegrated/Phoenix/html/login-dialog.html index a06d7dfdce..3bbb928a6c 100644 --- a/src/extensionsIntegrated/Phoenix/html/login-dialog.html +++ b/src/extensionsIntegrated/Phoenix/html/login-dialog.html @@ -1,16 +1,16 @@
diff --git a/src/extensionsIntegrated/Phoenix/html/profile-panel.html b/src/extensionsIntegrated/Phoenix/html/profile-panel.html index 9eb2cda00d..d4eaf8a093 100644 --- a/src/extensionsIntegrated/Phoenix/html/profile-panel.html +++ b/src/extensionsIntegrated/Phoenix/html/profile-panel.html @@ -2,38 +2,38 @@
diff --git a/src/extensionsIntegrated/Phoenix/profile-menu.js b/src/extensionsIntegrated/Phoenix/profile-menu.js index 5b8a83d62b..3df2c9848a 100644 --- a/src/extensionsIntegrated/Phoenix/profile-menu.js +++ b/src/extensionsIntegrated/Phoenix/profile-menu.js @@ -1,4 +1,7 @@ define(function (require, exports, module) { + const Mustache = require("thirdparty/mustache/mustache"); + + // HTML templates const loginTemplate = require("text!./html/login-dialog.html"); const profileTemplate = require("text!./html/profile-panel.html"); @@ -9,7 +12,27 @@ define(function (require, exports, module) { let isPopupVisible = false; // if user is logged in we show the profile menu, otherwise we show the login menu - const isLoggedIn = false; + const isLoggedIn = true; + + const defaultLoginData = { + welcomeTitle: "Welcome to Phoenix Code", + signInBtnText: "Sign in to your account", + supportBtnText: "Contact support" + }; + + const defaultProfileData = { + initials: "CA", + userName: "Charly A.", + planName: "Paid Plan", + quotaLabel: "AI quota used", + quotaUsed: "7,000", + quotaTotal: "10,000", + quotaUnit: "tokens", + quotaPercent: 70, + accountBtnText: "Account details", + supportBtnText: "Contact support", + signOutBtnText: "Sign out" + }; function _handleSignInBtnClick() { console.log("User clicked sign in button"); @@ -88,21 +111,28 @@ define(function (require, exports, module) { /** * Shows the sign-in popup when the user is not logged in + * @param {Object} loginData - Data to populate the template (optional) */ - function showLoginPopup() { + function showLoginPopup(loginData) { // If popup is already visible, just close it if (isPopupVisible) { closePopup(); return; } + // Merge provided data with defaults + const templateData = $.extend({}, defaultLoginData, loginData || {}); + // create the popup element closePopup(); // close any existing popup first - $popup = $(loginTemplate); + + // Render template with data + const renderedTemplate = Mustache.render(loginTemplate, templateData); + $popup = $(renderedTemplate); + $("body").append($popup); isPopupVisible = true; - // Position the popup positionPopup(); // event handlers for buttons @@ -125,16 +155,24 @@ define(function (require, exports, module) { /** * Shows the user profile popup when the user is logged in + * @param {Object} profileData - Data to populate the template (optional) */ - function showProfilePopup() { + function showProfilePopup(profileData) { // If popup is already visible, just close it if (isPopupVisible) { closePopup(); return; } + // Merge provided data with defaults + const templateData = $.extend({}, defaultProfileData, profileData || {}); + closePopup(); - $popup = $(profileTemplate); + + // Render template with data + const renderedTemplate = Mustache.render(profileTemplate, templateData); + $popup = $(renderedTemplate); + $("body").append($popup); isPopupVisible = true; positionPopup(); @@ -164,8 +202,9 @@ define(function (require, exports, module) { /** * Toggle the profile popup based on the user's login status * this function is called inside the src/extensionsIntegrated/Phoenix/main.js when user clicks on the profile icon + * @param {Object} data - Data to populate the templates (optional) */ - function init() { + function init(data) { // check if the popup is already visible or not. if visible close it if (isPopupVisible) { closePopup(); @@ -173,9 +212,9 @@ define(function (require, exports, module) { } if (isLoggedIn) { - showProfilePopup(); + showProfilePopup(data); } else { - showLoginPopup(); + showLoginPopup(data); } // handle window resize to reposition popup From ca3c7f1338165e149fef625be65d2eb47ffa7ba4 Mon Sep 17 00:00:00 2001 From: Pluto Date: Fri, 16 May 2025 19:13:21 +0530 Subject: [PATCH 27/32] refactor: improve styles to align the UI with rest of Phoenix --- .../Phoenix/html/login-dialog.html | 10 +- .../Phoenix/html/profile-panel.html | 30 +-- .../Phoenix/profile-menu.js | 2 +- src/styles/user-profile.less | 239 +++++++++++++++++- 4 files changed, 249 insertions(+), 32 deletions(-) diff --git a/src/extensionsIntegrated/Phoenix/html/login-dialog.html b/src/extensionsIntegrated/Phoenix/html/login-dialog.html index 3bbb928a6c..dbe692d262 100644 --- a/src/extensionsIntegrated/Phoenix/html/login-dialog.html +++ b/src/extensionsIntegrated/Phoenix/html/login-dialog.html @@ -3,13 +3,13 @@

{{welcomeTitle}}