From 117baebfd10757dbb6b59d37b4c60600ce975966 Mon Sep 17 00:00:00 2001 From: abose Date: Sat, 18 Apr 2026 11:56:31 +0530 Subject: [PATCH 01/19] feat: add vertical central control bar between sidebar and editor A new 30px control bar sits between the sidebar and the editor area with editor collapse, edit/save actions, file navigation, sidebar toggle, and a vertical filename label. The sidebar-toggle button has been moved from the titlebar into the control bar permanently. The collapse toggle expands live preview to fill the editor area (opening it first if not already open), and honors the user's last live preview width on restore. Dragging the plugin-toolbar resizer while collapsed exits the collapsed state so normal resize resumes. The NAVIGATE_SHOW_IN_FILE_TREE command now also expands the sidebar when it is hidden so reveal-in-tree works regardless of entry point (control-bar click, context menu, keyboard shortcut). --- src/brackets.js | 1 + src/command/Menus.js | 29 --- src/document/DocumentCommandHandlers.js | 3 + .../NavigationProvider.js | 9 +- src/index.html | 44 +++- src/styles/CentralControlBar.less | 173 ++++++++++++ src/styles/brackets.less | 3 +- src/styles/brackets_patterns_override.less | 22 -- src/view/CentralControlBar.js | 246 ++++++++++++++++++ 9 files changed, 469 insertions(+), 61 deletions(-) create mode 100644 src/styles/CentralControlBar.less create mode 100644 src/view/CentralControlBar.js diff --git a/src/brackets.js b/src/brackets.js index 3a3e08c739..4c53bb9c83 100644 --- a/src/brackets.js +++ b/src/brackets.js @@ -127,6 +127,7 @@ define(function (require, exports, module) { require("file/FileUtils"); require("project/SidebarView"); require("view/SidebarTabs"); + require("view/CentralControlBar"); require("utils/Resizer"); require("LiveDevelopment/main"); require("utils/NodeConnection"); diff --git a/src/command/Menus.js b/src/command/Menus.js index 18fb38ea21..c9a21f8a7c 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -1778,35 +1778,6 @@ define(function (require, exports, module) { const $hamburgerToggle = $hamburger.find(".hamburger-toggle"); let _activeSubmenuId = null; - // Sidebar collapse/expand toggle button before the File menu - const $sidebarToggle = $(``); - $menubar.prepend($sidebarToggle); - const $sidebarIcon = $sidebarToggle.find("a"); - - function _updateSidebarToggleIcon() { - const isVisible = $("#sidebar").is(":visible"); - if (isVisible) { - $sidebarIcon.html(''); - $sidebarIcon.attr("title", Strings.CMD_HIDE_SIDEBAR); - } else { - $sidebarIcon.html(''); - $sidebarIcon.attr("title", Strings.CMD_SHOW_SIDEBAR); - } - } - - $sidebarIcon.on("click", function (e) { - e.preventDefault(); - e.stopPropagation(); - CommandManager.execute(Commands.VIEW_HIDE_SIDEBAR); - }); - - $("#sidebar").on("panelCollapsed panelExpanded", _updateSidebarToggleIcon); - _updateSidebarToggleIcon(); - function _resetMenuItemStyles($menuItem) { const menu = menuMap[$menuItem.attr("id")]; if (menu) { diff --git a/src/document/DocumentCommandHandlers.js b/src/document/DocumentCommandHandlers.js index 2c35042527..7292015592 100644 --- a/src/document/DocumentCommandHandlers.js +++ b/src/document/DocumentCommandHandlers.js @@ -1953,6 +1953,9 @@ define(function (require, exports, module) { function handleShowInTree() { let activeFile = MainViewManager.getCurrentlyViewedFile(MainViewManager.ACTIVE_PANE); if(activeFile){ + if (!$("#sidebar").is(":visible")) { + CommandManager.execute(Commands.VIEW_HIDE_SIDEBAR); + } SidebarTabs.setActiveTab(SidebarTabs.SIDEBAR_TAB_FILES); ProjectManager.showInTree(activeFile); } diff --git a/src/extensionsIntegrated/NavigationAndHistory/NavigationProvider.js b/src/extensionsIntegrated/NavigationAndHistory/NavigationProvider.js index 304dfe6795..ba12210795 100644 --- a/src/extensionsIntegrated/NavigationAndHistory/NavigationProvider.js +++ b/src/extensionsIntegrated/NavigationAndHistory/NavigationProvider.js @@ -745,12 +745,11 @@ define(function (require, exports, module) { } function _setupNavigationButtons() { - let $mainNavBarRight = $("#mainNavBarRight"); let $mainNavBarLeft = $("#mainNavBarLeft"); - $showInTree = $mainNavBarRight.find("#showInfileTree"); - $navback = $mainNavBarRight.find("#navBackButton"); - $navForward = $mainNavBarRight.find("#navForwardButton"); - $searchNav = $mainNavBarRight.find("#searchNav"); + $showInTree = $("#showInfileTree"); + $navback = $("#navBackButton"); + $navForward = $("#navForwardButton"); + $searchNav = $("#searchNav"); $newProject = $mainNavBarLeft.find("#newProject"); updateTooltips(); diff --git a/src/index.html b/src/index.html index ac7e396d5b..7d12e4051a 100644 --- a/src/index.html +++ b/src/index.html @@ -914,10 +914,6 @@ phcode.io @@ -937,6 +933,46 @@ + +
+ +
+ +
+
+ + + +
+
+
+ + + +
+
+
+
+ + +
+
+
+
- - + + diff --git a/src/view/CentralControlBar.js b/src/view/CentralControlBar.js index 0e2a94f6b5..1ddfd4ac89 100644 --- a/src/view/CentralControlBar.js +++ b/src/view/CentralControlBar.js @@ -140,8 +140,10 @@ define(function (require, exports, module) { } editorCollapsed = wantCollapsed; $("body").toggleClass("ccb-editor-collapsed", editorCollapsed); - $("#ccbCollapseEditorBtn").toggleClass("is-active", editorCollapsed) - .attr("title", editorCollapsed ? "Expand editor" : "Collapse editor"); + const $collapseBtn = $("#ccbCollapseEditorBtn"); + $collapseBtn.toggleClass("is-active", editorCollapsed) + .attr("title", editorCollapsed ? "Switch to Code Editor" : "Switch to Visual Edit"); + $collapseBtn.find("i").attr("class", editorCollapsed ? "fa-solid fa-code" : "fa-solid fa-feather"); if (editorCollapsed) { livePreviewWasOpen = _isLivePreviewOpen(); From 7390e5c51634f88b07d96dcfb224e01747ead004 Mon Sep 17 00:00:00 2001 From: abose Date: Sat, 18 Apr 2026 12:17:55 +0530 Subject: [PATCH 03/19] fix: stabilize control-bar layout across window resizes in collapsed mode While the editor is collapsed, window resize events triggered two visible glitches: (1) the sidebar shrunk by ~1px per resize in a slow drift, because Resizer.updateResizeLimits recomputes sideBarMaxSize from sibling widths (which equals ~sidebarWidth once #main-toolbar is a full-width sibling) and clamps only when data-maxsize is a percentage; (2) #main-toolbar flickered as WorkspaceManager's _clampPluginPanelWidth capped it below our desired width before our handler reset it each frame. Fix both by pinning data-maxsize to "1000%" during collapse so the sidebar clamp is short-circuited, and by setting main-toolbar geometry with !important so the workspace clamp can't override it. Also add an applyingCollapsedLayout reentry guard on the EVENT_WORKSPACE_UPDATE_LAYOUT listener so our own recomputeLayout calls don't re-enter. --- src/view/CentralControlBar.js | 83 +++++++++++++++++++++++++---------- 1 file changed, 61 insertions(+), 22 deletions(-) diff --git a/src/view/CentralControlBar.js b/src/view/CentralControlBar.js index 1ddfd4ac89..a7d6eff04e 100644 --- a/src/view/CentralControlBar.js +++ b/src/view/CentralControlBar.js @@ -38,6 +38,7 @@ define(function (require, exports, module) { let savedToolbarWidth = null; let livePreviewWasOpen = false; let savedSidebarMaxSize = null; + let applyingCollapsedLayout = false; function _syncLeftPositions() { if (!$sidebar || !$bar || !$content) { @@ -48,8 +49,13 @@ define(function (require, exports, module) { if (!editorCollapsed) { $content.css("left", (sidebarWidth + BAR_WIDTH) + "px"); } else { + const mainToolbar = document.getElementById("main-toolbar"); const fullToolbarWidth = Math.max(0, window.innerWidth - sidebarWidth - BAR_WIDTH); - $("#main-toolbar").css({ left: (sidebarWidth + BAR_WIDTH) + "px", right: "auto", width: fullToolbarWidth + "px" }); + if (mainToolbar) { + mainToolbar.style.setProperty("left", (sidebarWidth + BAR_WIDTH) + "px", "important"); + mainToolbar.style.setProperty("right", "auto", "important"); + mainToolbar.style.setProperty("width", fullToolbarWidth + "px", "important"); + } } } @@ -84,33 +90,54 @@ define(function (require, exports, module) { } function _applyCollapsedLayout() { - const $mainToolbar = $("#main-toolbar"); - const sidebarWidth = $sidebar.is(":visible") ? ($sidebar.outerWidth() || 0) : 0; - // Use explicit px widths (not `auto`) so CSS transitions animate smoothly in - // both directions. `width: auto` on #main-toolbar would snap instead of easing. - const fullToolbarWidth = Math.max(0, window.innerWidth - sidebarWidth - BAR_WIDTH); - // Keep .content in flow (display:block) but collapse it to zero width to avoid - // TabBar infinite-loop when its ancestor becomes :hidden. visibility:hidden keeps - // layout queries stable while the bar animates out. - $content.css({ width: 0, "min-width": 0, right: "auto", visibility: "hidden", "pointer-events": "none" }); - $mainToolbar.css({ left: (sidebarWidth + BAR_WIDTH) + "px", right: "auto", width: fullToolbarWidth + "px" }); - // Sidebar's data-maxsize is "80%" of _sideBarMaxSize(), which sums non-content - // siblings of #sidebar. In collapsed mode #main-toolbar has width:auto and - // grows whenever sidebar shrinks, which feeds back into _sideBarMaxSize and - // creates a clamp-driven death spiral during sidebar drags. Pin maxsize to a - // fixed px while collapsed so the sidebar drag is purely mouse-driven. - if (savedSidebarMaxSize === null) { - savedSidebarMaxSize = $sidebar.data("maxsize"); + if (applyingCollapsedLayout) { + return; } - const fixedMax = Math.max(100, window.innerWidth - BAR_WIDTH - 100); - $sidebar.data("maxsize", fixedMax); - if (WorkspaceManager.recomputeLayout) { - WorkspaceManager.recomputeLayout(true); + applyingCollapsedLayout = true; + try { + const mainToolbar = document.getElementById("main-toolbar"); + const sidebarWidth = $sidebar.is(":visible") ? ($sidebar.outerWidth() || 0) : 0; + const fullToolbarWidth = Math.max(0, window.innerWidth - sidebarWidth - BAR_WIDTH); + // Keep .content in flow (display:block) but collapse it to zero width to avoid + // TabBar infinite-loop when its ancestor becomes :hidden. visibility:hidden keeps + // layout queries stable while the bar animates out. + $content.css({ width: 0, "min-width": 0, right: "auto", visibility: "hidden", "pointer-events": "none" }); + // Use !important on the main-toolbar geometry so WorkspaceManager's clamping + // inside handleWindowResize can't fight our width during active window resizes + // (which produced a visible width flicker in full-preview mode). + if (mainToolbar) { + mainToolbar.style.setProperty("left", (sidebarWidth + BAR_WIDTH) + "px", "important"); + mainToolbar.style.setProperty("right", "auto", "important"); + mainToolbar.style.setProperty("width", fullToolbarWidth + "px", "important"); + } + // Sidebar's data-maxsize is a percentage of _sideBarMaxSize() (main-view width + // minus all non-content siblings). In collapsed mode #main-toolbar is itself a + // huge sibling, so _sideBarMaxSize collapses to ~sidebarWidth, and every window + // resize clamps the sidebar a pixel smaller (Resizer.js updateResizeLimits) — + // producing a slow shrink a short moment after each resize. Raising the + // percentage well above 100% short-circuits the clamp in this mode while still + // coming from the same upstream code path. Numeric values are ignored by that + // code path, so we must use a percentage string. + if (savedSidebarMaxSize === null) { + savedSidebarMaxSize = $sidebar.data("maxsize"); + } + $sidebar.data("maxsize", "1000%"); + if (WorkspaceManager.recomputeLayout) { + WorkspaceManager.recomputeLayout(true); + } + } finally { + applyingCollapsedLayout = false; } } function _restoreExpandedLayout() { const $mainToolbar = $("#main-toolbar"); + const mainToolbar = $mainToolbar[0]; + if (mainToolbar) { + mainToolbar.style.removeProperty("left"); + mainToolbar.style.removeProperty("right"); + mainToolbar.style.removeProperty("width"); + } $mainToolbar.css({ left: "", right: "", width: "" }); $content.css({ width: "", "min-width": "", right: "", visibility: "", "pointer-events": "" }); // The <> button never closes live preview. If we have a saved width from @@ -226,6 +253,18 @@ define(function (require, exports, module) { _applyCollapsedLayout(); } }); + // WorkspaceManager's handleWindowResize runs _clampPluginPanelWidth on every + // window resize, which caps #main-toolbar at min(innerWidth * 0.75, innerWidth + // - sidebar - 100). That's tighter than what we want in collapsed mode (fill + // to innerWidth - sidebar - 30), leaving a blank gap on the right. Listen for + // the layout-update event that fires after the clamp and reassert our widths. + // The applyingCollapsedLayout guard breaks the recursion because our own + // recomputeLayout call would otherwise re-enter this handler. + WorkspaceManager.on(WorkspaceManager.EVENT_WORKSPACE_UPDATE_LAYOUT + ".ccb", function () { + if (editorCollapsed) { + _applyCollapsedLayout(); + } + }); // Dragging the plugin toolbar's left resizer while collapsed should exit the // collapsed state so the user can resize live preview normally. From ab2006764404b0c3f7a5caa2011b101526fe6cc0 Mon Sep 17 00:00:00 2001 From: abose Date: Sat, 18 Apr 2026 13:08:46 +0530 Subject: [PATCH 04/19] feat: move sidebar resizer to right of control bar and unify drag in design mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sidebar's right-resizer handle now visually sits to the right of #centralControlBar (instead of between the sidebar and the control bar), using a CSS transform so Resizer.repositionResizer can keep its own internal left offset. The same shift applies when the sidebar is hidden and the handle is reparented to .main-view. In design mode (editor collapsed), the main-toolbar's own left resizer is hidden — previously dragging it exited design mode. Now the single sidebar resizer acts as the sidebar↔live-preview splitter so the split can be adjusted without leaving the mode. Bumped sidebar data-minsize from 0 to 30 so drag can't auto-collapse the sidebar below the control-bar width; the dedicated sidebar-toggle button still fully hides it. --- src/index.html | 2 +- src/styles/CentralControlBar.less | 19 +++++++++++++++++++ src/view/CentralControlBar.js | 7 ------- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/index.html b/src/index.html index cec812874f..9a0678364f 100644 --- a/src/index.html +++ b/src/index.html @@ -905,7 +905,7 @@
-