Skip to content
Closed
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
cd9ba2e
feat: Add config to support more than 12 essential tabs
blakegearin May 20, 2025
6a8c7f9
Merge branch 'dev' into configurable-essentials-max
blakegearin May 30, 2025
144d55e
Add dynamic getter
blakegearin May 30, 2025
4a38fd8
Add overflow handling
blakegearin May 30, 2025
6a280bd
Merge branch 'dev' into configurable-essentials-max
mr-cheffy May 31, 2025
3126321
Merge branch 'dev' into configurable-essentials-max
blakegearin Jun 10, 2025
8df6c41
Merge branch 'dev' into configurable-essentials-max
blakegearin Jun 16, 2025
3137447
Merge branch 'dev' into configurable-essentials-max
blakegearin Jun 23, 2025
98a2fc6
Fix merge conflict
blakegearin Jun 23, 2025
b99dc64
Merge branch 'dev' into configurable-essentials-max
MiraiDevv Jun 26, 2025
895f075
Merge branch 'dev' into configurable-essentials-max
MiraiDevv Jun 26, 2025
d1df727
Merge branch 'dev' into configurable-essentials-max
MiraiDevv Jun 26, 2025
d74c582
Merge branch 'dev' into configurable-essentials-max
MiraiDevv Jun 28, 2025
325b23d
fix: put an initial value in the space name
MiraiDevv Jun 28, 2025
36bacbf
fix: Enhance pinned tabs resizing logic and workspace creation, inclu…
MiraiDevv Jun 28, 2025
aaf357a
Merge branch 'configurable-essentials-max' of https://github.com/Mira…
MiraiDevv Jun 28, 2025
c0f5759
Merge branch 'dev' into configurable-essentials-max
MiraiDevv Jul 3, 2025
fd50118
refactor: Remove essentials height preference and related logic from …
MiraiDevv Jul 9, 2025
f5b71dd
Merge branch 'configurable-essentials-max' of https://github.com/Mira…
MiraiDevv Jul 9, 2025
f132ba1
refactor: Remove the onPinnedTabsResize method to speed up workspace …
MiraiDevv Jul 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/browser/app/profile/features.inc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
pref('zen.tabs.vertical', true);
pref('zen.tabs.vertical.right-side', false);
pref('zen.tabs.rename-tabs', true);
pref('zen.tabs.essentials.max', 12);
pref('zen.tabs.essentials.height', 0); // In vh units
pref('zen.tabs.show-newtab-vertical', true);

pref('zen.ctrlTab.show-pending-tabs', false);
Expand Down
21 changes: 21 additions & 0 deletions src/zen/common/ZenUIManager.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ var gZenUIManager = {
'zen.urlbar.show-domain-only-in-sidebar',
true
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
'essentialsHeight',
'zen.tabs.essentials.height',
0,
this._updateEssentialsHeight.bind(this)
);

document.addEventListener('mousedown', this.handleMouseDown.bind(this), true);

Expand Down Expand Up @@ -62,6 +69,7 @@ var gZenUIManager = {
gZenWorkspaces.promiseInitialized.finally(() => {
this._hasLoadedDOM = true;
this.updateTabsToolbar();
this._updateEssentialsHeight();
});

window.addEventListener('TabClose', this.onTabClose.bind(this));
Expand All @@ -70,6 +78,19 @@ var gZenUIManager = {
gZenVerticalTabsManager.init();
},

_updateEssentialsHeight() {
const essentialsWrapper = document.getElementById('zen-essentials');
if (!essentialsWrapper) return;

essentialsWrapper.style.setProperty('--zen-essentials-height', `${this.essentialsHeight}vh`);

if (this.essentialsHeight === 0) {
essentialsWrapper.setAttribute('data-essentials-height', '0');
} else {
essentialsWrapper.removeAttribute('data-essentials-height');
}
},

handleMouseDown(event) {
this._lastClickPosition = {
clientX: event.clientX,
Expand Down
25 changes: 23 additions & 2 deletions src/zen/tabs/ZenPinnedTabManager.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,18 @@
}

class nsZenPinnedTabManager extends ZenDOMOperatedFeature {
MAX_ESSENTIALS_TABS = 12;

async init() {
if (!this.enabled) {
return;
}

XPCOMUtils.defineLazyPreferenceGetter(
lazy,
'zenTabsEssentialsMax',
'zen.tabs.essentials.max',
12
);

this._canLog = Services.prefs.getBoolPref('zen.pinned-tab-manager.debug', false);
this.observer = new ZenPinnedTabsObserver();
this._initClosePinnedTabShortcut();
Expand All @@ -69,6 +75,21 @@
gZenWorkspaces._resolvePinnedInitialized();
}

get MAX_ESSENTIALS_TABS() {
return lazy.zenTabsEssentialsMax;
}

async onWorkspaceChange(newWorkspace, onInit) {
if (!this.enabled || PrivateBrowsingUtils.isWindowPrivate(window)) {
return;
}

if (onInit) {
await this._refreshPinnedTabs({ init: onInit });
this._hasFinishedLoading = true;
}
}

log(message) {
if (this._canLog) {
console.log(`[ZenPinnedTabManager] ${message}`);
Expand Down
57 changes: 56 additions & 1 deletion src/zen/tabs/zen-tabs/vertical-tabs.css
Original file line number Diff line number Diff line change
Expand Up @@ -1270,18 +1270,31 @@

#zen-essentials {
z-index: 1;
overflow: hidden;

&:not([data-essentials-height="0"]) {
max-height: var(--zen-essentials-height);
}
}

/* Container for essential items (often pinned/favorite tabs) */
.zen-essentials-container {
will-change: transform;

overflow: hidden;
padding-bottom: var(--zen-toolbox-padding);
overflow-y: auto;
overflow-x: hidden;
gap: 4px;
transition:
max-height 0.3s ease-out,
grid-template-columns 0.3s ease-out;
opacity: 1;

#zen-essentials:not([data-essentials-height="0"]) & {
height: 100%;
max-height: var(--zen-essentials-height);
}

grid-template-columns: repeat(auto-fit, minmax(max(23.7%, 48px), 1fr));
&[data-hack-type='1'] {
grid-template-columns: repeat(auto-fit, minmax(max(30%, 48px), auto));
Expand Down Expand Up @@ -1320,6 +1333,48 @@
&[cloned] {
pointer-events: none;
}

&[overflowing] {
--zen-scrollbar-overflow-background: light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.08));

&::before {
content: '';
position: sticky;
top: 0;
left: 0;
right: 0;
width: 100%;
height: 1px;
opacity: 0;
pointer-events: none;
transition: opacity 0.1s;
background-color: var(--zen-scrollbar-overflow-background);
grid-column: 1 / -1;
}

&::after {
content: '';
position: sticky;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why sticky possition?

left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 1px;
opacity: 0;
pointer-events: none;
transition: opacity 0.1s;
background-color: var(--zen-scrollbar-overflow-background);
grid-column: 1 / -1;
}
}

&[overflowing]:not([scrolledtostart])::before {
opacity: 1;
}

&[overflowing]:not([scrolledtoend])::after {
opacity: 1;
}
}

/* Style tabs within the Essentials container (and a related welcome sidebar) */
Expand Down
117 changes: 70 additions & 47 deletions src/zen/workspaces/ZenWorkspaces.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,19 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
}
}

onPinnedTabsResize() {
for (const container of document.querySelectorAll('.zen-essentials-container')) {
this._updateEssentialsOverflow(container);
}
for (let element of document.querySelectorAll('.zen-workspace-pinned-tabs-section')) {
if (element.scrollHeight > element.clientHeight) {
element.setAttribute('hide-separator', 'true');
} else {
element.removeAttribute('hide-separator');
}
}
}

_initializeEmptyTab() {
this._emptyTab = gBrowser.addTrustedTab('about:blank', {
inBackground: true,
Expand Down Expand Up @@ -445,6 +458,17 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
essentialsContainer.setAttribute('container', container);
document.getElementById('zen-essentials').appendChild(essentialsContainer);

this._updateEssentialsOverflow(essentialsContainer);

essentialsContainer.addEventListener('scroll', () => {
this._updateEssentialsOverflow(essentialsContainer);
});

const resizeObserver = new ResizeObserver(() => {
this._updateEssentialsOverflow(essentialsContainer);
});
resizeObserver.observe(essentialsContainer);

// Set an initial hidden state if the essentials section is not supposed
// to be shown on the current workspace
if (
Expand All @@ -457,6 +481,30 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
return essentialsContainer;
}

_updateEssentialsOverflow(container) {
const isOverflowing = container.scrollHeight > container.clientHeight;
const isAtStart = container.scrollTop === 0;
const isAtEnd = (container.scrollTop + container.clientHeight) >= (container.scrollHeight - 1);

if (isOverflowing) {
container.setAttribute('overflowing', 'true');
} else {
container.removeAttribute('overflowing');
}

if (isAtStart) {
container.setAttribute('scrolledtostart', 'true');
} else {
container.removeAttribute('scrolledtostart');
}

if (isAtEnd) {
container.setAttribute('scrolledtoend', 'true');
} else {
container.removeAttribute('scrolledtoend');
}
}

getCurrentEssentialsContainer() {
const currentWorkspace = this.getActiveWorkspaceFromCache();
return this.getEssentialsSection(currentWorkspace?.containerTabId);
Expand Down Expand Up @@ -2254,56 +2302,30 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
}

async createAndSaveWorkspace(
name = 'Space',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, why was a change done here

icon = undefined,
dontChange = false,
name,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't recognize the changes from here down

icon,
setActive = false,
containerTabId = 0,
{ beforeChangeCallback } = { beforeChangeCallback: null } // Callback to run before changing workspace
{ beforeChangeCallback = null, afterChangeCallback = null } = {}
) {
if (!this.workspaceEnabled) {
if (this.privateWindowOrDisabled) {
return;
}
if (this.isPrivateWindow) {
name = 'Private ' + name;
}
// get extra tabs remaning (e.g. on new profiles) and just move them to the new workspace
const extraTabs = Array.from(gBrowser.tabContainer.arrowScrollbox.children).filter(
(child) =>
child.tagName === 'tab' &&
!child.hasAttribute('zen-workspace-id') &&
!child.hasAttribute('zen-empty-tab') &&
!child.hasAttribute('zen-essential')
);
let workspaceData = await this._createWorkspaceData(
name,
icon,
extraTabs,
!dontChange,
containerTabId
);
if (this.isPrivateWindow) {
this._privateWorkspace = workspaceData;
} else {
await this.saveWorkspace(workspaceData, dontChange);
}
if (!dontChange) {
const newWorkspace = await ZenWorkspacesStorage.createWorkspace(name, icon, containerTabId);
await this._propagateWorkspaceData();
await this._createWorkspaceTabsSection(newWorkspace, []);
this.registerPinnedResizeObserver();
if (setActive) {
if (beforeChangeCallback) {
try {
await beforeChangeCallback(workspaceData);
} catch (e) {
console.error('Error in beforeChangeCallback:', e);
}
await beforeChangeCallback(newWorkspace);
}
this.registerPinnedResizeObserver();
let changed = extraTabs.length > 0;
if (changed) {
gBrowser.tabContainer._invalidateCachedTabs();
gBrowser.selectedTab = extraTabs[0];
await this.changeWorkspace(newWorkspace);
if (afterChangeCallback) {
await afterChangeCallback();
}
await this.changeWorkspace(workspaceData);
}
this.onWindowResize();
return workspaceData;
await this._updateWorkspacesChangeContextMenu();
return newWorkspace;
}

async updateTabsContainers(target = undefined, forAnimation = false) {
Expand Down Expand Up @@ -2474,11 +2496,12 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {

// Context menu management
async contextChangeContainerTab(event) {
this._organizingWorkspaceStrip = true;
let workspaces = await this._workspaces();
let workspace = workspaces.workspaces.find(
(workspace) => workspace.uuid === (this.#contextMenuData?.workspaceId || this.activeWorkspace)
);
let workspace;
if (this.#contextMenuData.workspaceId) {
workspace = this.getWorkspaceFromId(this.#contextMenuData.workspaceId);
} else {
workspace = this.getActiveWorkspaceFromCache();
}
let userContextId = parseInt(event.target.getAttribute('data-usercontextid'));
workspace.containerTabId = userContextId + 0; // +0 to convert to number
await this.saveWorkspace(workspace);
Expand Down