diff --git a/src/browser/base/content/ZenUIManager.mjs b/src/browser/base/content/ZenUIManager.mjs
index f669c4924..cdb6c75c0 100644
--- a/src/browser/base/content/ZenUIManager.mjs
+++ b/src/browser/base/content/ZenUIManager.mjs
@@ -18,6 +18,10 @@ var gZenUIManager = {
return document.getElementById('zen-toast-container');
});
+ ChromeUtils.defineLazyGetter(this, '_toastContainer', () => {
+ return document.getElementById('zen-toast-container');
+ });
+
new ResizeObserver(this.updateTabsToolbar.bind(this)).observe(document.getElementById('TabsToolbar'));
new ResizeObserver(
@@ -245,6 +249,38 @@ var gZenUIManager = {
this._toastContainer.setAttribute('hidden', 'true');
}
},
+
+ // Section: Notification messages
+ _createToastElement(messageId, options) {
+ const element = document.createXULElement('vbox');
+ const label = document.createXULElement('label');
+ document.l10n.setAttributes(label, messageId, options);
+ element.appendChild(label);
+ if (options.descriptionId) {
+ const description = document.createXULElement('label');
+ description.classList.add('description');
+ document.l10n.setAttributes(description, options.descriptionId, options);
+ element.appendChild(description);
+ }
+ element.classList.add('zen-toast');
+ return element;
+ },
+
+ async showToast(messageId, options = {}) {
+ const toast = this._createToastElement(messageId, options);
+ this._toastContainer.removeAttribute('hidden');
+ this._toastContainer.appendChild(toast);
+ await this.motion.animate(toast, { opacity: [0, 1], scale: [0.8, 1] }, { type: 'spring', duration: 0.3 });
+ await new Promise((resolve) => setTimeout(resolve, 3000));
+ await this.motion.animate(toast, { opacity: [1, 0], scale: [1, 0.9] }, { duration: 0.2, bounce: 0 });
+ const toastHeight = toast.getBoundingClientRect().height;
+ // 5 for the separation between toasts
+ await this.motion.animate(toast, { marginBottom: [0, `-${toastHeight + 5}px`] }, { duration: 0.2 });
+ toast.remove();
+ if (!this._toastContainer.hasChildNodes()) {
+ this._toastContainer.setAttribute('hidden', 'true');
+ }
+ },
};
var gZenVerticalTabsManager = {
@@ -741,4 +777,85 @@ var gZenVerticalTabsManager = {
this._tabEdited = null;
},
+
+ renameTabKeydown(event) {
+ if (event.key === 'Enter') {
+ let label = this._tabEdited.querySelector('.tab-label-container-editing');
+ let input = this._tabEdited.querySelector('#tab-label-input');
+ let newName = input.value.trim();
+
+ // Check if name is blank, reset if so
+ // Always remove, so we can always rename and if it's empty,
+ // it will reset to the original name anyway
+ this._tabEdited.removeAttribute('zen-has-static-label');
+ if (newName) {
+ gBrowser._setTabLabel(this._tabEdited, newName);
+ this._tabEdited.setAttribute('zen-has-static-label', 'true');
+ } else {
+ gBrowser.setTabTitle(this._tabEdited);
+ }
+
+ // Maybe add some confetti here?!?
+ gZenUIManager.motion.animate(
+ this._tabEdited,
+ {
+ scale: [1, 0.98, 1],
+ },
+ {
+ duration: 0.25,
+ }
+ );
+
+ this._tabEdited.querySelector('.tab-editor-container').remove();
+ label.classList.remove('tab-label-container-editing');
+
+ this._tabEdited = null;
+ } else if (event.key === 'Escape') {
+ event.target.blur();
+ }
+ },
+
+ renameTabStart(event) {
+ if (
+ this._tabEdited ||
+ !Services.prefs.getBoolPref('zen.tabs.rename-tabs') ||
+ Services.prefs.getBoolPref('browser.tabs.closeTabByDblclick') ||
+ !gZenVerticalTabsManager._prefsSidebarExpanded
+ )
+ return;
+ this._tabEdited = event.target.closest('.tabbrowser-tab');
+ if (!this._tabEdited || !this._tabEdited.pinned || this._tabEdited.hasAttribute('zen-essential')) {
+ this._tabEdited = null;
+ return;
+ }
+ const label = this._tabEdited.querySelector('.tab-label-container');
+ label.classList.add('tab-label-container-editing');
+
+ const container = window.MozXULElement.parseXULToFragment(`
+
+ `);
+ label.after(container);
+ const containerHtml = this._tabEdited.querySelector('.tab-editor-container');
+ const input = document.createElement('input');
+ input.id = 'tab-label-input';
+ input.value = this._tabEdited.label;
+ input.addEventListener('keydown', this.renameTabKeydown.bind(this));
+
+ containerHtml.appendChild(input);
+ input.focus();
+ input.select();
+
+ input.addEventListener('blur', this._renameTabHalt);
+ },
+
+ renameTabHalt(event) {
+ if (document.activeElement === event.target || !this._tabEdited) {
+ return;
+ }
+ this._tabEdited.querySelector('.tab-editor-container').remove();
+ const label = this._tabEdited.querySelector('.tab-label-container-editing');
+ label.classList.remove('tab-label-container-editing');
+
+ this._tabEdited = null;
+ },
};
diff --git a/src/browser/base/content/zen-styles/zen-tabs/vertical-tabs.css b/src/browser/base/content/zen-styles/zen-tabs/vertical-tabs.css
index e0e834675..8689e6a68 100644
--- a/src/browser/base/content/zen-styles/zen-tabs/vertical-tabs.css
+++ b/src/browser/base/content/zen-styles/zen-tabs/vertical-tabs.css
@@ -325,6 +325,9 @@
top: 0px;
right: 0px;
--tab-collapsed-width: 34px;
+ top: 0px;
+ right: 0px;
+ --tab-collapsed-width: 34px;
--tab-min-height: 16px;
width: var(--tab-collapsed-width) !important;
z-index: 1;
@@ -334,6 +337,11 @@
background: light-dark(rgb(249, 249, 249), rgb(63, 63, 63)) !important;
border: 2px solid light-dark(rgba(0, 0, 0, 0.4), rgba(255, 255, 255, 0.4));
}
+ & .tab-background {
+ /* Solid colors because we don't want to show the background */
+ background: light-dark(rgb(249, 249, 249), rgb(63, 63, 63)) !important;
+ border: 2px solid light-dark(rgba(0, 0, 0, 0.4), rgba(255, 255, 255, 0.4));
+ }
}
}
}
@@ -955,6 +963,7 @@
padding: 0;
}
+#zen-essentials-container > .tabbrowser-tab {
#zen-essentials-container > .tabbrowser-tab {
--toolbarbutton-inner-padding: 0;
max-width: unset;
@@ -995,6 +1004,7 @@
}
@media (-moz-bool-pref: 'zen.theme.essentials-favicon-bg') {
+ &[visuallyselected] > .tab-stack > .tab-background {
&[visuallyselected] > .tab-stack > .tab-background {
&::after {
content: "";
diff --git a/src/browser/components/tabbrowser/content/tabbrowser-js.patch b/src/browser/components/tabbrowser/content/tabbrowser-js.patch
index 34bfd4b41..3323b47e9 100644
--- a/src/browser/components/tabbrowser/content/tabbrowser-js.patch
+++ b/src/browser/components/tabbrowser/content/tabbrowser-js.patch
@@ -2,6 +2,7 @@ diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/compo
index ff90a70bdad6c94ec4b90027ff102972d0eb28e5..c29a3620f85219074b2eeef8d75b4ca7232414e2 100644
--- a/browser/components/tabbrowser/content/tabbrowser.js
+++ b/browser/components/tabbrowser/content/tabbrowser.js
+@@ -406,11 +406,52 @@
@@ -406,11 +406,52 @@
return this.tabContainer.visibleTabs;
}
@@ -40,6 +41,7 @@ index ff90a70bdad6c94ec4b90027ff102972d0eb28e5..c29a3620f85219074b2eeef8d75b4ca7
+ }
+ if (!tab.hidden) {
+ i += !tab.hasAttribute("zen-glance-tab");
++ i += !tab.hasAttribute("zen-glance-tab");
+ }
+ }
+ return i;
@@ -53,10 +55,12 @@ index ff90a70bdad6c94ec4b90027ff102972d0eb28e5..c29a3620f85219074b2eeef8d75b4ca7
+ if (!tab.pinned && !tab.hasAttribute("zen-glance-tab")) {
break;
}
++ i += !tab.hasAttribute("zen-glance-tab");
+ i += !tab.hasAttribute("zen-glance-tab");
}
return i;
}
+@@ -807,7 +848,7 @@
@@ -807,7 +848,7 @@
this.showTab(aTab);
if (this.tabContainer.verticalMode) {
@@ -66,6 +70,7 @@ index ff90a70bdad6c94ec4b90027ff102972d0eb28e5..c29a3620f85219074b2eeef8d75b4ca7
);
} else {
this.moveTabTo(aTab, this.pinnedTabCount, { forceStandaloneTab: true });
+@@ -828,7 +869,7 @@
@@ -828,7 +869,7 @@
// the moving of a tab from the vertical pinned tabs container
// and back into arrowscrollbox.
@@ -75,6 +80,7 @@ index ff90a70bdad6c94ec4b90027ff102972d0eb28e5..c29a3620f85219074b2eeef8d75b4ca7
});
} else {
this.moveTabTo(aTab, this.pinnedTabCount - 1, {
+@@ -1055,6 +1096,8 @@
@@ -1055,6 +1096,8 @@
let LOCAL_PROTOCOLS = ["chrome:", "about:", "resource:", "data:"];
@@ -84,6 +90,7 @@ index ff90a70bdad6c94ec4b90027ff102972d0eb28e5..c29a3620f85219074b2eeef8d75b4ca7
if (
aIconURL &&
!aLoadingPrincipal &&
+@@ -1065,6 +1108,9 @@
@@ -1065,6 +1108,9 @@
);
return;
@@ -94,6 +101,7 @@ index ff90a70bdad6c94ec4b90027ff102972d0eb28e5..c29a3620f85219074b2eeef8d75b4ca7
let browser = this.getBrowserForTab(aTab);
browser.mIconURL = aIconURL;
+@@ -1310,6 +1356,7 @@
@@ -1310,6 +1356,7 @@
if (!this._previewMode) {
newTab.recordTimeFromUnloadToReload();
@@ -130,6 +138,7 @@ index ff90a70bdad6c94ec4b90027ff102972d0eb28e5..c29a3620f85219074b2eeef8d75b4ca7
aTab.linkedPanel = uniqueId;
// Inject the into the DOM if necessary.
+@@ -2446,8 +2496,8 @@
@@ -2446,8 +2496,8 @@
// If we transitioned from one browser to two browsers, we need to set
// hasSiblings=false on both the existing browser and the new browser.
@@ -141,6 +150,7 @@ index ff90a70bdad6c94ec4b90027ff102972d0eb28e5..c29a3620f85219074b2eeef8d75b4ca7
} else {
aTab.linkedBrowser.browsingContext.hasSiblings = this.tabs.length > 1;
}
+@@ -2679,6 +2729,12 @@
@@ -2679,6 +2729,12 @@
);
}
@@ -154,6 +164,7 @@ index ff90a70bdad6c94ec4b90027ff102972d0eb28e5..c29a3620f85219074b2eeef8d75b4ca7
if (!UserInteraction.running("browser.tabs.opening", window)) {
UserInteraction.start("browser.tabs.opening", "initting", window);
}
+@@ -2742,6 +2798,12 @@
@@ -2742,6 +2798,12 @@
noInitialLabel,
skipBackgroundNotify,
@@ -167,6 +178,7 @@ index ff90a70bdad6c94ec4b90027ff102972d0eb28e5..c29a3620f85219074b2eeef8d75b4ca7
if (insertTab) {
// insert the tab into the tab container in the correct position
this._insertTabAtIndex(t, {
+@@ -2885,6 +2947,9 @@
@@ -2885,6 +2947,9 @@
}
}