diff --git a/src/index.html b/src/index.html index 4d89b9020..88d90271b 100644 --- a/src/index.html +++ b/src/index.html @@ -937,7 +937,10 @@
- + diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js index 7350fda57..482fec812 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -2074,6 +2074,7 @@ define({ "PROMO_LEARN_MORE": "Learn More\u2026", "PROMO_OPT_OUT_LINK": "Opt out?", "PROMO_OPT_OUT_NOTE": "You can cancel your trial anytime by selecting `Help > Cancel Phoenix Pro Trial`.", + "PROMO_OPT_OUT_DIALOG_TITLE": "Cancel Phoenix Pro Trial", "PROMO_UPGRADE_APP_UPSELL_BUTTON": "Upgrade to {0}", "PROMO_PRO_ENDED_TITLE": "{0} trial ended", "PROMO_PHOENIX_PRO_ENDED_MESSAGE": "Thanks for trying Pro! You're now on the Free Community Edition.
Upgrade anytime to unlock these features:", diff --git a/src/styles/CentralControlBar.less b/src/styles/CentralControlBar.less index ce0198976..d6c511563 100644 --- a/src/styles/CentralControlBar.less +++ b/src/styles/CentralControlBar.less @@ -101,6 +101,12 @@ /* Nudge the collapse-editor icon up so it visually aligns with titlebar icons */ #ccbCollapseEditorBtn { margin-top: -2px; + + svg { + width: 15px; + height: 15px; + pointer-events: none; + } } /* Nav buttons: suppress legacy sprite styles (NavigationProvider toggles enabled/disabled class) */ diff --git a/src/styles/images/pen_nib.svg b/src/styles/images/pen_nib.svg new file mode 100644 index 000000000..97b5b2c98 --- /dev/null +++ b/src/styles/images/pen_nib.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/utils/Resizer.js b/src/utils/Resizer.js index 9ae4e2988..3809aea9f 100644 --- a/src/utils/Resizer.js +++ b/src/utils/Resizer.js @@ -503,22 +503,26 @@ define(function (require, exports, module) { previousSize = newSize; if ($element.is(":visible")) { - if (newSize < 10) { + if (collapsible && newSize < 10) { toggle($element); elementSizeFunction.apply($element, [0]); } else { - // Trigger resizeStarted just before the first successful resize update + // Non-collapsible panels must never be auto-hidden by a drag that + // squeezes them to near-zero — a narrow window with a wide sidebar + // can push maxsize below 10, and hiding the toolbar that way + // strands the user with no way to get it back. + var effectiveSize = collapsible ? newSize : Math.max(newSize, minSize); if (!resizeStarted) { resizeStarted = true; - $element.trigger(EVENT_PANEL_RESIZE_START, newSize); + $element.trigger(EVENT_PANEL_RESIZE_START, effectiveSize); } // Resize the main element to the new size. If there is a content element, // its size is the new size minus the size of the non-resizable elements - resizeElement(newSize, (newSize - baseSize)); - adjustSibling(newSize); + resizeElement(effectiveSize, (effectiveSize - baseSize)); + adjustSibling(effectiveSize); - $element.trigger(EVENT_PANEL_RESIZE_UPDATE, [newSize]); + $element.trigger(EVENT_PANEL_RESIZE_UPDATE, [effectiveSize]); } } else if (newSize > 10) { elementSizeFunction.apply($element, [newSize]); diff --git a/src/view/CentralControlBar.js b/src/view/CentralControlBar.js index 098a28699..904e21a06 100644 --- a/src/view/CentralControlBar.js +++ b/src/view/CentralControlBar.js @@ -40,6 +40,7 @@ define(function (require, exports, module) { let livePreviewWasOpen = false; let savedSidebarMaxSize = null; let applyingCollapsedLayout = false; + let penNibIconHTML = null; function _getRenderedSidebarWidth() { // Use offsetWidth (not jQuery's outerWidth) to force a synchronous reflow @@ -238,7 +239,7 @@ define(function (require, exports, module) { const $collapseBtn = $("#ccbCollapseEditorBtn"); $collapseBtn.toggleClass("is-active", editorCollapsed) .attr("title", editorCollapsed ? Strings.CCB_SWITCH_TO_CODE_EDITOR : Strings.CCB_SWITCH_TO_DESIGN_MODE); - $collapseBtn.find("i").attr("class", editorCollapsed ? "fa-solid fa-code" : "fa-solid fa-feather"); + $collapseBtn.html(editorCollapsed ? '' : penNibIconHTML); if (_toggleDesignModeCommand) { _toggleDesignModeCommand.setChecked(editorCollapsed); } @@ -303,6 +304,10 @@ define(function (require, exports, module) { $fileLabel = $("#ccbFileLabel"); $fileName = $fileLabel.find(".ccb-file-name"); + // Cache the authored pen-nib SVG from the DOM so the toggle handler + // can restore it after swapping in the fa-code icon for design mode. + penNibIconHTML = $("#ccbCollapseEditorBtn").html(); + _wireButtons(); // The HTML titles on the control-bar buttons are fallback English // strings; set the localized versions up front so the initial render diff --git a/src/view/WorkspaceManager.js b/src/view/WorkspaceManager.js index 6cabd82c2..77b223997 100644 --- a/src/view/WorkspaceManager.js +++ b/src/view/WorkspaceManager.js @@ -88,6 +88,20 @@ define(function (require, exports, module) { */ const MAIN_TOOLBAR_WIDTH = 30; + /** + * Returns the sidebar's rendered width, or 0 if hidden. jQuery's outerWidth() + * on a display:none element that still carries an explicit style.width returns + * that stale width rather than 0, which corrupts main-view layout math. + * @private + */ + function _getSidebarWidth() { + var sb = document.getElementById("sidebar"); + if (!sb || sb.offsetWidth === 0) { + return 0; + } + return sb.offsetWidth; + } + /** * The ".content" vertical stack (editor + all header/footer panels) * @type {jQueryObject} @@ -184,8 +198,14 @@ define(function (require, exports, module) { } }); - var sidebarWidth = $("#sidebar").outerWidth() || 0; - $mainToolbar.data("maxsize", Math.min(window.innerWidth * 0.75, window.innerWidth - sidebarWidth - 100)); + var sidebarWidth = _getSidebarWidth(); + var pluginIconsBarWidth = $pluginIconsBar.outerWidth() || MAIN_TOOLBAR_WIDTH; + var minToolbarWidth = ((currentlyShownPanel && currentlyShownPanel.minWidth) || 0) + pluginIconsBarWidth; + // Floor the toolbar's maxsize at its minimum width. Without the floor, a narrow + // window with a wide sidebar can drive the cap below 10px, and Resizer's drag + // logic would then squeeze the toolbar to zero and hide it. + var rawMax = Math.min(window.innerWidth * 0.75, window.innerWidth - sidebarWidth - 100); + $mainToolbar.data("maxsize", Math.max(minToolbarWidth, rawMax)); } @@ -224,6 +244,8 @@ define(function (require, exports, module) { if (currentlyShownPanel && $mainToolbar.is(":visible")) { _clampPluginPanelWidth(currentlyShownPanel); } + // Then coordinate sidebar width in editor mode so the three regions fit. + _ensureEditorLayoutFits(); // FIXME (issue #4564) Workaround https://github.com/codemirror/CodeMirror/issues/1787 triggerUpdateLayout(); @@ -375,6 +397,19 @@ define(function (require, exports, module) { $mainPluginPanel = $("#main-plugin-panel"); $pluginIconsBar = $("#plugin-icons-bar"); + // Sidebar show/resize steals width from the plugin-panel area. Without + // re-clamping, the main-toolbar keeps its previous (now-oversized) width + // and overlaps the sidebar/editor. Mirror the window-resize handling. + $("#sidebar").on("panelExpanded.workspace panelCollapsed.workspace panelResizeEnd.workspace", + function () { + if (currentlyShownPanel && $mainToolbar.is(":visible")) { + _clampPluginPanelWidth(currentlyShownPanel); + } + _ensureEditorLayoutFits(); + triggerUpdateLayout(); + updateResizeLimits(); + }); + // --- Create the bottom panel tabbed container --- $bottomPanelContainer = $('
'); let $bottomPanelTabBar = $('
'); @@ -530,7 +565,7 @@ define(function (require, exports, module) { } function _clampPluginPanelWidth(panelBeingShown) { - let sidebarWidth = $("#sidebar").outerWidth() || 0; + let sidebarWidth = _getSidebarWidth(); let pluginIconsBarWidth = $pluginIconsBar.outerWidth(); let minToolbarWidth = (panelBeingShown.minWidth || 0) + pluginIconsBarWidth; let maxToolbarWidth = Math.max( @@ -546,6 +581,45 @@ define(function (require, exports, module) { } } + const CCB_WIDTH = 30; + const MIN_EDITOR_WIDTH = 100; + const MIN_SIDEBAR_WIDTH = 30; + + /** + * In editor mode the sidebar, editor, and plugin-panel (main-toolbar) must + * share the horizontal space. Resizer's own sidebar clamp runs lazily on + * the next mousemove, and only considers percent-based max sizes — so a + * window shrink, or showing the sidebar at its remembered width, can leave + * the sidebar overlapping the live-preview panel. Here we do an eager + * coordinated shrink: clamp sidebar so sidebar + CCB + current toolbar + + * MIN_EDITOR_WIDTH fits in the window. Design mode handles its own + * geometry with !important, so this is a no-op there. + * @private + */ + function _ensureEditorLayoutFits() { + if (_isInDesignMode) { + return; + } + var $sb = $("#sidebar"); + if (!$sb.length || $sb[0].offsetWidth === 0) { + return; + } + var toolbarW = $mainToolbar.is(":visible") ? $mainToolbar.width() : 0; + var maxSidebar = window.innerWidth - CCB_WIDTH - toolbarW - MIN_EDITOR_WIDTH; + var currentSb = $sb[0].offsetWidth; + if (currentSb > maxSidebar) { + var newSb = Math.max(MIN_SIDEBAR_WIDTH, maxSidebar); + $sb.width(newSb); + var resync = $sb.data("resyncSizer"); + if (typeof resync === "function") { + resync(); + } + // Notify listeners (CCB syncs its left offset; content tracks via forceleft). + $sb.trigger("panelResizeUpdate", [newSb]); + $sb.trigger("panelResizeEnd", [newSb]); + } + } + function _showPluginSidePanel(panelID) { let panelBeingShown = getPanelForID(panelID); let pluginIconsBarWidth = $pluginIconsBar.outerWidth(); @@ -615,7 +689,7 @@ define(function (require, exports, module) { // Respect min/max constraints var minSize = currentlyShownPanel.minWidth || 0; var minToolbarWidth = minSize + pluginIconsBarWidth; - var sidebarWidth = $("#sidebar").outerWidth() || 0; + var sidebarWidth = _getSidebarWidth(); var maxToolbarWidth = Math.min(window.innerWidth * 0.75, window.innerWidth - sidebarWidth - 100); newToolbarWidth = Math.max(newToolbarWidth, minToolbarWidth); newToolbarWidth = Math.min(newToolbarWidth, maxToolbarWidth); diff --git a/tracking-repos.json b/tracking-repos.json index 7eab94bfa..e555dba24 100644 --- a/tracking-repos.json +++ b/tracking-repos.json @@ -1,5 +1,5 @@ { "phoenixPro": { - "commitID": "227551b263c5d4ea5233e3920639bc5aa493b321" + "commitID": "59e1d73841f94f546ad9b62a76217eb0a1e450e8" } }