diff --git a/docs/API-Reference/view/PanelView.md b/docs/API-Reference/view/PanelView.md
index 544969a668..78d9eee962 100644
--- a/docs/API-Reference/view/PanelView.md
+++ b/docs/API-Reference/view/PanelView.md
@@ -141,6 +141,7 @@ Preference key for persisting the maximize state across reloads.
* [.registerOnCloseRequestedHandler(handler)](#Panel+registerOnCloseRequestedHandler)
* [.requestClose()](#Panel+requestClose) ⇒ Promise.<boolean>
* [.show()](#Panel+show)
+ * [.addToTabBar()](#Panel+addToTabBar)
* [.hide()](#Panel+hide)
* [.focus()](#Panel+focus) ⇒ boolean
* [.setVisible(visible)](#Panel+setVisible)
@@ -206,6 +207,14 @@ registered, `hide()` is called.
### panel.show()
Shows the panel
+**Kind**: instance method of [Panel](#Panel)
+
+
+### panel.addToTabBar()
+Adds the panel to the tab bar and open set without showing the container.
+Use this during startup to restore a panel's tab when the container
+was collapsed by the user — avoids forcing the bottom panel open.
+
**Kind**: instance method of [Panel](#Panel)
@@ -303,6 +312,26 @@ When the saved height is near-max or unknown, a sensible default is used.
## isMaximized() ⇒ boolean
Returns true if the bottom panel is currently maximized.
+**Kind**: global function
+
+
+## collapseContainer()
+Collapse the bottom panel container (transient hide) without touching
+which panel is logically active. Fires EVENT_PANEL_HIDDEN with the
+default panel id as a "container collapsed" signal so toolbar icons
+and menu items that mirror container visibility deselect.
+No-op if the container is already hidden.
+
+**Kind**: global function
+
+
+## restoreContainer()
+Re-show the bottom panel container after a previous collapse, with the
+previously active panel still mounted. Fires EVENT_PANEL_SHOWN for the
+active panel id so toolbar icons / menu items that mirror visibility
+re-select. No-op if the container is already visible or there's no
+active panel to restore.
+
**Kind**: global function
diff --git a/src/LiveDevelopment/BrowserScripts/RemoteFunctions.js b/src/LiveDevelopment/BrowserScripts/RemoteFunctions.js
index 81964e71c5..5fe5e77665 100644
--- a/src/LiveDevelopment/BrowserScripts/RemoteFunctions.js
+++ b/src/LiveDevelopment/BrowserScripts/RemoteFunctions.js
@@ -918,7 +918,9 @@ function RemoteFunctions(config = {}) {
const nodes = window.document.querySelectorAll(rule);
- // Highlight all matching nodes
+ // Highlight all matching nodes. selectElement() will narrow _clickHighlight
+ // down to the chosen element below; createCssSelectorHighlight() then
+ // re-highlights the siblings in a separate overlay.
for (let i = 0; i < nodes.length; i++) {
highlight(nodes[i]);
}
@@ -927,22 +929,23 @@ function RemoteFunctions(config = {}) {
_clickHighlight.selector = rule;
}
- // In edit mode, select the best element and create temporary highlights for the rest.
- // In highlight mode, skip selection so all matching elements stay highlighted equally.
- if (config.mode === 'edit') {
- const { element, skipSelection } = findBestElementToSelect(nodes, rule);
-
- if (!skipSelection) {
- if (element) {
- selectElement(element, true);
- } else {
- // No valid element found, dismiss UI
- dismissUIAndCleanupState();
- }
- }
+ // Both edit and highlight modes go through the same selection path:
+ // selectElement() handles scroll-to-view and the prominent click-highlight,
+ // createCssSelectorHighlight() shows siblings dimly. fromEditor=true
+ // suppresses tool-handler invocation, so highlight mode gets the
+ // highlighting/scroll behavior without any UI boxes.
+ const { element, skipSelection } = findBestElementToSelect(nodes, rule);
- createCssSelectorHighlight(nodes, rule);
+ if (!skipSelection) {
+ if (element) {
+ selectElement(element, true);
+ } else {
+ // No valid element found, dismiss UI
+ dismissUIAndCleanupState();
+ }
}
+
+ createCssSelectorHighlight(nodes, rule);
}
// recreate UI boxes so that they are placed properly
diff --git a/src/extensions/default/Git/src/Panel.js b/src/extensions/default/Git/src/Panel.js
index 41a509cbb5..d86d19692b 100644
--- a/src/extensions/default/Git/src/Panel.js
+++ b/src/extensions/default/Git/src/Panel.js
@@ -1358,7 +1358,16 @@ define(function (require, exports) {
// Show gitPanel when appropriate
if (Preferences.get("panelEnabled") && Setup.isExtensionActivated()) {
- toggle(true);
+ // If the bottom panel container is collapsed, just add the Git tab
+ // without forcing it open. The user collapsed it intentionally.
+ const $container = $("#bottom-panel-container");
+ if ($container.is(":visible")) {
+ toggle(true);
+ } else {
+ gitPanel.addToTabBar();
+ $("#git-toolbar-icon").removeClass("forced-hidden");
+ refresh();
+ }
}
_panelResized();
GutterManager.init();
@@ -1510,6 +1519,23 @@ define(function (require, exports) {
CommandManager.get(Constants.CMD_GIT_TOGGLE_PANEL).setChecked(false);
Preferences.set("panelEnabled", false);
}
+ // When the bottom panel container is collapsed, deselect the icon
+ // but don't save preference — the panel is still logically open.
+ if (panelID === WorkspaceManager.DEFAULT_PANEL_ID && Main.$icon) {
+ Main.$icon.toggleClass("on", false);
+ Main.$icon.toggleClass("selected-button", false);
+ CommandManager.get(Constants.CMD_GIT_TOGGLE_PANEL).setChecked(false);
+ }
+ });
+
+ // When any bottom panel is shown (container is visible),
+ // re-select the git icon if git panel is still open.
+ WorkspaceManager.on(WorkspaceManager.EVENT_WORKSPACE_PANEL_SHOWN, function (event, panelID) {
+ if (Main.$icon && Preferences.get("panelEnabled")) {
+ Main.$icon.toggleClass("on", true);
+ Main.$icon.toggleClass("selected-button", true);
+ CommandManager.get(Constants.CMD_GIT_TOGGLE_PANEL).setChecked(true);
+ }
});
exports.init = init;
diff --git a/src/extensionsIntegrated/CustomSnippets/main.js b/src/extensionsIntegrated/CustomSnippets/main.js
index 3235942cba..e88122f448 100644
--- a/src/extensionsIntegrated/CustomSnippets/main.js
+++ b/src/extensionsIntegrated/CustomSnippets/main.js
@@ -255,14 +255,33 @@ define(function (require, exports, module) {
});
}
+ // Track whether the snippets panel has an open tab (survives container collapse).
+ let _snippetsTabOpen = false;
+
// When the panel tab is closed externally (e.g. via the × button),
// update the menu checked state to stay in sync.
WorkspaceManager.on(WorkspaceManager.EVENT_WORKSPACE_PANEL_HIDDEN, function (event, panelID) {
if (panelID === PANEL_ID && customSnippetsPanel) {
+ _snippetsTabOpen = false;
+ CommandManager.get(MY_COMMAND_ID).setChecked(false);
+ }
+ // Container collapsed — uncheck menu item but keep tab-open flag
+ if (panelID === WorkspaceManager.DEFAULT_PANEL_ID) {
CommandManager.get(MY_COMMAND_ID).setChecked(false);
}
});
+ // When any bottom panel is shown (container is visible),
+ // re-check menu item if snippets panel still has an open tab.
+ WorkspaceManager.on(WorkspaceManager.EVENT_WORKSPACE_PANEL_SHOWN, function (event, panelID) {
+ if (panelID === PANEL_ID) {
+ _snippetsTabOpen = true;
+ }
+ if (_snippetsTabOpen) {
+ CommandManager.get(MY_COMMAND_ID).setChecked(true);
+ }
+ });
+
AppInit.appReady(function () {
CommandManager.register(MENU_ITEM_NAME, MY_COMMAND_ID, showCustomSnippetsPanel);
// Render template with localized strings
diff --git a/src/extensionsIntegrated/DisplayShortcuts/main.js b/src/extensionsIntegrated/DisplayShortcuts/main.js
index 3c71d6b0c2..330ccb4225 100644
--- a/src/extensionsIntegrated/DisplayShortcuts/main.js
+++ b/src/extensionsIntegrated/DisplayShortcuts/main.js
@@ -539,14 +539,33 @@ define(function (require, exports, module) {
KeyBindingManager.on(KeyBindingManager.EVENT_NEW_PRESET, _updatePresets);
KeyBindingManager.on(KeyBindingManager.EVENT_PRESET_CHANGED, _updatePresets);
+ // Track whether the shortcuts panel has an open tab (survives container collapse).
+ let _shortcutsTabOpen = false;
+
// When the panel tab is closed externally (e.g. via the × button),
// update the menu checked state and clean up resources.
WorkspaceManager.on(WorkspaceManager.EVENT_WORKSPACE_PANEL_HIDDEN, function (event, panelID) {
if (panelID === TOGGLE_SHORTCUTS_ID && panel) {
+ _shortcutsTabOpen = false;
destroyKeyList();
_clearSortingEventHandlers();
CommandManager.get(TOGGLE_SHORTCUTS_ID).setChecked(false);
}
+ // Container collapsed — uncheck menu item but keep tab-open flag
+ if (panelID === WorkspaceManager.DEFAULT_PANEL_ID) {
+ CommandManager.get(TOGGLE_SHORTCUTS_ID).setChecked(false);
+ }
+ });
+
+ // When any bottom panel is shown (container is visible),
+ // re-check menu item if shortcuts panel still has an open tab.
+ WorkspaceManager.on(WorkspaceManager.EVENT_WORKSPACE_PANEL_SHOWN, function (event, panelID) {
+ if (panelID === TOGGLE_SHORTCUTS_ID) {
+ _shortcutsTabOpen = true;
+ }
+ if (_shortcutsTabOpen) {
+ CommandManager.get(TOGGLE_SHORTCUTS_ID).setChecked(true);
+ }
});
});
});
diff --git a/src/extensionsIntegrated/Phoenix-live-preview/live-preview.css b/src/extensionsIntegrated/Phoenix-live-preview/live-preview.css
index 98451f774e..e4f64a51e5 100644
--- a/src/extensionsIntegrated/Phoenix-live-preview/live-preview.css
+++ b/src/extensionsIntegrated/Phoenix-live-preview/live-preview.css
@@ -155,22 +155,48 @@
transform: translateX(-50%);
z-index: 10;
display: none;
- padding: 6px 16px;
+ padding: 6px 14px;
border-radius: 6px;
background: rgba(0, 0, 0, 0.88);
border: 1px solid rgba(255, 255, 255, 0.18);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
color: #fff;
- font-size: 15px;
+ font-size: 13px;
font-weight: 600;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
white-space: nowrap;
pointer-events: none;
- letter-spacing: 0.5px;
+ letter-spacing: 0.3px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.06);
}
+.responsive-dimension-label .responsive-dimension-device {
+ color: rgba(255, 255, 255, 0.7);
+ font-weight: 400;
+}
+
+.responsive-dimension-label .responsive-dimension-device i {
+ margin-right: 5px;
+ font-size: 11px;
+ vertical-align: middle;
+}
+
+.responsive-dimension-label .responsive-dimension-name {
+ vertical-align: middle;
+}
+
+.responsive-dimension-label .responsive-dimension-separator {
+ color: rgba(255, 255, 255, 0.35);
+ margin: 0 6px;
+ font-weight: 400;
+}
+
+.responsive-dimension-label .responsive-dimension-size {
+ color: #fff;
+ font-weight: 600;
+}
+
.plugin-toolbar {
height: var(--toolbar-height);
color: #a0a0a0;
@@ -321,6 +347,10 @@
font-size: inherit;
}
+.device-size-item-icon-fit {
+ font-size: 11px;
+}
+
.device-size-item-row {
display: flex;
justify-content: space-between;
@@ -340,50 +370,78 @@
color: rgba(100, 180, 255, 0.8);
}
-#livePreviewModeBtn {
- min-width: fit-content;
+.lp-mode-btn-group {
display: flex;
align-items: center;
+ flex-shrink: 0;
margin: 0 4px 0 3px;
- max-width: 80%;
- text-overflow: ellipsis;
- overflow: hidden;
- white-space: nowrap;
- cursor: pointer;
- background: transparent;
- box-shadow: none;
border: 1px solid transparent;
+ border-radius: 3px;
box-sizing: border-box;
+}
+
+.lp-mode-btn-group:hover {
+ border-color: rgba(255, 255, 255, 0.1);
+}
+
+.lp-mode-icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 22px;
+ height: 22px;
+ cursor: pointer;
+ background: transparent;
+ box-shadow: none !important;
+ border: none;
color: #a0a0a0;
- font-size: 14px;
- font-weight: normal;
- padding: 0 0.35em;
+ padding: 0;
+ margin: 0;
}
-#livePreviewModeBtn:hover,
-#livePreviewModeBtn:focus,
-#livePreviewModeBtn:active {
+#live-preview-plugin-toolbar .lp-mode-icon:hover,
+#live-preview-plugin-toolbar .lp-mode-icon:focus,
+#live-preview-plugin-toolbar .lp-mode-icon:active {
background: transparent !important;
- border: 1px solid rgba(255, 255, 255, 0.1) !important;
+ border: none !important;
box-shadow: none !important;
}
-#livePreviewModeBtn.btn-dropdown::after {
- position: static;
- margin-top: 2px;
- margin-left: 5px;
+.lp-mode-icon.selected {
+ color: #FBB03B;
}
-#reloadLivePreviewButton {
+.lp-mode-dropdown-chevron {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ background: transparent;
+ box-shadow: none !important;
+ border: none;
+ border-left: 1px solid transparent;
+ border-radius: 0 3px 3px 0;
color: #a0a0a0;
- margin-left: 3px;
- margin-top: 0;
- width: 30px;
+ padding: 0 4px;
+ margin: 0;
height: 22px;
- flex-shrink: 0;
+ font-size: 10px;
}
-#designModeToggleLivePreviewButton {
+.lp-mode-btn-group:hover .lp-mode-dropdown-chevron {
+ border-left-color: rgba(255, 255, 255, 0.1);
+}
+
+#live-preview-plugin-toolbar .lp-mode-dropdown-chevron:hover,
+#live-preview-plugin-toolbar .lp-mode-dropdown-chevron:focus,
+#live-preview-plugin-toolbar .lp-mode-dropdown-chevron:active {
+ background: transparent !important;
+ border: none !important;
+ border-left: 1px solid rgba(255, 255, 255, 0.1) !important;
+ box-shadow: none !important;
+}
+
+#reloadLivePreviewButton {
color: #a0a0a0;
margin-left: 3px;
margin-top: 0;
@@ -392,7 +450,7 @@
flex-shrink: 0;
}
-#previewModeLivePreviewButton {
+#designModeToggleLivePreviewButton {
color: #a0a0a0;
margin-left: 3px;
margin-top: 0;
@@ -401,10 +459,6 @@
flex-shrink: 0;
}
-#previewModeLivePreviewButton.selected{
- color: #FBB03B;
-}
-
.live-preview-status-overlay {
display: flex;
flex-direction: row;
diff --git a/src/extensionsIntegrated/Phoenix-live-preview/main.js b/src/extensionsIntegrated/Phoenix-live-preview/main.js
index e83743ea01..c2bbaafa18 100644
--- a/src/extensionsIntegrated/Phoenix-live-preview/main.js
+++ b/src/extensionsIntegrated/Phoenix-live-preview/main.js
@@ -169,6 +169,7 @@ define(function (require, exports, module) {
$firefoxButtonBallast,
$panelTitle,
$modeBtn,
+ $modeBtnGroup,
$previewBtn,
$designModeBtn;
@@ -343,26 +344,19 @@ define(function (require, exports, module) {
*/
function _updateLPControlsForMdviewer() {
const inDesignMode = WorkspaceManager.isInDesignMode && WorkspaceManager.isInDesignMode();
+ const showPen = !_isMdviewrActive;
+ // Dropdown is also hidden in design mode (see $designModeBtn wiring in
+ // _createExtensionPanel) because the preview-mode options are moot
+ // when the editor is fully collapsed.
+ const showChevron = !_isMdviewrActive && !inDesignMode;
if ($previewBtn) {
- $previewBtn.toggle(!_isMdviewrActive);
+ $previewBtn.toggle(showPen);
}
if ($modeBtn) {
- // Also hidden in design mode (see $designModeBtn wiring in
- // _createExtensionPanel) because the preview-mode dropdown is moot
- // when the editor is fully collapsed.
- $modeBtn.toggle(!_isMdviewrActive && !inDesignMode);
+ $modeBtn.toggle(showChevron);
}
- }
-
- function _updateModeButton(mode) {
- if ($modeBtn) {
- if (mode === "highlight") {
- $modeBtn[0].textContent = Strings.LIVE_PREVIEW_MODE_HIGHLIGHT;
- } else if (mode === "edit") {
- $modeBtn[0].textContent = Strings.LIVE_PREVIEW_MODE_EDIT;
- } else {
- $modeBtn[0].textContent = Strings.LIVE_PREVIEW_MODE_PREVIEW;
- }
+ if ($modeBtnGroup && $modeBtnGroup.length) {
+ $modeBtnGroup.toggle(showPen || showChevron);
}
}
@@ -371,14 +365,12 @@ define(function (require, exports, module) {
// Pencil button lights up only when edit mode is active; preview /
// highlight modes leave it un-tinted. Click toggles between edit
- // and preview.
+ // and preview. The chevron next to it opens the full mode dropdown.
if (currentMode === LiveDevelopment.CONSTANTS.LIVE_EDIT_MODE) {
$previewBtn.addClass('selected');
} else {
$previewBtn.removeClass('selected');
}
-
- _updateModeButton(currentMode);
}
function _showModeSelectionDropdown(event) {
@@ -809,6 +801,7 @@ define(function (require, exports, module) {
$panelTitle = $panel.find("#panel-live-preview-title");
$settingsIcon = $panel.find("#livePreviewSettingsBtn");
$modeBtn = $panel.find("#livePreviewModeBtn");
+ $modeBtnGroup = $panel.find("#lpModeBtnGroup");
$previewBtn = $panel.find("#previewModeLivePreviewButton");
$designModeBtn = $panel.find("#designModeToggleLivePreviewButton");
diff --git a/src/extensionsIntegrated/Phoenix-live-preview/panel.html b/src/extensionsIntegrated/Phoenix-live-preview/panel.html
index d62408e8f7..f6c33b3e34 100644
--- a/src/extensionsIntegrated/Phoenix-live-preview/panel.html
+++ b/src/extensionsIntegrated/Phoenix-live-preview/panel.html
@@ -7,10 +7,14 @@
-
-
+
+
+
+