feat: Zen Folders, p=#9355, c=folders

* Start working on zen folders

* Rework zen-folder SessionStore

* Refactor restoreDataFromSessionStore

* fix linter

* Fix preserve folder order on restore

* Feat allow dragging tabs into zen-folder

* Fix ensure collapsed folders are hidden on session restore

* Feat store parentId nested folders

* feat: Implement tabs list popup

* refactor: Move tabs popup to `popups.inc`

* feat: Implement drag-and-drop folder into folder

* feat: Improved UI for search panel, b=no-bug, c=folders

* fix: Add extra margin when animating collapsed folders, b=no-bug, c=folders

* feat: Implement tab group rename and other UI changes, b=no-bug, c=folders, common

* feat: Add animated folder dots and adaptive search popup positioning

* fix: resolve conflicts

* fix: Correct active state indication for collapsed folders

* feat: Allow folders to be double clicked, b=no-bug, c=common, folders

* fix: incorrect tab order

* chore: Update prefs to the rust version, b=no-bug, c=folders

* fix: better handling of subfolders

* chore: Improve dynamic spacing when drag and dropping and fixed split views UI, b=no-bug, c=tabs, folders

* feat: Empty tab and improve drag and drop

* fix: add tab search event once

* fix: Empty tab should always be at first position

* feat: improve drag and drop interaction with folders

* feat: Improve drag-and-drop interaction for zen folders

* fix: Improve zen folder session restoration and visibility

* fix: Correct visible element indexing

* fix: Correct restore subfolder order

* feat: Use empty tabs and dont highlight current folder we currently are in, b=no-bug, c=tabs, folders

* feat: persist and restore split-view group state in subfolders

* fix: npm run pretty

* fix: dropIndicator and transform for split-view-group

* fix: Formatting

* fix: improve split group and folder drag-and-drop and persistence

* chore: Fix lint issues and merge with dev, b=no-bug, c=folders

* chore: Move folder element to a different location, b=no-bug, c=folders

* feat: Added a simple folders context menu and simplified patches, b=no-bug, c=tabs, folders

* fix: Correct active tab position in folders collapse animation

* feat: Add ungroup and delete folder actions

* fix: Fixed empty tabs not being able to be pinned, b=no-bug, c=workspaces

* feat: Added folder -> space conversion and pref checks, b=no-bug, c=folders, workspaces

* Update locales/en-US/browser/browser/zen-folders.ftl

Co-authored-by: Patrik Egyed <pregnor@gmail.com>
Signed-off-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>

* fix: Fixed folders not expanding when dragging another folder inside, b=no-bug, c=folders

* refactor: Refactor and improve tab group expansion logic

* feat: New folder dots

* test: Added simple folder creation test, b=no-bug, c=folders, tests

* fix: Don't expand folder when a tab inside it is selected

* feat: Added change folder to space menu item, b=no-bug, c=folders, workspaces

* feat: Added a menu item to create folders more easily, b=no-bug, c=workspaces, common, folders

* feat: Improved animations for collapsing active folders, b=no-bug, c=folders

* fix: Insert folder before pinned separator

* test: Improve folder and welcome testing, b=no-bug, c=folders, tests, welcome

* test: Fixed welcome tests, b=no-bug, c=folders, tests, welcome

* chore: lint, b=no-bug, c=tests, welcome

* feat: Add better selected UI, b=no-bug, c=folders

* feat: Emoji icons

* fix: Better handling of drag-and-drop folder highlighting

* fix: Single quotes

* fix: Hide emoji when folder has selected tab

* feat: Improved icons and animations, b=no-bug, c=folders, tabs

* fix: Fixed fetching the wrong prefs, b=no-bug, c=tabs, folders

* fix: Smoother dot animations

* fix: dragOverFolderThreshold condition and linter

* feat: visually collapse/expand active tab groups on drag/drop

* fix: Correctly transform folder with selected tab

* feat: Added better icons picker to support SVG, b=no-bug, c=common, folders, workspaces

* fix: Correctly transform tabs after moving them

* fix: Fixed not handling properly pinned tab count, b=no-bug, c=tabs, common, folders

* chore: Small formatting, b=no-bug, c=folders

* feat: Support SVG for folder icons

* fix: Formatting

* fix: Performance improvements for SVG icons

* fix: Shift up the folder icon

* fix: Handle null/undefined user icon values defensively

* feat: Improved icon sizes and fixed bug when collapsing folders with collapsed folders, b=no-bug, c=common, folders, workspaces

* chore: Tweaked the transform values for icons, b=no-bug, c=folders

* feat: Added support for collapsed mode (experimental), b=no-bug, c=folders

* fix: ungroup split view

* fix: Improve handling of special tabs during folder creation and drag-and-drop

* fix: Formatting

* feat: Imrpoved hardware accelaration for the icons and folder height calculation, b=no-bug, c=tabs, folders

* refactor: Extract dragover logic for tab group labels

* fix: Small fixes to the folders UI, b=no-bug, c=tabs, folders

* feat: Improved icons opacity and dialog, b=no-bug, c=common, workspaces

* test: Added subfolders basic test, b=no-bug, c=folders, tests

* fix: Drop indicator for folder targets

* feat: Improved drag and drop handling from normal to pinned tabs, b=no-bug, c=folders, tabs

* fix: Fixed moving split views into pinned tabs container, b=no-bug, c=folders, tabs

* feat: Improved new drag and drop offset, b=no-bug, c=tabs

* feat: Refine folder drop behavior with new thresholds

* fix: tabs.js extra space in patch

* fix: Properly handle has-active state

* fix: Add optional chaining for activeGroups length check

* fix: Fixed moving tabs to the workspace indicator not showing any feedback, b=no-bug, c=tabs

* feat: Change svg stroke width, b=no-bug, c=folders, tabs

* feat: Remove aspect ratio for the folder icon, b=no-bug, c=folders, tabs

* feat: Don't reset transform when pining tabs, b=no-bug, c=tabs

* feat: Ungroup tabs when dragging and make sure to animate tabs after the selected one, b=no-bug, c=tabs, folders

* fix: Transform folder with active tab

* fix: Fixed expand animation not working for the first time, b=no-bug, c=folders

* feat: Add expand to selected functionality for folders

* fix: Formatting

* Update src/zen/folders/ZenFolder.mjs

Signed-off-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>

* Update src/zen/workspaces/ZenWorkspaces.mjs

Signed-off-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>

* fix: Set icon for new workspace

* fix: Formatting

* fix: Hide the icon if empty

* fix: Optimize tab drag-over transitions

* feat: Lower the drag and drop threshold, b=no-bug, c=tabs, folders

* feat: Hide search panel when theres no visible tabs, b=no-bug, c=tabs, folders

* fix: Adapt tab-group to new changes

* fix: Fixed expanding split views as folders, b=no-bug, c=folders

* feat(tabs): Implement tab grouping persistence for pinned tabs

This commit introduces the ability to group pinned tabs for better organization.

Changes include:

- Added `createGroup` to create new tab groups.
- Added `addTabToGroup` to add existing tabs to a group.
- Added `removeTabFromGroup` to remove a tab from a group (moving it to the root level).
- Added `moveTabBetweenGroups` to move tabs between different groups or to the root level.
- Added `getAllGroups` to retrieve all tab groups, optionally filtered by workspace.
- Added `getGroupInfo` to retrieve information about a specific group, including its child count.
- Added `reorderTabsInGroup` to reorder tabs within a specific group.

These functions provide a comprehensive API for managing tab groups within the Zen Browser.  Error handling and input validation are included for robustness.  Database transactions are used to ensure data consistency. Observers are notified of changes to notify the sync engine.

* feat: Improve stroke colors for light mode, b=no-bug, c=folders

* perf: cache and optimize animation updates

* fix: Expand folder after drop

* fix: Update active state on tab change

* feat: Sync groups to new windows, b=no-bug, c=folders, tabs, workspaces

* feat: Finish window syncing for new folders, b=no-bug, c=tabs, folders, workspaces

* feat: Make sure SVG icons use the context fill instead of the current color, b=no-bug, c=common, folders

* feat: Added support for drag and drop in collapsed mode, b=no-bug, c=tabs, folders

* fix: Clean up tab attributes and styles on workspace transfer

* fix: Fixed svg icons being always dark, b=no-bug, c=workspaces

---------

Signed-off-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>
Co-authored-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>
Co-authored-by: Mr. M <mr.m@tuta.com>
Co-authored-by: Patrik Egyed <pregnor@gmail.com>
Co-authored-by: Kristijan Ribarić <kriba13@gmail.com>
This commit is contained in:
octaviusz
2025-08-06 18:59:08 +03:00
committed by GitHub
parent 7cf96dde23
commit 591dce5921
94 changed files with 3534 additions and 531 deletions

View File

@@ -1,3 +1,23 @@
zen-folders-search-placeholder =
.placeholder = Search { $folder-name }...
zen-folders-panel-rename-folder =
.label = Rename Folder
zen-folders-panel-expand-folder =
.label = Expand Folder
zen-folders-panel-delete-folder =
.label = Delete Folder
zen-folders-panel-convert-folder-to-space =
.label = Convert folder to Space
zen-folders-panel-change-folder-space =
.label = Change Space...
zen-folders-panel-change-icon-folder =
.label = Change Icon
zen-folders-search-no-results = No tabs matching that search 🤔

View File

@@ -57,3 +57,8 @@ zen-close-label = Close
zen-singletoolbar-urlbar-placeholder-with-name =
.placeholder = Search...
zen-icons-picker-emoji =
.label = Emojis
zen-icons-picker-svg =
.label = Icons

View File

@@ -4,6 +4,9 @@ zen-panel-ui-workspaces-text = Spaces
zen-panel-ui-workspaces-create =
.label = Create Space
zen-panel-ui-folder-create =
.label = Create Folder
zen-workspaces-panel-context-delete =
.label = Delete Space
.accesskey = D

View File

@@ -14,9 +14,6 @@
- name: browser.tabs.hoverPreview.enabled
value: false
- name: browser.tabs.dragdrop.moveOverThresholdPercent
value: 50
- name: browser.tabs.unloadTabInContextMenu
value: true

9
prefs/folders.yaml Normal file
View File

@@ -0,0 +1,9 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
- name: zen.folders.search.enabled
value: true
- name: zen.folders.search.hover-delay
value: 1000 # ms

View File

@@ -42,3 +42,12 @@
- name: zen.view.window.scheme
value: 2
- name: zen.view.drag-and-drop.move-over-threshold
value: 70
- name: zen.view.drag-and-drop.drop-inside-upper-threshold
value: 80
- name: zen.view.drag-and-drop.drop-inside-lower-threshold
value: 30

View File

@@ -44,6 +44,7 @@
# Scripts used all over the browser
<script type="text/javascript" src="chrome://browser/content/ZenUIManager.mjs"></script>
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenFolder.mjs"></script>
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenFolders.mjs"></script>
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenMods.mjs"></script>
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenCompactMode.mjs"></script>

View File

@@ -61,6 +61,7 @@
content/browser/zen-components/ZenGlanceManager.mjs (../../zen/glance/ZenGlanceManager.mjs)
content/browser/zen-styles/zen-glance.css (../../zen/glance/zen-glance.css)
content/browser/zen-components/ZenFolder.mjs (../../zen/folders/ZenFolder.mjs)
content/browser/zen-components/ZenFolders.mjs (../../zen/folders/ZenFolders.mjs)
content/browser/zen-styles/zen-folders.css (../../zen/folders/zen-folders.css)

View File

@@ -57,4 +57,6 @@
<command id="cmd_zenGlanceClose" />
<command id="cmd_zenGlanceExpand" />
<command id="cmd_zenGlanceSplit" />
<command id="cmd_zenOpenFolderCreation" />
</commandset>

View File

@@ -6,3 +6,4 @@
<link rel="localization" href="browser/zen-split-view.ftl"/>
<link rel="localization" href="browser/zen-general.ftl"/>
<link rel="localization" href="browser/zen-vertical-tabs.ftl"/>
<link rel="localization" href="browser/zen-folders.ftl"/>

View File

@@ -3,9 +3,21 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
<panel type="arrow" orient="vertical" id="PanelUI-zen-emojis-picker" position="bottomright topright" side="bottom">
<hbox id="PanelUI-zen-emojis-picker-header">
<html:input type="search" id="PanelUI-zen-emojis-picker-search" placeholder="Search emojis" />
<hbox id="PanelUI-zen-picker-header">
<toolbarbutton class="toolbarbutton-1" />
<hbox id="PanelUI-zen-emojis-buttons-wrapper">
<toolbarbutton id="PanelUI-zen-emojis-picker-change-emojis" class="selected" data-l10n-id="zen-icons-picker-emoji" />
<toolbarbutton id="PanelUI-zen-emojis-picker-change-svg" data-l10n-id="zen-icons-picker-svg" />
</hbox>
<toolbarbutton id="PanelUI-zen-emojis-picker-none" class="toolbarbutton-1" />
</hbox>
<hbox id="PanelUI-zen-emojis-picker-list" />
<hbox id="PanelUI-zen-emojis-picker-pages">
<vbox emojis="true">
<hbox id="PanelUI-zen-emojis-picker-header">
<html:input type="search" id="PanelUI-zen-emojis-picker-search" placeholder="Search emojis" />
</hbox>
<hbox id="PanelUI-zen-emojis-picker-list" />
</vbox>
<hbox svgs="true" id="PanelUI-zen-emojis-picker-svgs" />
</hbox>
</panel>

View File

@@ -0,0 +1,14 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
<panel id="zen-folder-tabs-popup" type="arrow" orient="vertical">
<hbox class="tabs-list-header">
<image class="zen-folder-tabs-list-search-icon" src="chrome://global/skin/icons/search-glass.svg"/>
<html:input id="zen-folder-tabs-list-search" data-l10n-id="zen-folders-search-placeholder" type="search"/>
</hbox>
<scrollbox class="zen-folder-tabs-list-scrollbox" flex="1">
<vbox id="zen-folder-tabs-list"></vbox>
<hbox id="zen-folder-tabs-search-no-results" hidden="true" flex="1" data-l10n-id="zen-folders-search-no-results" />
</scrollbox>
</panel>

View File

@@ -5,6 +5,7 @@
<menupopup id="zenCreateNewPopup">
<menuitem data-l10n-id="tabs-toolbar-new-tab" command="cmd_newNavigatorTab" image="chrome://browser/skin/zen-icons/plus.svg" />
<menuseparator/>
<menuitem data-l10n-id="zen-panel-ui-folder-create" command="cmd_zenOpenFolderCreation" image="chrome://browser/skin/zen-icons/folder.svg" />
<menuitem data-l10n-id="zen-panel-ui-workspaces-create" command="cmd_zenOpenWorkspaceCreation" image="chrome://browser/skin/zen-icons/duplicate-tab.svg" />
</menupopup>
@@ -30,3 +31,18 @@
<menuitem data-l10n-id="zen-panel-ui-workspaces-create" command="cmd_zenOpenWorkspaceCreation"/>
<menuitem id="context_zenDeleteWorkspace" data-l10n-id="zen-workspaces-panel-context-delete" command="cmd_zenCtxDeleteWorkspace"/>
</menupopup>
<menupopup id="zenFolderActions">
<menuitem id="context_zenFolderRename" data-l10n-id="zen-folders-panel-rename-folder"/>
<menuitem id="context_zenFolderChangeIcon" data-l10n-id="zen-folders-panel-change-icon-folder"/>
<menuitem id="context_zenFolderExpand" data-l10n-id="zen-folders-panel-expand-folder"/>
<menuseparator />
<menu id="context_zenChangeFolderSpace"
data-l10n-id="zen-folders-panel-change-folder-space">
<menupopup />
</menu>
<menuitem id="context_zenFolderToSpace" data-l10n-id="zen-folders-panel-convert-folder-to-space" />
<menuseparator />
<menuitem id="context_zenFolderDelete" data-l10n-id="zen-folders-panel-delete-folder"/>
</menupopup>

View File

@@ -4,5 +4,6 @@
#include zen-panels/gradient-generator.inc
#include zen-panels/emojis-picker.inc
#include zen-panels/folders-search.inc
#include zen-panels/popups.inc

View File

@@ -1,8 +1,8 @@
diff --git a/browser/components/extensions/parent/ext-browser.js b/browser/components/extensions/parent/ext-browser.js
index 179816fa96ccf26604d52f71232296398dd9bdbd..1d77da215d89acf0697b70cf6272e700c455c088 100644
index 1e382981a33ca341c306a78ed81718e4ad7c2b3e..e98f76f1e9dd381116328d6d9b901585ea80dce4 100644
--- a/browser/components/extensions/parent/ext-browser.js
+++ b/browser/components/extensions/parent/ext-browser.js
@@ -308,6 +308,7 @@ class TabTracker extends TabTrackerBase {
@@ -351,6 +351,7 @@ class TabTracker extends TabTrackerBase {
}
getId(nativeTab) {
@@ -10,7 +10,7 @@ index 179816fa96ccf26604d52f71232296398dd9bdbd..1d77da215d89acf0697b70cf6272e700
let id = this._tabs.get(nativeTab);
if (id) {
return id;
@@ -342,6 +343,7 @@ class TabTracker extends TabTrackerBase {
@@ -385,6 +386,7 @@ class TabTracker extends TabTrackerBase {
if (nativeTab.ownerGlobal.closed) {
throw new Error("Cannot attach ID to a tab in a closed window.");
}
@@ -18,7 +18,7 @@ index 179816fa96ccf26604d52f71232296398dd9bdbd..1d77da215d89acf0697b70cf6272e700
this._tabs.set(nativeTab, id);
if (nativeTab.linkedBrowser) {
@@ -1218,6 +1220,10 @@ class TabManager extends TabManagerBase {
@@ -1268,6 +1270,10 @@ class TabManager extends TabManagerBase {
}
canAccessTab(nativeTab) {

View File

@@ -1114,11 +1114,6 @@ Preferences.addAll([
type: 'bool',
default: true,
},
{
id: 'zen.tabs.show-newtab-under',
type: 'bool',
default: false,
},
{
id: 'zen.glance.activation-method',
type: 'string',

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/sessionstore/SessionStore.sys.mjs b/browser/components/sessionstore/SessionStore.sys.mjs
index be029379c101a0105d4837136e064e6007b67c3e..93cf9042822f1eed1e53a28bc9f052597bd305bb 100644
index be029379c101a0105d4837136e064e6007b67c3e..cfa861920b15c2f535f1d9351425e29ee03bbdda 100644
--- a/browser/components/sessionstore/SessionStore.sys.mjs
+++ b/browser/components/sessionstore/SessionStore.sys.mjs
@@ -2120,7 +2120,6 @@ var SessionStoreInternal = {
@@ -83,7 +83,15 @@ index be029379c101a0105d4837136e064e6007b67c3e..93cf9042822f1eed1e53a28bc9f05259
// update the internal state data for this window
for (let tab of tabs) {
if (tab == aWindow.FirefoxViewHandler.tab) {
@@ -5581,7 +5585,7 @@ var SessionStoreInternal = {
@@ -5569,6 +5573,7 @@ var SessionStoreInternal = {
tabsData.push(tabData);
}
+ winData.folders = aWindow.gZenFolders?.storeDataForSessionStore() || [];
// update tab group state for this window
winData.groups = [];
for (let tabGroup of aWindow.gBrowser.tabGroups) {
@@ -5581,7 +5586,7 @@ var SessionStoreInternal = {
// a window is closed, point to the first item in the tab strip instead (it will never be the Firefox View tab,
// since it's only inserted into the tab strip after it's selected).
if (aWindow.FirefoxViewHandler.tab?.selected) {
@@ -92,7 +100,7 @@ index be029379c101a0105d4837136e064e6007b67c3e..93cf9042822f1eed1e53a28bc9f05259
winData.title = tabbrowser.tabs[0].label;
}
winData.selected = selectedIndex;
@@ -5693,8 +5697,8 @@ var SessionStoreInternal = {
@@ -5693,8 +5698,8 @@ var SessionStoreInternal = {
// selectTab represents.
let selectTab = 0;
if (overwriteTabs) {
@@ -103,15 +111,16 @@ index be029379c101a0105d4837136e064e6007b67c3e..93cf9042822f1eed1e53a28bc9f05259
selectTab = Math.min(selectTab, winData.tabs.length);
}
@@ -5737,6 +5741,7 @@ var SessionStoreInternal = {
@@ -5737,6 +5742,8 @@ var SessionStoreInternal = {
winData.tabs,
winData.groups ?? []
);
+ aWindow.gZenFolders?.restoreDataFromSessionStore(winData.folders);
+ aWindow.gZenViewSplitter?.restoreDataFromSessionStore(winData.splitViewData);
this._log.debug(
`restoreWindow, createTabsForSessionRestore returned ${tabs.length} tabs`
);
@@ -6286,6 +6291,22 @@ var SessionStoreInternal = {
@@ -6286,6 +6293,22 @@ var SessionStoreInternal = {
// Most of tabData has been restored, now continue with restoring
// attributes that may trigger external events.

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/tabbrowser/content/tab.js b/browser/components/tabbrowser/content/tab.js
index e5509953509c4da8756e36a0792f76814e24ba0c..5ce4207e5e580053aa9a857168fd20a2972905b5 100644
index e5509953509c4da8756e36a0792f76814e24ba0c..32e21e1295aa508a2b8dc6b8a0ea4d678dd81594 100644
--- a/browser/components/tabbrowser/content/tab.js
+++ b/browser/components/tabbrowser/content/tab.js
@@ -21,6 +21,7 @@
@@ -42,16 +42,28 @@ index e5509953509c4da8756e36a0792f76814e24ba0c..5ce4207e5e580053aa9a857168fd20a2
return;
}
@@ -224,7 +227,7 @@
@@ -224,7 +227,19 @@
}
get visible() {
- return this.isOpen && !this.hidden && !this.group?.collapsed;
+ return this.isOpen && !this.hidden && !this.group?.collapsed && !this.hasAttribute("zen-empty-tab");
+ if (!this.isOpen || this.hidden || this.hasAttribute("zen-empty-tab")) {
+ return false;
+ }
+
+ // Recursively check all parent groups
+ let currentParent = this.group;
+ while (currentParent && !this.hasAttribute("folder-active")) {
+ if (currentParent.collapsed) {
+ return false;
+ }
+ currentParent = currentParent.group;
+ }
+ return true;
}
get hidden() {
@@ -295,7 +298,7 @@
@@ -295,7 +310,7 @@
return false;
}
@@ -60,7 +72,21 @@ index e5509953509c4da8756e36a0792f76814e24ba0c..5ce4207e5e580053aa9a857168fd20a2
}
get lastAccessed() {
@@ -467,6 +470,8 @@
@@ -372,8 +387,11 @@
}
get group() {
- if (this.parentElement?.tagName == "tab-group") {
- return this.parentElement;
+ if (typeof gBrowser === "undefined") {
+ return null;
+ }
+ if (gBrowser.isTabGroup(this.parentElement?.parentElement)) {
+ return this.parentElement.parentElement;
}
return null;
}
@@ -467,6 +485,8 @@
this.style.MozUserFocus = "ignore";
} else if (
event.target.classList.contains("tab-close-button") ||
@@ -69,7 +95,7 @@ index e5509953509c4da8756e36a0792f76814e24ba0c..5ce4207e5e580053aa9a857168fd20a2
event.target.classList.contains("tab-icon-overlay") ||
event.target.classList.contains("tab-audio-button")
) {
@@ -521,6 +526,10 @@
@@ -521,6 +541,10 @@
this.style.MozUserFocus = "";
}
@@ -80,7 +106,7 @@ index e5509953509c4da8756e36a0792f76814e24ba0c..5ce4207e5e580053aa9a857168fd20a2
on_click(event) {
if (event.button != 0) {
return;
@@ -569,6 +578,7 @@
@@ -569,6 +593,7 @@
)
);
} else {
@@ -88,7 +114,7 @@ index e5509953509c4da8756e36a0792f76814e24ba0c..5ce4207e5e580053aa9a857168fd20a2
gBrowser.removeTab(this, {
animate: true,
triggeringEvent: event,
@@ -581,6 +591,14 @@
@@ -581,6 +606,14 @@
// (see tabbrowser-tabs 'click' handler).
gBrowser.tabContainer._blockDblClick = true;
}
@@ -103,7 +129,7 @@ index e5509953509c4da8756e36a0792f76814e24ba0c..5ce4207e5e580053aa9a857168fd20a2
}
on_dblclick(event) {
@@ -604,6 +622,8 @@
@@ -604,6 +637,8 @@
animate: true,
triggeringEvent: event,
});

View File

@@ -1,28 +1,39 @@
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022dd09884ba 100644
index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..3918e7c17dd686bd99e1fdb49750db7946304382 100644
--- a/browser/components/tabbrowser/content/tabbrowser.js
+++ b/browser/components/tabbrowser/content/tabbrowser.js
@@ -422,15 +422,49 @@
@@ -422,15 +422,60 @@
return this.tabContainer.visibleTabs;
}
+ get _numVisiblePinTabsWithoutCollapsed() {
+ let i = 0;
+ for (let item of this.tabContainer.ariaFocusableItems) {
+ if (this.isTabGroupLabel(item) && item.closest("tab-group")?.pinned) {
+ if (this.isTabGroupLabel(item) && item.group?.pinned) {
+ i += 1;
+ continue;
+ }
+ if (!item.pinned && !item.hasAttribute("zen-glance-tab")) {
+ break;
+ }
+ if ((!item.group?.hasAttribute("split-view-group") && !item.group?.collapsed) && !item.hidden) {
+ if (item.visible) {
+ i += !item.hasAttribute("zen-glance-tab");
+ }
+ }
+ return i;
+ }
+
+ ungroupTabsUntilNoActive(tab) {
+ if (!tab || !tab.group) return;
+ const activeGroups = tab.group.activeGroups;
+ if (activeGroups?.length) {
+ const lastActiveGroup = activeGroups[activeGroups.length - 1];
+ this.#handleTabMove(tab, () => {
+ lastActiveGroup.after(tab);
+ });
+ }
+ }
+
+ get _numZenEssentials() {
+ let i = 0;
+ for (let tab of this.tabs) {
@@ -54,7 +65,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
set selectedTab(val) {
if (
gSharedTabWarning.willShowSharedTabWarning(val) ||
@@ -578,6 +612,7 @@
@@ -578,6 +623,7 @@
this.tabpanels.appendChild(panel);
let tab = this.tabs[0];
@@ -62,13 +73,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
tab.linkedPanel = uniqueId;
this._selectedTab = tab;
this._selectedBrowser = browser;
@@ -858,14 +893,18 @@
aTab,
{ telemetrySource = this.TabMetrics.METRIC_SOURCE.UNKNOWN } = {}
) {
- if (aTab.pinned || aTab == FirefoxViewHandler.tab) {
+ if (aTab.pinned || aTab == FirefoxViewHandler.tab || aTab.hasAttribute("zen-empty-tab")) {
return;
@@ -863,9 +909,13 @@
}
this.showTab(aTab);
@@ -83,7 +88,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
aTab.setAttribute("pinned", "true");
this._updateTabBarForPinnedTabs();
@@ -878,11 +917,15 @@
@@ -878,11 +928,15 @@
}
this.#handleTabMove(aTab, () => {
@@ -100,7 +105,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
});
aTab.style.marginInlineStart = "";
@@ -1060,6 +1103,8 @@
@@ -1060,6 +1114,8 @@
let LOCAL_PROTOCOLS = ["chrome:", "about:", "resource:", "data:"];
@@ -109,7 +114,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
if (
aIconURL &&
!aLoadingPrincipal &&
@@ -1070,6 +1115,9 @@
@@ -1070,6 +1126,9 @@
);
return;
}
@@ -119,7 +124,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
let browser = this.getBrowserForTab(aTab);
browser.mIconURL = aIconURL;
@@ -1319,6 +1367,7 @@
@@ -1319,6 +1378,7 @@
if (!this._previewMode) {
newTab.recordTimeFromUnloadToReload();
newTab.updateLastAccessed();
@@ -127,7 +132,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
oldTab.updateLastAccessed();
// if this is the foreground window, update the last-seen timestamps.
if (this.ownerGlobal == BrowserWindowTracker.getTopWindow()) {
@@ -1471,6 +1520,9 @@
@@ -1471,6 +1531,9 @@
}
let activeEl = document.activeElement;
@@ -137,7 +142,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
// If focus is on the old tab, move it to the new tab.
if (activeEl == oldTab) {
newTab.focus();
@@ -1794,7 +1846,8 @@
@@ -1794,7 +1857,8 @@
}
_setTabLabel(aTab, aLabel, { beforeTabOpen, isContentTitle, isURL } = {}) {
@@ -147,7 +152,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
return false;
}
@@ -1902,7 +1955,7 @@
@@ -1902,7 +1966,7 @@
newIndex = this.selectedTab._tPos + 1;
}
@@ -156,7 +161,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
if (this.isTabGroupLabel(targetTab)) {
throw new Error(
"Replacing a tab group label with a tab is not supported"
@@ -2166,6 +2219,7 @@
@@ -2166,6 +2230,7 @@
uriIsAboutBlank,
userContextId,
skipLoad,
@@ -164,7 +169,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
} = {}) {
let b = document.createXULElement("browser");
// Use the JSM global to create the permanentKey, so that if the
@@ -2239,8 +2293,7 @@
@@ -2239,8 +2304,7 @@
// we use a different attribute name for this?
b.setAttribute("name", name);
}
@@ -174,7 +179,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
b.setAttribute("transparent", "true");
}
@@ -2405,7 +2458,7 @@
@@ -2405,7 +2469,7 @@
let panel = this.getPanel(browser);
let uniqueId = this._generateUniquePanelID();
@@ -183,7 +188,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
aTab.linkedPanel = uniqueId;
// Inject the <browser> into the DOM if necessary.
@@ -2464,8 +2517,8 @@
@@ -2464,8 +2528,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.
if (this.tabs.length == 2) {
@@ -194,7 +199,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
} else {
aTab.linkedBrowser.browsingContext.hasSiblings = this.tabs.length > 1;
}
@@ -2709,6 +2762,8 @@
@@ -2709,6 +2773,8 @@
schemelessInput,
hasValidUserGestureActivation = false,
textDirectiveUserActivation = false,
@@ -203,7 +208,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
} = {}
) {
// all callers of addTab that pass a params object need to pass
@@ -2719,6 +2774,12 @@
@@ -2719,6 +2785,12 @@
);
}
@@ -216,7 +221,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
if (!UserInteraction.running("browser.tabs.opening", window)) {
UserInteraction.start("browser.tabs.opening", "initting", window);
}
@@ -2782,6 +2843,19 @@
@@ -2782,6 +2854,19 @@
noInitialLabel,
skipBackgroundNotify,
});
@@ -236,15 +241,18 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
if (insertTab) {
// Insert the tab into the tab container in the correct position.
this.#insertTabAtIndex(t, {
@@ -2790,6 +2864,7 @@
@@ -2790,8 +2875,9 @@
ownerTab,
openerTab,
pinned,
+ essential,
bulkOrderedOpen,
tabGroup: tabGroup ?? openerTab?.group,
- tabGroup: tabGroup ?? openerTab?.group,
+ tabGroup: tabGroup,
});
@@ -2808,6 +2883,7 @@
}
@@ -2808,6 +2894,7 @@
openWindowInfo,
skipLoad,
triggeringRemoteType,
@@ -252,12 +260,12 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
}));
if (focusUrlBar) {
@@ -2928,6 +3004,12 @@
@@ -2928,6 +3015,12 @@
}
}
+ if (typeof window.gZenVerticalTabsManager !== "undefined") {
+ gZenVerticalTabsManager.animateTab(t);
+ gZenVerticalTabsManager.animateItemOpen(t);
+ }
+ if (typeof window.gZenCompactModeManager !== "undefined" && !skipLoad && insertTab) {
+ gZenCompactModeManager._onTabOpen(t, inBackground);
@@ -265,7 +273,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
// Additionally send pinned tab events
if (pinned) {
this.#notifyPinnedStatus(t);
@@ -3016,10 +3098,10 @@
@@ -3016,10 +3109,10 @@
isAdoptingGroup = false,
isUserTriggered = false,
telemetryUserCreateSource = "unknown",
@@ -277,7 +285,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
}
if (!color) {
@@ -3040,7 +3122,12 @@
@@ -3040,7 +3133,12 @@
label,
isAdoptingGroup
);
@@ -291,7 +299,16 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
group,
insertBefore?.group ?? insertBefore
);
@@ -3357,6 +3444,7 @@
@@ -3163,7 +3261,7 @@
}
this.#handleTabMove(tab, () =>
- gBrowser.tabContainer.insertBefore(tab, tab.group.nextElementSibling)
+ tab.group.after(tab)
);
}
@@ -3357,6 +3455,7 @@
openWindowInfo,
skipLoad,
triggeringRemoteType,
@@ -299,7 +316,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
}
) {
// If we don't have a preferred remote type (or it is `NOT_REMOTE`), and
@@ -3426,6 +3514,7 @@
@@ -3426,6 +3525,7 @@
openWindowInfo,
name,
skipLoad,
@@ -307,7 +324,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
});
}
@@ -3613,7 +3702,7 @@
@@ -3613,7 +3713,7 @@
// Add a new tab if needed.
if (!tab) {
let createLazyBrowser =
@@ -316,7 +333,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
let url = "about:blank";
if (tabData.entries?.length) {
@@ -3650,8 +3739,10 @@
@@ -3650,8 +3750,10 @@
insertTab: false,
skipLoad: true,
preferredRemoteType,
@@ -328,7 +345,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
if (select) {
tabToSelect = tab;
}
@@ -3663,7 +3754,8 @@
@@ -3663,7 +3765,8 @@
this.pinTab(tab);
// Then ensure all the tab open/pinning information is sent.
this._fireTabOpen(tab, {});
@@ -338,7 +355,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
let { groupId } = tabData;
const tabGroup = tabGroupWorkingData.get(groupId);
// if a tab refers to a tab group we don't know, skip any group
@@ -3677,7 +3769,10 @@
@@ -3677,7 +3780,10 @@
tabGroup.stateData.id,
tabGroup.stateData.color,
tabGroup.stateData.collapsed,
@@ -350,7 +367,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
);
tabsFragment.appendChild(tabGroup.node);
}
@@ -3722,9 +3817,23 @@
@@ -3722,9 +3828,23 @@
// to remove the old selected tab.
if (tabToSelect) {
let leftoverTab = this.selectedTab;
@@ -366,15 +383,15 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
+ gZenWorkspaces._initialTab._shouldRemove = true;
+ }
+ }
+ }
}
+ else {
+ gZenWorkspaces._tabToRemoveForEmpty = this.selectedTab;
}
+ }
+ this._hasAlreadyInitializedZenSessionStore = true;
if (tabs.length > 1 || !tabs[0].selected) {
this._updateTabsAfterInsert();
@@ -3919,7 +4028,7 @@
@@ -3919,7 +4039,7 @@
// Ensure we have an index if one was not provided.
if (typeof elementIndex != "number" && typeof tabIndex != "number") {
// Move the new tab after another tab if needed, to the end otherwise.
@@ -383,7 +400,16 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
if (
!bulkOrderedOpen &&
((openerTab &&
@@ -3942,7 +4051,7 @@
@@ -3931,7 +4051,7 @@
let lastRelatedTab =
openerTab && this._lastRelatedTabMap.get(openerTab);
let previousTab = lastRelatedTab || openerTab || this.selectedTab;
- if (!tabGroup) {
+ if (!tabGroup && pinned === previousTab.group?.pinned) {
tabGroup = previousTab.group;
}
if (
@@ -3942,7 +4062,7 @@
) {
elementIndex = Infinity;
} else if (previousTab.visible) {
@@ -392,7 +418,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
} else if (previousTab == FirefoxViewHandler.tab) {
elementIndex = 0;
}
@@ -3970,14 +4079,14 @@
@@ -3970,14 +4090,14 @@
}
// Ensure index is within bounds.
if (tab.pinned) {
@@ -411,7 +437,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
// Prevent a flash of unstyled content by setting up the tab content
// and inherited attributes before appending it (see Bug 1592054):
@@ -3985,7 +4094,7 @@
@@ -3985,7 +4105,7 @@
this.tabContainer._invalidateCachedTabs();
@@ -420,7 +446,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
if (this.isTab(itemAfter) && itemAfter.group == tabGroup) {
// Place at the front of, or between tabs in, the same tab group
this.tabContainer.insertBefore(tab, itemAfter);
@@ -4018,6 +4127,7 @@
@@ -4018,6 +4138,7 @@
if (pinned) {
this._updateTabBarForPinnedTabs();
}
@@ -428,7 +454,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
TabBarVisibility.update();
}
@@ -4307,6 +4417,9 @@
@@ -4307,6 +4428,9 @@
return;
}
@@ -438,7 +464,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
this.removeTabs(selectedTabs, { isUserTriggered, telemetrySource });
}
@@ -4568,6 +4681,7 @@
@@ -4568,6 +4692,7 @@
telemetrySource,
} = {}
) {
@@ -446,7 +472,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
// When 'closeWindowWithLastTab' pref is enabled, closing all tabs
// can be considered equivalent to closing the window.
if (
@@ -4657,6 +4771,7 @@
@@ -4657,6 +4782,7 @@
if (lastToClose) {
this.removeTab(lastToClose, aParams);
}
@@ -454,7 +480,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
} catch (e) {
console.error(e);
}
@@ -4695,6 +4810,12 @@
@@ -4695,6 +4821,12 @@
aTab._closeTimeNoAnimTimerId = Glean.browserTabclose.timeNoAnim.start();
}
@@ -467,7 +493,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
// Handle requests for synchronously removing an already
// asynchronously closing tab.
if (!animate && aTab.closing) {
@@ -4709,6 +4830,9 @@
@@ -4709,6 +4841,9 @@
// state).
let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width;
let isLastTab = this.#isLastTabInWindow(aTab);
@@ -477,7 +503,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
if (
!this._beginRemoveTab(aTab, {
closeWindowFastpath: true,
@@ -4891,7 +5015,7 @@
@@ -4891,7 +5026,7 @@
closeWindowWithLastTab != null
? closeWindowWithLastTab
: !window.toolbar.visible ||
@@ -486,7 +512,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
if (closeWindow) {
// We've already called beforeunload on all the relevant tabs if we get here,
@@ -4915,6 +5039,7 @@
@@ -4915,6 +5050,7 @@
newTab = true;
}
@@ -494,7 +520,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
aTab._endRemoveArgs = [closeWindow, newTab];
// swapBrowsersAndCloseOther will take care of closing the window without animation.
@@ -4955,9 +5080,7 @@
@@ -4955,9 +5091,7 @@
aTab._mouseleave();
if (newTab) {
@@ -505,7 +531,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
} else {
TabBarVisibility.update();
}
@@ -5090,6 +5213,7 @@
@@ -5090,6 +5224,7 @@
this.tabs[i]._tPos = i;
}
@@ -513,7 +539,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
if (!this._windowIsClosing) {
// update tab close buttons state
this.tabContainer._updateCloseButtons();
@@ -5302,6 +5426,7 @@
@@ -5302,6 +5437,7 @@
}
let excludeTabs = new Set(aExcludeTabs);
@@ -521,7 +547,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
// If this tab has a successor, it should be selectable, since
// hiding or closing a tab removes that tab as a successor.
@@ -5314,13 +5439,13 @@
@@ -5314,13 +5450,13 @@
!excludeTabs.has(aTab.owner) &&
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
) {
@@ -537,7 +563,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
);
let tab = this.tabContainer.findNextTab(aTab, {
@@ -5336,7 +5461,7 @@
@@ -5336,7 +5472,7 @@
}
if (tab) {
@@ -546,7 +572,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
}
// If no qualifying visible tab was found, see if there is a tab in
@@ -5357,7 +5482,7 @@
@@ -5357,7 +5493,7 @@
});
}
@@ -555,7 +581,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
}
_blurTab(aTab) {
@@ -5759,10 +5884,10 @@
@@ -5759,10 +5895,10 @@
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
}
@@ -568,7 +594,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
aTab.selected ||
aTab.closing ||
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
@@ -5952,7 +6077,7 @@
@@ -5952,7 +6088,7 @@
* `true` if element is a `<tab-group>`
*/
isTabGroup(element) {
@@ -577,7 +603,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
}
/**
@@ -6029,7 +6154,7 @@
@@ -6029,7 +6165,7 @@
// Don't allow mixing pinned and unpinned tabs.
if (this.isTab(element) && element.pinned) {
@@ -586,7 +612,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
} else {
tabIndex = Math.max(tabIndex, this.pinnedTabCount);
}
@@ -6055,10 +6180,16 @@
@@ -6055,10 +6191,16 @@
this.#handleTabMove(
element,
() => {
@@ -605,7 +631,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
if (neighbor && this.isTab(element) && tabIndex > element._tPos) {
neighbor.after(element);
} else {
@@ -6116,13 +6247,13 @@
@@ -6116,22 +6258,26 @@
#moveTabNextTo(element, targetElement, moveBefore = false, metricsContext) {
if (this.isTabGroupLabel(targetElement)) {
targetElement = targetElement.group;
@@ -619,9 +645,9 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
- if (this.isTabGroupLabel(element)) {
+ if (this.isTabGroupLabel(element) || element.group?.hasAttribute("split-view-group")) {
element = element.group;
if (targetElement?.group) {
targetElement = targetElement.group;
@@ -6130,8 +6261,15 @@
- if (targetElement?.group) {
- targetElement = targetElement.group;
- }
}
// Don't allow mixing pinned and unpinned tabs.
@@ -638,7 +664,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
moveBefore = false;
} else if (!element.pinned && targetElement && targetElement.pinned) {
// If the caller asks to move an unpinned element next to a pinned
@@ -6145,7 +6283,7 @@
@@ -6145,7 +6291,7 @@
// move the tab group right before the first unpinned tab.
// 4. Moving a tab group and the first unpinned tab is grouped:
// move the tab group right before the first unpinned tab's tab group.
@@ -647,7 +673,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
if (targetElement.group) {
targetElement = targetElement.group;
}
@@ -6153,6 +6291,7 @@
@@ -6153,6 +6299,7 @@
}
let getContainer = () =>
@@ -655,7 +681,16 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
element.pinned
? this.tabContainer.pinnedTabsContainer
: this.tabContainer;
@@ -6210,7 +6349,7 @@
@@ -6161,7 +6308,7 @@
element,
() => {
if (moveBefore) {
- getContainer().insertBefore(element, targetElement);
+ targetElement.parentElement.insertBefore(element, targetElement);
} else if (targetElement) {
targetElement.after(element);
} else {
@@ -6210,7 +6357,7 @@
if (!this.isTab(aTab)) {
throw new Error("Can only move a tab into a tab group");
}
@@ -664,7 +699,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
return;
}
if (aTab.group && aTab.group.id === aGroup.id) {
@@ -6304,6 +6443,10 @@
@@ -6304,6 +6451,10 @@
moveActionCallback();
@@ -675,7 +710,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
// Clear tabs cache after moving nodes because the order of tabs may have
// changed.
this.tabContainer._invalidateCachedTabs();
@@ -7198,7 +7341,7 @@
@@ -7198,7 +7349,7 @@
// preventDefault(). It will still raise the window if appropriate.
break;
}
@@ -684,7 +719,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
window.focus();
aEvent.preventDefault();
break;
@@ -8143,6 +8286,7 @@
@@ -8143,6 +8294,7 @@
aWebProgress.isTopLevel
) {
this.mTab.setAttribute("busy", "true");
@@ -692,7 +727,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
gBrowser._tabAttrModified(this.mTab, ["busy"]);
this.mTab._notselectedsinceload = !this.mTab.selected;
}
@@ -9108,7 +9252,7 @@ var TabContextMenu = {
@@ -9108,7 +9260,7 @@ var TabContextMenu = {
);
contextUnpinSelectedTabs.hidden =
!this.contextTab.pinned || !this.multiselected;
@@ -701,7 +736,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..41f869c4841ab4055b706487611f022d
// Move Tab items
let contextMoveTabOptions = document.getElementById(
"context_moveTabOptions"
@@ -9384,6 +9528,7 @@ var TabContextMenu = {
@@ -9384,6 +9536,7 @@ var TabContextMenu = {
)
);
} else {

View File

@@ -1,8 +1,154 @@
diff --git a/browser/components/tabbrowser/content/tabgroup.js b/browser/components/tabbrowser/content/tabgroup.js
index 2ed2fa02b16013e40a32391b3dba0a4301d65262..f485a7e2e09d81d0699e38e19fbd6d184d6c00ea 100644
index 2ed2fa02b16013e40a32391b3dba0a4301d65262..aea9fb03dbc28a524ec5a26e76510b1fda11a48c 100644
--- a/browser/components/tabbrowser/content/tabgroup.js
+++ b/browser/components/tabbrowser/content/tabgroup.js
@@ -270,7 +270,7 @@
@@ -13,10 +13,12 @@
class MozTabbrowserTabGroup extends MozXULElement {
static markup = `
- <vbox class="tab-group-label-container" pack="center">
+ <hbox class="tab-group-label-container" pack="center">
<label class="tab-group-label" role="button"/>
- </vbox>
- <html:slot/>
+ </hbox>
+ <html:div class="tab-group-container">
+ <html:div class="zen-tab-group-start"/>
+ </html:div>
`;
/** @type {string} */
@@ -45,16 +47,22 @@
}
connectedCallback() {
+ if (this.group && this._lastGroup != this.group) {
+ this._lastGroup = this.group;
+ this.group.dispatchEvent(
+ new CustomEvent("FolderGrouped", {
+ bubbles: true,
+ detail: this,
+ })
+ );
+ }
// Always set the mutation observer to listen for tab change events, even
// if we are already initialized.
// This is needed to ensure events continue to fire even if the tab group is
// moved from the horizontal to vertical tab layout or vice-versa, which
// causes the component to be repositioned in the DOM.
- this.#observeTabChanges();
- if (this._initialized) {
- return;
- }
+ if (!this._initialized) {
this._initialized = true;
this.saveOnWindowClose = true;
@@ -68,12 +76,14 @@
this.#labelElement.container = gBrowser.tabContainer;
this.#labelElement.group = this;
- this.#labelElement.addEventListener("click", this);
+ this.querySelector(".tab-group-label-container").addEventListener("click", this);
+ if (!this.isZenFolder) {
this.#labelElement.addEventListener("contextmenu", e => {
e.preventDefault();
gBrowser.tabGroupMenu.openEditModal(this);
return false;
});
+ }
this.#updateLabelAriaAttributes();
this.#updateCollapsedAriaAttributes();
@@ -93,6 +103,11 @@
// claim that a tab group was created by adoption the first time it
// mounts after getting created by `Tabbrowser.adoptTabGroup`.
this.#wasCreatedByAdoption = false;
+ this.appendChild = function (child) {
+ this.querySelector(".tab-group-container").appendChild(child);
+ }
+ }
+ this.#observeTabChanges();
}
disconnectedCallback() {
@@ -123,7 +138,10 @@
}
});
}
- this.#tabChangeObserver.observe(this, { childList: true });
+ const container = this.querySelector(".tab-group-container");
+ if (container) {
+ this.#tabChangeObserver.observe(container, { childList: true });
+ }
}
get color() {
@@ -172,7 +190,6 @@
// always create a text node and get consistent layout.
this.setAttribute("label", val || "\u200b");
- this.dataset.tooltip = val;
this.#updateLabelAriaAttributes();
if (diff) {
@@ -244,7 +261,53 @@
}
get tabs() {
- return Array.from(this.children).filter(node => node.matches("tab"));
+ // add other group tabs if they are under this group
+ let childs = Array.from(this.querySelector(".tab-group-container")?.children ?? []);
+ const tabsCollect = [];
+ for (let item of childs) {
+ tabsCollect.push(item);
+ if (gBrowser.isTabGroup(item)) {
+ tabsCollect.push(...item.tabs);
+ }
+ }
+ return tabsCollect.filter(node => node.matches("tab"));
+ }
+
+ get childGroupsAndTabs() {
+ const result = [];
+ const container = this.querySelector(".tab-group-container");
+
+ for (const item of Array.from(container.children)) {
+ if (gBrowser.isTab(item)) {
+ result.push(item);
+ } else if (gBrowser.isTabGroup(item)) {
+ const labelContainer = item.labelElement;
+ labelContainer.visible = item.visible;
+ if (gBrowser.isTabGroupLabel(labelContainer)) {
+ result.push(labelContainer);
+ }
+ result.push(...item.childGroupsAndTabs);
+ }
+ }
+ return result;
+ }
+
+ get group() {
+ if (gBrowser.isTabGroup(this.parentElement?.parentElement)) {
+ return this.parentElement.parentElement;
+ }
+ return null;
+ }
+
+ get visible() {
+ let currentGroup = this;
+ while (currentGroup?.group) {
+ currentGroup = currentGroup?.group;
+ if (currentGroup.collapsed) {
+ return false;
+ }
+ }
+ return true;
}
/**
@@ -270,7 +333,7 @@
*/
addTabs(tabs, metricsContext) {
for (let tab of tabs) {
@@ -11,3 +157,19 @@ index 2ed2fa02b16013e40a32391b3dba0a4301d65262..f485a7e2e09d81d0699e38e19fbd6d18
tab.ownerGlobal.gBrowser.unpinTab(tab);
}
let tabToMove =
@@ -333,7 +396,7 @@
* @param {PointerEvent} event
*/
on_click(event) {
- if (event.target === this.#labelElement && event.button === 0) {
+ if (event.target.closest('.tab-group-label-container') && event.button === 0) {
event.preventDefault();
this.collapsed = !this.collapsed;
gBrowser.tabGroupMenu.close();
@@ -364,5 +427,6 @@
}
}
+ window.MozTabbrowserTabGroup = MozTabbrowserTabGroup;
customElements.define("tab-group", MozTabbrowserTabGroup);
}

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/tabbrowser/content/tabs.js b/browser/components/tabbrowser/content/tabs.js
index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e2ccc453a 100644
index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..1b934a6837a79e378d729462b62de424b0741bbd 100644
--- a/browser/components/tabbrowser/content/tabs.js
+++ b/browser/components/tabbrowser/content/tabs.js
@@ -289,6 +289,7 @@
@@ -28,16 +28,23 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
let tabsPerRow = 0;
let position = RTL_UI
? window.windowUtils.getBoundsWithoutFlushing(
@@ -780,7 +781,7 @@
} else if (isTabGroupLabel(tab) && !tab.group.collapsed) {
@@ -777,11 +778,12 @@
if (tab.multiselected) {
this.#moveTogetherSelectedTabs(tab);
- } else if (isTabGroupLabel(tab) && !tab.group.collapsed) {
+ } else if (isTabGroupLabel(tab)) {
this._lockTabSizing();
this.#keepTabSizeLocked = true;
- tab.group.collapsed = true;
- expandGroupOnDrop = true;
+ tab.group.collapsed = !tab.group.hasAttribute("split-view-group");
expandGroupOnDrop = true;
+ expandGroupOnDrop = !tab.group.collapsed || tab.group.hasAttribute("has-active");
+ gZenFolders.collapseVisibleTab(tab.group);
}
}
@@ -879,7 +880,7 @@
@@ -879,7 +881,7 @@
? event.screenY - window.screenY - tabOffset
: event.screenY - window.screenY,
scrollPos:
@@ -46,7 +53,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
? this.pinnedTabsContainer.scrollPosition
: this.arrowScrollbox.scrollPosition,
screenX: event.screenX,
@@ -933,6 +934,10 @@
@@ -933,6 +935,10 @@
}
let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0);
@@ -57,7 +64,18 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
if (
(effects == "move" || effects == "copy") &&
document == draggedTab.ownerDocument &&
@@ -1089,6 +1094,18 @@
@@ -1060,7 +1066,9 @@
isTabGroupLabel(draggedTab) &&
draggedTab._dragData?.expandGroupOnDrop
) {
- draggedTab.group.collapsed = false;
+ const isActive = draggedTab.group.hasAttribute("has-active");
+ draggedTab.group.collapsed = isActive;
+ if (isActive) gZenFolders.expandVisibleTab(draggedTab.group);
this.#keepTabSizeLocked = false;
this._unlockTabSizing();
}
@@ -1089,6 +1097,18 @@
this._tabDropIndicator.hidden = true;
event.stopPropagation();
@@ -76,11 +94,13 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
if (draggedTab && dropEffect == "copy") {
let duplicatedDraggedTab;
let duplicatedTabs = [];
@@ -1128,10 +1145,11 @@
@@ -1127,11 +1147,12 @@
newTranslateY -= tabHeight;
}
} else {
let isPinned = draggedTab.pinned;
- let isPinned = draggedTab.pinned;
- let numPinned = gBrowser.pinnedTabCount;
+ let isPinned = draggedTab?.group ? draggedTab.group.pinned : draggedTab.pinned;
+ let numPinned = gBrowser._numVisiblePinTabsWithoutCollapsed;
+ let essential = draggedTab.hasAttribute("zen-essential");
let tabs = this.ariaFocusableItems.slice(
@@ -91,16 +111,15 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
);
let size = this.verticalMode ? "height" : "width";
let screenAxis = this.verticalMode ? "screenY" : "screenX";
@@ -1180,7 +1198,7 @@
(oldTranslateX && oldTranslateX != newTranslateX) ||
@@ -1181,6 +1202,7 @@
(oldTranslateY && oldTranslateY != newTranslateY);
} else if (this.verticalMode) {
- shouldTranslate &&= oldTranslateY && oldTranslateY != newTranslateY;
+ shouldTranslate &&= oldTranslateY && oldTranslateY != newTranslateY && movingTabs.length === 1;
shouldTranslate &&= oldTranslateY && oldTranslateY != newTranslateY;
+ shouldTranslate = false; // TODO: Find a way to animate vertical tab moves.
} else {
shouldTranslate &&= oldTranslateX && oldTranslateX != newTranslateX;
}
@@ -1349,6 +1367,7 @@
@@ -1349,6 +1371,7 @@
let nextItem = this.ariaFocusableItems[newIndex];
let tabGroup = isTab(nextItem) && nextItem.group;
@@ -108,7 +127,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
gBrowser.loadTabs(urls, {
inBackground,
replace,
@@ -1381,6 +1400,17 @@
@@ -1381,6 +1404,17 @@
this.finishMoveTogetherSelectedTabs(draggedTab);
this.finishAnimateTabMove();
@@ -126,7 +145,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
this.#expandGroupOnDrop(draggedTab);
if (
@@ -1607,7 +1637,7 @@
@@ -1607,7 +1641,7 @@
}
get newTabButton() {
@@ -135,7 +154,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
}
get verticalMode() {
@@ -1623,6 +1653,7 @@
@@ -1623,6 +1657,7 @@
}
get overflowing() {
@@ -143,7 +162,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
return this.hasAttribute("overflow");
}
@@ -1631,26 +1662,54 @@
@@ -1631,26 +1666,54 @@
if (this.#allTabs) {
return this.#allTabs;
}
@@ -205,7 +224,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
}
/**
@@ -1717,20 +1776,17 @@
@@ -1717,33 +1780,27 @@
let elementIndex = 0;
@@ -225,11 +244,16 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
child.labelElement.elementIndex = elementIndex++;
focusableItems.push(child.labelElement);
- if (!child.collapsed) {
+ if (!child.collapsed && !child.hasAttribute("split-view-group")) {
let visibleTabsInGroup = child.tabs.filter(tab => tab.visible);
visibleTabsInGroup.forEach(tab => {
- let visibleTabsInGroup = child.tabs.filter(tab => tab.visible);
- visibleTabsInGroup.forEach(tab => {
+ if (!child.hasAttribute("split-view-group")) {
+ let visibleTabsAndGroupsInGroup = child.childGroupsAndTabs.filter(tab => tab.visible);
+ visibleTabsAndGroupsInGroup.forEach(tab => {
tab.elementIndex = elementIndex++;
@@ -1740,10 +1796,7 @@
});
- focusableItems.push(...visibleTabsInGroup);
+ focusableItems.push(...visibleTabsAndGroupsInGroup);
}
}
}
@@ -241,7 +265,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
return this.#focusableItems;
}
@@ -1751,6 +1804,7 @@
@@ -1751,6 +1808,7 @@
_invalidateCachedTabs() {
this.#allTabs = null;
this._invalidateCachedVisibleTabs();
@@ -249,7 +273,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
}
_invalidateCachedVisibleTabs() {
@@ -1766,8 +1820,8 @@
@@ -1766,8 +1824,8 @@
#isContainerVerticalPinnedGrid(tab) {
return (
this.verticalMode &&
@@ -260,7 +284,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
!this.expandOnHover
);
}
@@ -1783,7 +1837,7 @@
@@ -1783,7 +1841,7 @@
if (node == null) {
// We have a container for non-tab elements at the end of the scrollbox.
@@ -269,7 +293,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
}
node.before(tab);
@@ -1878,7 +1932,7 @@
@@ -1878,7 +1936,7 @@
// There are separate "new tab" buttons for horizontal tabs toolbar, vertical tabs and
// for when the tab strip is overflowed (which is shared by vertical and horizontal tabs);
// Attach the long click popup to all of them.
@@ -278,7 +302,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
const newTab2 = this.newTabButton;
const newTabVertical = document.getElementById(
"vertical-tabs-newtab-button"
@@ -1973,10 +2027,12 @@
@@ -1973,10 +2031,12 @@
_handleTabSelect(aInstant) {
let selectedTab = this.selectedItem;
@@ -291,7 +315,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
selectedTab._notselectedsinceload = false;
}
@@ -2130,7 +2186,7 @@
@@ -2130,7 +2190,7 @@
return;
}
@@ -300,7 +324,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
let directionX = screenX > dragData.animLastScreenX;
let directionY = screenY > dragData.animLastScreenY;
@@ -2139,6 +2195,8 @@
@@ -2139,6 +2199,8 @@
let { width: tabWidth, height: tabHeight } =
draggedTab.getBoundingClientRect();
@@ -309,7 +333,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
let shiftSizeX = tabWidth * movingTabs.length;
let shiftSizeY = tabHeight;
dragData.tabWidth = tabWidth;
@@ -2168,7 +2226,7 @@
@@ -2168,7 +2230,7 @@
let translateX = screenX - dragData.screenX;
let translateY = screenY - dragData.screenY;
translateY +=
@@ -318,7 +342,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
let firstBoundX = firstTabInRow.screenX - firstMovingTabScreenX;
let firstBoundY = firstTabInRow.screenY - firstMovingTabScreenY;
let lastBoundX =
@@ -2294,7 +2352,7 @@
@@ -2294,7 +2356,7 @@
}
dragData.animDropElementIndex = newIndex;
@@ -327,7 +351,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
dragData.dropBefore = newIndex < tabs.length;
// Shift background tabs to leave a gap where the dragged tab
@@ -2327,12 +2385,16 @@
@@ -2327,12 +2389,16 @@
this.#clearDragOverCreateGroupTimer();
@@ -348,7 +372,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
if (this.#rtlMode) {
tabs.reverse();
@@ -2346,7 +2408,7 @@
@@ -2346,7 +2412,7 @@
let size = this.verticalMode ? "height" : "width";
let translateAxis = this.verticalMode ? "translateY" : "translateX";
let scrollDirection = this.verticalMode ? "scrollTop" : "scrollLeft";
@@ -357,7 +381,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
let translateX = event.screenX - dragData.screenX;
let translateY = event.screenY - dragData.screenY;
@@ -2360,12 +2422,21 @@
@@ -2360,12 +2426,18 @@
let lastTab = tabs.at(-1);
let lastMovingTab = movingTabs.at(-1);
let firstMovingTab = movingTabs[0];
@@ -371,16 +395,13 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
let lastMovingTabScreen = endEdge(lastMovingTab);
let firstMovingTabScreen = firstMovingTab[screenAxis];
let shiftSize = lastMovingTabScreen - firstMovingTabScreen;
+ if (firstMovingTab.hasAttribute("split-view-group")) {
+ shiftSize += 5; // A hack to allow more space for the group
+ }
let translate = screen - dragData[screenAxis];
- if (!isPinned) {
+ if (true) {
translate +=
this.arrowScrollbox.scrollbox[scrollDirection] - dragData.scrollPos;
} else if (isPinned && this.verticalMode) {
@@ -2384,6 +2455,9 @@
@@ -2384,6 +2456,9 @@
// Shift the `.tab-group-label-container` to shift the label element.
item = item.parentElement;
}
@@ -390,17 +411,25 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
item.style.transform = `${translateAxis}(${translate}px)`;
}
@@ -2521,6 +2595,9 @@
@@ -2521,6 +2596,9 @@
break;
}
let element = tabs[mid];
+ if (element?.group?.hasAttribute("split-view-group")) {
+ element = element.group.labelElement;
+ element = element.group;
+ }
let elementForSize = isTabGroupLabel(element)
? element.parentElement
: element;
@@ -2604,7 +2681,7 @@
@@ -2540,6 +2618,7 @@
};
let dropElement = getOverlappedElement();
+ if (dropElement?.hasAttribute("split-view-group")) dropElement = dropElement.labelElement;
let newDropElementIndex;
if (dropElement) {
@@ -2604,7 +2683,7 @@
let shouldCreateGroupOnDrop;
let dropBefore;
if (dropElement) {
@@ -409,16 +438,40 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
? dropElement.parentElement
: dropElement;
@@ -2659,7 +2736,7 @@
if (
isTabGroupLabel(draggedTab) &&
dropElement?.group &&
- !dropElement.group.collapsed
+ !dropElement.group.collapsed && !dropElement.group.hasAttribute("split-view-group")
) {
let overlappedGroup = dropElement.group;
@@ -2624,7 +2703,7 @@
? Services.prefs.getIntPref(
"browser.tabs.dragDrop.moveOverThresholdPercent"
) / 100
- : 0.5;
+ : Services.prefs.getIntPref('zen.view.drag-and-drop.move-over-threshold') / 100;
moveOverThreshold = Math.min(1, Math.max(0, moveOverThreshold));
let shouldMoveOver = overlapPercent > moveOverThreshold;
if (logicalForward && shouldMoveOver) {
@@ -2656,23 +2735,6 @@
@@ -2686,12 +2763,7 @@
// If dragging a group over another group, don't make it look like it is
// possible to drop the dragged group inside the other group.
- if (
- isTabGroupLabel(draggedTab) &&
- dropElement?.group &&
- !dropElement.group.collapsed
- ) {
- let overlappedGroup = dropElement.group;
-
- if (isTabGroupLabel(dropElement)) {
- dropBefore = true;
- newDropElementIndex = dropElement.elementIndex;
- } else {
- dropBefore = false;
- newDropElementIndex = overlappedGroup.tabs.at(-1).elementIndex + 1;
- }
-
- dropElement = overlappedGroup;
- }
// Constrain drop direction at the boundary between pinned and
// unpinned tabs so that they don't mix together.
@@ -2686,14 +2748,13 @@
}
}
@@ -428,20 +481,64 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
- !isPinned &&
- (!numPinned || newDropElementIndex > numPinned)
- ) {
+ if (isTab(draggedTab)) {
+ if (isTab(draggedTab) || isTabGroupLabel(draggedTab)) {
let dragOverGroupingThreshold = 1 - moveOverThreshold;
+ if (draggedTab && !dropElement?.group) {
+ gZenFolders.highlightGroupOnDragOver(null);
+ }
+
// When dragging tab(s) over an ungrouped tab, signal to the user
@@ -2739,7 +2811,7 @@
// Dropping right before the tab group.
dropElement = dropElementGroup;
colorCode = undefined;
// that dropping the tab(s) will create a new tab group.
shouldCreateGroupOnDrop =
@@ -2703,12 +2764,12 @@
overlapPercent > dragOverGroupingThreshold;
if (shouldCreateGroupOnDrop) {
- this.#dragOverCreateGroupTimer = setTimeout(
- () => this.#triggerDragOverCreateGroup(dragData, dropElement),
- Services.prefs.getIntPref(
- "browser.tabs.dragDrop.createGroup.delayMS"
- )
- );
+ // this.#dragOverCreateGroupTimer = setTimeout(
+ // () => this.#triggerDragOverCreateGroup(dragData, dropElement),
+ // Services.prefs.getIntPref(
+ // "browser.tabs.dragDrop.createGroup.delayMS"
+ // )
+ // );
} else {
this.removeAttribute("movingtab-createGroup");
document
@@ -2735,19 +2796,14 @@
dropElement = dropElementGroup;
colorCode = undefined;
} else if (isTabGroupLabel(dropElement)) {
- if (dropBefore) {
- // Dropping right before the tab group.
- dropElement = dropElementGroup;
- colorCode = undefined;
- } else if (dropElementGroup.collapsed) {
+ } else if (dropElement?.group?.hasAttribute("split-view-group")) {
// Dropping right after the collapsed tab group.
dropElement = dropElementGroup;
colorCode = undefined;
@@ -2769,7 +2841,7 @@
- // Dropping right after the collapsed tab group.
- dropElement = dropElementGroup;
- colorCode = undefined;
- } else {
- // Dropping right before the first tab in the tab group.
- dropElement = dropElementGroup.tabs[0];
- dropBefore = true;
- }
+ ({ dropElement, colorCode, dropBefore } = gZenFolders.handleDragOverTabGroupLabel(
+ dropElement,
+ draggedTab,
+ overlapPercent,
+ movingTabs,
+ dropBefore,
+ colorCode
+ ));
}
this.#setDragOverGroupColor(colorCode);
this.toggleAttribute("movingtab-ungroup", !colorCode);
@@ -2769,15 +2825,24 @@
// Shift background tabs to leave a gap where the dragged tab
// would currently be dropped.
for (let item of tabs) {
@@ -450,7 +547,14 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
continue;
}
@@ -2778,6 +2850,9 @@
let shift = getTabShift(item, newDropElementIndex);
let transform = shift ? `${translateAxis}(${shift}px)` : "";
+ if (item.group?.hasAttribute("split-view-group")) {
+ item = item.group;
+ }
+ if (item.group?.hasAttribute("has-active") && draggedTab.group != item.group) {
+ item = item.group;
+ }
if (isTabGroupLabel(item)) {
// Shift the `.tab-group-label-container` to shift the label element.
item = item.parentElement;
@@ -460,7 +564,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
}
item.style.transform = transform;
}
@@ -2830,8 +2905,9 @@
@@ -2830,12 +2895,14 @@
);
}
@@ -472,10 +576,21 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
return;
}
@@ -2843,6 +2919,12 @@
this.#setMovingTabMode(false);
+ gZenFolders.highlightGroupOnDragOver(null);
for (let item of this.ariaFocusableItems) {
if (isTabGroupLabel(item)) {
@@ -2843,6 +2910,18 @@
item = item.parentElement;
}
item.style.transform = "";
+ if (item.closest("zen-folder")?.hasAttribute("has-active")) item.closest("zen-folder").style.transform = "";
+ if (item.closest("zen-folder")?.hasAttribute("has-active")) {
+ for (let tab of item.closest("zen-folder").tabs) {
+ tab.style.transform = "";
+ }
+ }
+ if (item.closest("tab-group")?.hasAttribute("split-view-group")) item.closest("tab-group").style.transform = "";
+ if (item.closest("tab-group")?.hasAttribute("split-view-group")) {
+ for (let tab of item.closest("tab-group").tabs) {
@@ -485,7 +600,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
item.removeAttribute("dragover-createGroup");
}
this.removeAttribute("movingtab-createGroup");
@@ -2889,7 +2971,7 @@
@@ -2889,7 +2968,7 @@
let postTransitionCleanup = () => {
movingTab._moveTogetherSelectedTabsData.animate = false;
};
@@ -494,7 +609,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
postTransitionCleanup();
} else {
let onTransitionEnd = transitionendEvent => {
@@ -3062,7 +3144,7 @@
@@ -3062,7 +3141,7 @@
}
_notifyBackgroundTab(aTab) {
@@ -503,6 +618,18 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
return;
}
@@ -3171,7 +3250,10 @@
#getDragTarget(event, { ignoreSides = false } = {}) {
let { target } = event;
while (target) {
- if (isTab(target) || isTabGroupLabel(target)) {
+ if (isTab(target) || isTabGroupLabel(target) || target?.classList?.contains("tab-group-label-container")) {
+ if (target.classList?.contains("tab-group-label-container")) {
+ target = target.querySelector(".tab-group-label");
+ }
break;
}
target = target.parentNode;
@@ -3188,6 +3270,9 @@
return null;
}

View File

@@ -1,5 +1,5 @@
diff --git a/browser/themes/shared/tabbrowser/tabs.css b/browser/themes/shared/tabbrowser/tabs.css
index 79a95268e590b1561510460f63270a4d3814bc05..d1f91562ff429cc149bbd1cdccd49ab0abab2780 100644
index 79a95268e5..3a9fe04d6a 100644
--- a/browser/themes/shared/tabbrowser/tabs.css
+++ b/browser/themes/shared/tabbrowser/tabs.css
@@ -19,7 +19,7 @@
@@ -102,6 +102,30 @@ index 79a95268e590b1561510460f63270a4d3814bc05..d1f91562ff429cc149bbd1cdccd49ab0
&:is([soundplaying], [muted], [activemedia-blocked]) {
display: flex;
}
@@ -1004,20 +997,20 @@ tab-group {
*/
display: contents;
- #tabbrowser-tabs[orient="horizontal"] &[collapsed] > .tabbrowser-tab {
+ #tabbrowser-tabs[orient="horizontal"] &[collapsed] .tab-group-container > .tabbrowser-tab {
min-width: 0 !important;
max-width: 0 !important;
padding: 0;
overflow-clip-margin: 0;
}
- #tabbrowser-tabs[orient="vertical"] &[collapsed] > .tabbrowser-tab {
+ #tabbrowser-tabs[orient="vertical"] &[collapsed] .tab-group-container > .tabbrowser-tab {
display: none;
}
}
#tabbrowser-tabs[orient="vertical"][expanded] {
- tab-group > :is(.tab-group-label-container, .tabbrowser-tab),
+ tab-group > :is(.tab-group-label-container, .tab-group-container, .tabbrowser-tab),
&[movingtab][movingtab-addToGroup]:not([movingtab-createGroup], [movingtab-ungroup]) .tabbrowser-tab:is(:active, [multiselected]) {
margin-inline-start: var(--space-medium);
}
@@ -1370,7 +1363,7 @@ tab-group {
}
}

View File

@@ -0,0 +1,5 @@
# Icons used in Zen's emoji picker:
_Credit to MacKenzie Nason for the Icons and Favicons_
figma file: https://www.figma.com/community/file/1228728710215940920

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.84167 13.5836L6.62299 15.1069C6.57158 15.1711 6.49377 15.2086 6.41152 15.2086H6L6.54164 12.5002L6 9.79171H6.41152C6.49377 9.79171 6.57158 9.82917 6.62299 9.89337L7.84167 11.4167H10.8751L9.85657 6.32393C9.82719 6.17729 9.92232 6.03456 10.069 6.00526C10.0865 6.00172 10.1043 6 10.1221 6H10.3335C10.6695 6 10.9809 6.17629 11.1538 6.46448L14.1252 11.4166H17.2564C17.6967 11.4166 18.1265 11.5507 18.4887 11.8012L18.7159 11.9583C19.0151 12.1652 19.0899 12.5753 18.8831 12.8745C18.8379 12.9399 18.7813 12.9965 18.7159 13.0416L18.4887 13.1988C18.1265 13.4492 17.6967 13.5833 17.2564 13.5833H14.1252L11.1538 18.5355C10.9809 18.8236 10.6695 18.9999 10.3335 18.9999H10.1221C10.1043 18.9999 10.0865 18.9982 10.069 18.9947C9.92232 18.9654 9.82719 18.8227 9.85657 18.676L10.8751 13.5832L7.84167 13.5836Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 1004 B

View File

@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.49891 12.2216C6.2136 12.2074 5.99839 12.4631 6.05591 12.7429C6.27686 13.8176 6.80813 14.8085 7.58934 15.5897C8.37055 16.3709 9.36142 16.9022 10.4361 17.1231C10.7159 17.1807 10.9716 16.9654 10.9574 16.6801C10.947 16.4712 10.9228 16.2631 10.8848 16.0573C10.8439 15.835 10.6093 15.715 10.3969 15.7922L10.3654 15.8036C10.1811 15.8706 9.97752 15.7752 9.91117 15.5907C9.84515 15.4071 9.94 15.2047 10.1233 15.138L10.1559 15.1262C10.3683 15.0489 10.4715 14.8064 10.3606 14.6093C10.2775 14.4616 10.1864 14.3184 10.0877 14.1806C9.95583 13.9963 9.69182 13.9872 9.53158 14.1475L9.50608 14.173C9.368 14.3111 9.14414 14.3111 9.00607 14.173C8.86799 14.0349 8.86799 13.811 9.00607 13.673L9.03077 13.6483C9.19054 13.4885 9.18148 13.2253 8.99778 13.0937C8.85932 12.9945 8.71555 12.903 8.56712 12.8195C8.37063 12.709 8.12886 12.8121 8.05204 13.024L8.04067 13.0554C7.97418 13.2388 7.77177 13.3338 7.58816 13.2678C7.4038 13.2015 7.3084 12.998 7.37533 12.8139L7.38687 12.7821C7.46406 12.5697 7.34402 12.3352 7.12179 12.2942C6.91596 12.2563 6.70788 12.2321 6.49891 12.2216Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M12.7429 6.05602C12.463 5.99849 12.2074 6.21371 12.2216 6.49902C12.232 6.70798 12.2562 6.91607 12.2942 7.1219C12.3351 7.34412 12.5697 7.46417 12.7821 7.38698L12.8136 7.37551C12.9979 7.30854 13.2015 7.404 13.2678 7.58847C13.3338 7.77203 13.239 7.97442 13.0557 8.04111L13.0231 8.05295C12.8106 8.13026 12.7075 8.3728 12.8184 8.56985C12.9015 8.71759 12.9926 8.86071 13.0913 8.99856C13.2232 9.18283 13.4872 9.19192 13.6474 9.03168L13.6729 9.00617C13.811 8.8681 14.0348 8.8681 14.1729 9.00617C14.311 9.14425 14.311 9.36811 14.1729 9.50619L14.1483 9.53084C13.9885 9.69062 13.9976 9.95388 14.1814 10.0854C14.3198 10.1845 14.4635 10.2759 14.612 10.3593C14.8087 10.4699 15.0506 10.3668 15.1277 10.1548L15.1395 10.1224C15.2062 9.93889 15.409 9.84419 15.5925 9.91082C15.7761 9.97748 15.8709 10.1804 15.8041 10.364L15.7923 10.3965C15.715 10.6091 15.8352 10.8441 16.0578 10.885C16.2634 10.9229 16.4713 10.9471 16.6801 10.9575C16.9654 10.9717 17.1806 10.716 17.1231 10.4362C16.9021 9.36153 16.3709 8.37066 15.5896 7.58945C14.8084 6.80824 13.8176 6.27696 12.7429 6.05602Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M15.717 11.7797C15.4943 11.7321 15.2642 11.8474 15.1863 12.0615L15.1592 12.136C15.0924 12.3195 14.8896 12.4142 14.7061 12.3474C14.5227 12.2807 14.428 12.0779 14.4947 11.8944L14.5221 11.8189C14.6004 11.6036 14.4968 11.3661 14.2941 11.2595C14.0161 11.1133 13.7508 10.9439 13.5011 10.7533C13.3186 10.6139 13.059 10.6202 12.8966 10.7826L12.8396 10.8396C12.7015 10.9776 12.4777 10.9776 12.3396 10.8396C12.2015 10.7015 12.2015 10.4776 12.3396 10.3395L12.3966 10.2826C12.5589 10.1202 12.5653 9.8605 12.4259 9.67801C12.2352 9.42834 12.0659 9.16308 11.9196 8.88504C11.813 8.6823 11.5756 8.57876 11.3603 8.657L11.2847 8.68445C11.1013 8.75113 10.8985 8.65648 10.8317 8.47304C10.765 8.28955 10.8596 8.0867 11.0431 8.01995L11.1177 7.99281C11.3317 7.91494 11.447 7.68484 11.3995 7.46212C11.3323 7.14735 11.2919 6.82754 11.2788 6.50613C11.2666 6.20909 11.0236 5.96094 10.7296 6.00512C9.54523 6.1831 8.44321 6.73579 7.5895 7.5895C6.73579 8.44321 6.1831 9.54523 6.00512 10.7296C5.96094 11.0236 6.20909 11.2666 6.50613 11.2788C6.82754 11.2919 7.14735 11.3323 7.46212 11.3995C7.68484 11.447 7.91494 11.3317 7.99281 11.1177L8.01995 11.0431C8.0867 10.8596 8.28955 10.765 8.47304 10.8317C8.65648 10.8985 8.75113 11.1013 8.68445 11.2847L8.657 11.3603C8.57876 11.5756 8.6823 11.813 8.88504 11.9196C9.16308 12.0659 9.42834 12.2352 9.67801 12.4259C9.8605 12.5653 10.1202 12.5589 10.2826 12.3966L10.3395 12.3396C10.4776 12.2015 10.7015 12.2015 10.8396 12.3396C10.9776 12.4777 10.9776 12.7015 10.8396 12.8396L10.7826 12.8966C10.6202 13.059 10.6139 13.3186 10.7533 13.5011C10.9439 13.7508 11.1133 14.0161 11.2595 14.2941C11.3661 14.4968 11.6036 14.6004 11.8189 14.5221L11.8944 14.4947C12.0779 14.428 12.2807 14.5227 12.3474 14.7061C12.4142 14.8896 12.3195 15.0924 12.136 15.1592L12.0615 15.1863C11.8474 15.2642 11.7321 15.4943 11.7797 15.717C11.8469 16.0318 11.8872 16.3516 11.9004 16.673C11.9125 16.97 12.1556 17.2182 12.4496 17.174C13.6339 16.996 14.7359 16.4434 15.5896 15.5896C16.4434 14.7359 16.996 13.6339 17.174 12.4496C17.2182 12.1556 16.97 11.9125 16.673 11.9004C16.3516 11.8872 16.0318 11.8469 15.717 11.7797Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.9076 10.428H13.479L11.7747 8.04188L12.1816 7.75126L12.5884 7.46064L14.7079 10.428H17.113C17.4542 10.428 17.6952 10.7623 17.5873 11.0861L16.0346 15.7442C15.8985 16.1525 15.5163 16.428 15.0859 16.428H8.52743C8.097 16.428 7.71486 16.1525 7.57875 15.7442L6.02604 11.0861C5.91812 10.7623 6.1591 10.428 6.50038 10.428H8.6787L10.7982 7.46064C11.2369 6.84645 12.1497 6.84646 12.5884 7.46064L12.1816 7.75126L11.7747 8.04188C11.7348 7.98604 11.6518 7.98604 11.6119 8.04188L9.9076 10.428ZM10.8067 13.928C10.8067 13.3757 11.2544 12.928 11.8067 12.928C12.359 12.928 12.8067 13.3757 12.8067 13.928C12.8067 14.4802 12.359 14.928 11.8067 14.928C11.2544 14.928 10.8067 14.4802 10.8067 13.928Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 890 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 9.75C7 9.88807 7.11193 10 7.25 10H8.11358C8.25165 10 8.36358 9.88807 8.36358 9.75V9.57141C8.36358 9.09866 8.77134 8.71422 9.27272 8.71422H10.6363C11.1377 8.71422 11.5454 9.09868 11.5454 9.57141V9.75C11.5454 9.88807 11.6574 10 11.7954 10H12.2046C12.3426 10 12.4546 9.88807 12.4546 9.75V9.57141C12.4546 9.09866 12.8623 8.71422 13.3637 8.71422H14.7273C15.2287 8.71422 15.6364 9.09868 15.6364 9.57141V9.75C15.6364 9.88807 15.7484 10 15.8864 10H16.75C16.8881 10 17 9.88807 17 9.75V7.42859C17 7.19182 16.7964 7 16.5454 7H7.45457C7.20345 7 7 7.19193 7 7.42859V9.75ZM18 16.4545V12.6364L17.9999 12.6363C17.9999 11.7339 17.4232 10.9999 16.7142 11H7.28575C6.57675 11 6.0001 11.7339 6.0001 12.6363L6 16.4544C6 16.7557 6.19193 16.9999 6.42859 16.9999C6.66536 16.9999 6.85719 16.7556 6.85719 16.4544V15.9089L17.1428 15.909V16.4545C17.1428 16.7559 17.3347 17 17.5714 17C17.8082 17 18 16.7557 18 16.4545Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.2102 17.9021C13.031 17.9021 13.7036 17.2409 13.7036 16.4201H10.7167C10.7167 17.2409 11.3779 17.9135 12.2102 17.9021ZM17.215 14.425C16.7704 13.9461 15.9267 13.2165 15.9267 10.8338C15.9267 9.03253 14.6613 7.57327 12.9512 7.23125V6.74103C12.9512 6.5445 12.8731 6.35601 12.7341 6.21704C12.5952 6.07807 12.4067 6 12.2102 6C12.0136 6 11.8251 6.07807 11.6862 6.21704C11.5472 6.35601 11.4691 6.5445 11.4691 6.74103V7.23125C9.75904 7.58467 8.49359 9.03253 8.49359 10.8338C8.49359 13.2165 7.66135 13.9461 7.20533 14.425C7.06853 14.5732 7.00012 14.7556 7.00012 14.9266C7.00012 15.3028 7.30794 15.6676 7.74115 15.679H16.6791C17.1238 15.679 17.4202 15.3142 17.4202 14.9266C17.4202 14.7442 17.3632 14.5732 17.215 14.425Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 882 B

View File

@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 14.9291V6.34512C6 6.25062 6.06948 6.1705 6.16303 6.15714L6.70303 6.07999C8.51235 5.82152 10.3555 6.19373 11.9227 7.13407C11.9707 7.16283 12 7.21464 12 7.27054V17.1804C10.6894 16.1975 9.14493 15.5735 7.51931 15.3703L6.24936 15.2116C6.1069 15.1938 6 15.0727 6 14.9291Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M19 14.9291L19 6.34512C19 6.25062 18.9305 6.1705 18.837 6.15714L18.297 6.08C16.4876 5.82152 14.6445 6.19373 13.0773 7.13407C13.0293 7.16283 13 7.21464 13 7.27054L13 17.1804C14.3106 16.1975 15.8551 15.5735 17.4807 15.3703L18.7506 15.2116C18.8931 15.1938 19 15.0727 19 14.9291Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 785 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.5 5.5C8.39543 5.5 7.5 6.39543 7.5 7.5V17.5C7.5 17.9091 7.74563 18.2608 8.09744 18.4157C8.34017 18.5225 8.60536 18.3946 8.79289 18.2071L11.2929 15.7071C11.6834 15.3166 12.3166 15.3166 12.7071 15.7071L15.2071 18.2071C15.3946 18.3946 15.6598 18.5225 15.9026 18.4157C16.2544 18.2608 16.5 17.9091 16.5 17.5V7.5C16.5 6.39543 15.6046 5.5 14.5 5.5H9.5Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 559 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5 18C16.0899 18 19 15.0899 19 11.5C19 7.91015 16.0899 5 12.5 5C8.91015 5 6 7.91015 6 11.5C6 12.9273 6.46006 14.2472 7.23998 15.3194C7.26155 15.8859 7.18156 16.4553 7 17L6.5 18.5L9.44403 17.2383C10.355 17.7244 11.3953 18 12.5 18ZM9.5 13C10.0523 13 10.5 12.5523 10.5 12C10.5 11.4477 10.0523 11 9.5 11C8.94772 11 8.5 11.4477 8.5 12C8.5 12.5523 8.94772 13 9.5 13ZM12.5 13C13.0523 13 13.5 12.5523 13.5 12C13.5 11.4477 13.0523 11 12.5 11C11.9477 11 11.5 11.4477 11.5 12C11.5 12.5523 11.9477 13 12.5 13ZM16.5 12C16.5 12.5523 16.0523 13 15.5 13C14.9477 13 14.5 12.5523 14.5 12C14.5 11.4477 14.9477 11 15.5 11C16.0523 11 16.5 11.4477 16.5 12Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 849 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.5 6.5C7.39543 6.5 6.5 7.39543 6.5 8.5V15.5C6.5 16.6046 7.39543 17.5 8.5 17.5H15.5C16.6046 17.5 17.5 16.6046 17.5 15.5V8.5C17.5 7.39543 16.6046 6.5 15.5 6.5H8.5ZM14.8763 9.82925C15.0581 9.62143 15.0371 9.30555 14.8293 9.12371C14.6214 8.94187 14.3056 8.96293 14.1237 9.17075L10.9756 12.7685L9.85355 11.6464C9.65829 11.4512 9.34171 11.4512 9.14645 11.6464C8.95118 11.8417 8.95118 12.1583 9.14645 12.3536L10.6464 13.8536C10.7443 13.9514 10.8783 14.0043 11.0166 13.9997C11.1549 13.9951 11.2852 13.9334 11.3763 13.8293L14.8763 9.82925Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 744 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12.5" cy="11.5" r="5.5" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 197 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.06652 10.5825C7.46543 10.4185 7.7892 10.0883 7.9396 9.68401C8.51925 8.12601 10.1927 7 12.1666 7C14.1245 7 15.7867 8.10776 16.3793 9.64606C16.5382 10.0587 16.8783 10.3918 17.2918 10.5485C18.55 11.0251 19.4444 12.2414 19.4444 13.6667C19.4444 15.5076 17.9521 17 16.1111 17H8.33333C6.49238 17 5 15.5076 5 13.6667C5 12.2742 5.85384 11.0811 7.06652 10.5825Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 566 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.03033 7.96967C9.32322 8.26256 9.32322 8.73744 9.03033 9.03033L6.06066 12L9.03033 14.9697C9.32322 15.2626 9.32322 15.7374 9.03033 16.0303C8.73744 16.3232 8.26256 16.3232 7.96967 16.0303L4.46967 12.5303C4.17678 12.2374 4.17678 11.7626 4.46967 11.4697L7.96967 7.96967C8.26256 7.67678 8.73744 7.67678 9.03033 7.96967ZM14.9697 7.96967C15.2626 7.67678 15.7374 7.67678 16.0303 7.96967L19.5303 11.4697C19.8232 11.7626 19.8232 12.2374 19.5303 12.5303L16.0303 16.0303C15.7374 16.3232 15.2626 16.3232 14.9697 16.0303C14.6768 15.7374 14.6768 15.2626 14.9697 14.9697L17.9393 12L14.9697 9.03033C14.6768 8.73744 14.6768 8.26256 14.9697 7.96967Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 844 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5014 6C15.743 6 17.9984 7.12567 17.9987 8.13923C17.9987 8.13934 17.9988 8.13943 17.9989 8.13943C17.999 8.13943 17.9991 8.13952 17.9991 8.13964C17.9988 9.14934 15.7433 10.275 12.5014 10.275C9.25943 10.275 7 9.14915 7 8.13943C7 7.12582 9.25931 6 12.5014 6ZM12.5014 12.7477C9.25943 12.7477 7 11.6218 7 10.6121V9.96112C7 9.79071 7.20349 9.69343 7.34531 9.78791C8.45886 10.5297 10.3128 10.9991 12.5014 10.9991C14.6854 10.9991 16.5414 10.5301 17.6528 9.78881C17.7948 9.69415 17.9987 9.79147 17.9987 9.96206V10.6121C17.9987 11.6219 15.7436 12.7477 12.5014 12.7477ZM7 13.1043C7 14.114 9.25943 15.2398 12.5014 15.2398C14.6757 15.2398 16.405 14.733 17.3106 14.0947C17.4111 14.002 17.5194 13.9169 17.6277 13.8317L17.6278 13.8316C17.8678 13.5957 17.9993 13.3441 17.9993 13.1043V12.4328C17.9993 12.2625 17.7961 12.1653 17.6545 12.2598C16.5321 13.0082 14.6611 13.4911 12.502 13.4911C10.3423 13.4911 8.46807 13.0081 7.34514 12.2596C7.20349 12.1651 7.00042 12.2623 7.00033 12.4325L7 13.1043ZM12.5014 17.732C9.25943 17.732 7 16.6061 7 15.5964L7.00033 14.9246C7.00042 14.7544 7.20349 14.6573 7.34513 14.7517C8.46807 15.5002 10.3423 15.9832 12.502 15.9832C14.6611 15.9832 16.5321 15.5003 17.6545 14.7519C17.7961 14.6574 17.9993 14.7546 17.9993 14.9249V15.5964C17.9993 15.8362 17.8678 16.0878 17.6278 16.3237L17.6277 16.3238C17.5194 16.409 17.4111 16.4941 17.3106 16.5869C16.405 17.2251 14.6757 17.732 12.5014 17.732Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.33435 5.38869C5.25328 5.47796 5.19794 5.58755 5.17422 5.70578C5.14355 5.85845 4.94155 8.95378 7.82755 11.8391C10.5707 14.5804 12.3725 14.7081 13.5361 14.6171C13.6102 14.6113 13.6833 14.6375 13.7358 14.69L17.8461 18.8003C17.9437 18.898 18.102 18.898 18.1997 18.8003L18.7888 18.2112C18.8864 18.1136 18.8864 17.9553 18.7888 17.8577L13.6137 12.6826C13.516 12.5849 13.516 12.4266 13.6137 12.329L14.3174 11.6252C14.4151 11.5276 14.5734 11.5276 14.671 11.6252L15.3562 12.3104C15.4812 12.4354 15.6508 12.5056 15.8276 12.5056C16.0043 12.5056 16.1739 12.4354 16.2989 12.3104L19.9268 8.68256C20.0244 8.58493 20.0244 8.42663 19.9268 8.329L19.3377 7.73989C19.24 7.64226 19.0817 7.64226 18.9841 7.73989L16.0043 10.7197C15.9067 10.8173 15.7484 10.8173 15.6508 10.7197L15.6137 10.6826C15.516 10.5849 15.516 10.4266 15.6137 10.329L18.5934 7.34922C18.6911 7.25159 18.6911 7.0933 18.5934 6.99567L18.0043 6.40656C17.9067 6.30893 17.7484 6.30893 17.6508 6.40656L14.671 9.38634C14.5734 9.48397 14.4151 9.48397 14.3174 9.38634L14.2803 9.34922C14.1827 9.25159 14.1827 9.0933 14.2803 8.99567L17.2601 6.01589C17.3577 5.91826 17.3577 5.75997 17.2601 5.66234L16.671 5.07322C16.5734 4.97559 16.4151 4.97559 16.3174 5.07322L12.6896 8.70111C12.5646 8.82613 12.4944 8.99567 12.4944 9.17245C12.4944 9.34922 12.5646 9.51876 12.6896 9.64378L13.3748 10.329C13.4724 10.4266 13.4724 10.5849 13.3748 10.6826L12.671 11.3863C12.5734 11.4839 12.4151 11.4839 12.3174 11.3863L6.29889 5.36511C6.21355 5.27991 6.1067 5.21946 5.98972 5.19018C5.87274 5.1609 5.75001 5.1639 5.6346 5.19886C5.51919 5.23382 5.41542 5.29943 5.33435 5.38869ZM6.98405 18.605C7.08168 18.7026 7.23997 18.7026 7.3376 18.605L10.2649 15.6777C10.39 15.5526 10.3492 15.3404 10.1898 15.2638C9.94189 15.1448 9.69988 15.0139 9.46457 14.8716C9.36429 14.8109 9.23517 14.8247 9.15226 14.9076L6.39509 17.6623C6.29739 17.76 6.29735 17.9183 6.39501 18.016L6.98405 18.605Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 12.5C7 9.80047 8.31653 6.59643 10.7399 5.40704C11.271 5.14637 11.8684 5 12.5 5C13.1316 5 13.729 5.14637 14.2601 5.40705C16.6835 6.59643 18 9.80047 18 12.5C18 15.5376 15.5376 18 12.5 18C9.46243 18 7 15.5376 7 12.5Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 428 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 18.2705H7V12.7705V6.77048C7 6.52606 7.17671 6.31747 7.4178 6.27729L8.49856 6.09716C9.84606 5.87258 11.2297 6.03924 12.4854 6.57737C13.5205 7.02102 14.6578 7.17143 15.7727 7.01216L17.4293 6.77551C17.5728 6.75501 17.7181 6.79779 17.8276 6.89276C17.9371 6.98773 18 7.12554 18 7.27048V13.2705C18 13.5226 17.8122 13.7353 17.562 13.7666L15.0917 14.0754C14.0044 14.2113 12.9004 14.0692 11.883 13.6622C11.2361 13.4034 10.5457 13.2705 9.849 13.2705L8 13.2705V18.2705Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 633 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6 8C6 6.89543 6.89539 6 8 6H10.5C11.1531 6 11.7087 6.4174 11.9147 7H17C18.1046 7 19 7.89543 19 9V9.5H6V8ZM6 10.5V15C6 16.1046 6.89539 17 8 17H17C18.1046 17 19 16.1046 19 15V10.5H6Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 393 B

View File

@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.5 7C9.22386 7 9 7.22386 9 7.5H16C16 7.22386 15.7761 7 15.5 7H9.5Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M7 11C7 10.4477 7.44772 10 8 10H17C17.5523 10 18 10.4477 18 11V16C18 16.5523 17.5523 17 17 17H8C7.44771 17 7 16.5523 7 16V11Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M8.75 8C8.33579 8 8 8.33579 8 8.75V9.5H17V8.75C17 8.33579 16.6642 8 16.25 8H8.75Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 584 B

View File

@@ -0,0 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 6.5C6 6.22386 6.22386 6 6.5 6H10.5C10.7761 6 11 6.22386 11 6.5V10.5C11 10.7761 10.7761 11 10.5 11H6.5C6.22386 11 6 10.7761 6 10.5V6.5Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M6 13.5C6 13.2239 6.22386 13 6.5 13H10.5C10.7761 13 11 13.2239 11 13.5V17.5C11 17.7761 10.7761 18 10.5 18H6.5C6.22386 18 6 17.7761 6 17.5V13.5Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M13 6.5C13 6.22386 13.2239 6 13.5 6H17.5C17.7761 6 18 6.22386 18 6.5V10.5C18 10.7761 17.7761 11 17.5 11H13.5C13.2239 11 13 10.7761 13 10.5V6.5Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M13 13.5C13 13.2239 13.2239 13 13.5 13H17.5C17.7761 13 18 13.2239 18 13.5V17.5C18 17.7761 17.7761 18 17.5 18H13.5C13.2239 18 13 17.7761 13 17.5V13.5Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 951 B

View File

@@ -0,0 +1,11 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 7.5C10 8.32843 9.32843 9 8.5 9C7.67157 9 7 8.32843 7 7.5C7 6.67157 7.67157 6 8.5 6C9.32843 6 10 6.67157 10 7.5Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M14 7.5C14 8.32843 13.3284 9 12.5 9C11.6716 9 11 8.32843 11 7.5C11 6.67157 11.6716 6 12.5 6C13.3284 6 14 6.67157 14 7.5Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M18 7.5C18 8.32843 17.3284 9 16.5 9C15.6716 9 15 8.32843 15 7.5C15 6.67157 15.6716 6 16.5 6C17.3284 6 18 6.67157 18 7.5Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M10 11.5C10 12.3284 9.32843 13 8.5 13C7.67157 13 7 12.3284 7 11.5C7 10.6716 7.67157 10 8.5 10C9.32843 10 10 10.6716 10 11.5Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M10 15.5C10 16.3284 9.32843 17 8.5 17C7.67157 17 7 16.3284 7 15.5C7 14.6716 7.67157 14 8.5 14C9.32843 14 10 14.6716 10 15.5Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M14 11.5C14 12.3284 13.3284 13 12.5 13C11.6716 13 11 12.3284 11 11.5C11 10.6716 11.6716 10 12.5 10C13.3284 10 14 10.6716 14 11.5Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M14 15.5C14 16.3284 13.3284 17 12.5 17C11.6716 17 11 16.3284 11 15.5C11 14.6716 11.6716 14 12.5 14C13.3284 14 14 14.6716 14 15.5Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M18 11.5C18 12.3284 17.3284 13 16.5 13C15.6716 13 15 12.3284 15 11.5C15 10.6716 15.6716 10 16.5 10C17.3284 10 18 10.6716 18 11.5Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M18 15.5C18 16.3284 17.3284 17 16.5 17C15.6716 17 15 16.3284 15 15.5C15 14.6716 15.6716 14 16.5 14C17.3284 14 18 14.6716 18 15.5Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.625 6.75C14.1268 6.74863 13.6342 6.85514 13.1811 7.06222C12.728 7.2693 12.325 7.57204 12 7.94963C11.675 7.57204 11.2721 7.2693 10.8189 7.06222C10.3658 6.85514 9.87322 6.74863 9.37502 6.75C8.68778 6.75107 8.01607 6.95442 7.44365 7.33472C6.87123 7.71501 6.42343 8.2554 6.1561 8.88851C5.88877 9.52161 5.81373 10.2194 5.94036 10.8949C6.06698 11.5703 6.38965 12.1936 6.86814 12.6869L11.6876 17.6123C11.7284 17.6538 11.777 17.6868 11.8306 17.7093C11.8842 17.7319 11.9418 17.7435 12 17.7435C12.0584 17.7435 12.1162 17.7318 12.17 17.7091C12.2238 17.6864 12.2725 17.6532 12.3133 17.6114L16.9639 12.8391C17.491 12.3672 17.8621 11.7462 18.0282 11.0584C18.1942 10.3707 18.1473 9.64872 17.8936 8.98827C17.6399 8.32782 17.1915 7.76008 16.6077 7.36034C16.024 6.9606 15.3325 6.74775 14.625 6.75Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 955 B

View File

@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 8.5C9 8.22386 9.22386 8 9.5 8H15.5C15.7761 8 16 8.22386 16 8.5C16 8.77614 15.7761 9 15.5 9H9.5C9.22386 9 9 8.77614 9 8.5Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M9 9.5C8.72386 9.5 8.5 9.72386 8.5 10C8.5 10.2761 8.72386 10.5 9 10.5H16C16.2761 10.5 16.5 10.2761 16.5 10C16.5 9.72386 16.2761 9.5 16 9.5H9Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.03144 11.3251L6 11.4037V14.5C6 15.8807 7.11929 17 8.5 17H16.5C17.8807 17 19 15.8807 19 14.5V11.4266L18.9821 11.3671C18.9801 11.3597 18.9779 11.3524 18.9756 11.3452L17.6927 7.06898C17.5024 6.4345 16.9184 6 16.256 6H9.17703C8.56368 6 8.01211 6.37343 7.78432 6.94291L6.03997 11.3038C6.03697 11.3108 6.03412 11.3179 6.03144 11.3251ZM8.71279 7.3143C8.78873 7.12448 8.97258 7 9.17703 7H16.256C16.4768 7 16.6714 7.14483 16.7349 7.35633L17.768 11.9946H14.19C13.7672 11.9946 13.4648 12.3302 13.4299 12.6963C13.4008 13.0004 13.3101 13.3496 13.1402 13.6075C12.9842 13.8442 12.7737 13.9946 12.44 13.9946C12.1063 13.9946 11.8958 13.8442 11.7399 13.6075C11.57 13.3496 11.4792 13.0004 11.4502 12.6963C11.4152 12.3302 11.1129 11.9946 10.69 11.9946H7.17854L8.71279 7.3143Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.12165 14.4062L7.23707 14.9535C6.90872 15.1566 6.92477 15.6395 7.26589 15.8204L11.9185 18.2878C12.065 18.3655 12.2405 18.3655 12.387 18.2878L17.0396 15.8204C17.3808 15.6395 17.3968 15.1566 17.0684 14.9535L16.1838 14.4061L12.8555 16.1713C12.416 16.4044 11.8895 16.4044 11.4499 16.1713L8.12165 14.4062Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M7.26575 9.82038C6.92462 9.63947 6.90857 9.15662 7.23693 8.95346L11.8895 6.0748C12.0507 5.97506 12.2545 5.97507 12.4157 6.0748L17.0683 8.95346C17.3967 9.15662 17.3806 9.63947 17.0395 9.82038L12.3869 12.2878C12.2404 12.3655 12.0649 12.3655 11.9184 12.2878L7.26575 9.82038Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M8.12161 11.4062L7.23703 11.9535C6.90867 12.1566 6.92472 12.6395 7.26584 12.8204L11.9185 15.2878C12.065 15.3655 12.2405 15.3655 12.387 15.2878L17.0396 12.8204C17.3807 12.6395 17.3968 12.1566 17.0684 11.9535L16.1838 11.4061L12.8555 13.1712C12.4159 13.4043 11.8894 13.4043 11.4499 13.1712L8.12161 11.4062Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.239 9.5199H13.289L14.489 5.9949C14.714 5.2449 13.814 4.6449 13.214 5.2449L7.214 11.2449C6.764 11.6949 7.064 12.5199 7.739 12.5199H11.689L9.489 17.0449C9.264 17.7949 10.164 18.3949 10.764 17.7949L16.764 10.7949C17.214 10.3449 16.914 9.5199 16.239 9.5199Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 429 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.5 7C6.39543 7 5.5 7.89543 5.5 9V15C5.5 16.1046 6.39543 17 7.5 17H16.5C17.6046 17 18.5 16.1046 18.5 15V9C18.5 7.89543 17.6046 7 16.5 7H7.5ZM8.32926 8.62372C8.12144 8.44188 7.80556 8.46294 7.62372 8.67075C7.44188 8.87857 7.46294 9.19445 7.67075 9.3763L11.6708 12.8763C11.8593 13.0412 12.1407 13.0412 12.3293 12.8763L16.3293 9.3763C16.5371 9.19445 16.5581 8.87857 16.3763 8.67075C16.1945 8.46294 15.8786 8.44188 15.6708 8.62372L12 11.8356L8.32926 8.62372Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 667 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.5 16.4375L6.72361 17.8257C6.39116 17.9919 6 17.7502 6 17.3785V8.9375C6 8.78012 6.0741 8.63193 6.2 8.5375L9.5 6.0625V16.4375ZM10.5 16.4375L13.5 17.9375V7.875L10.5 6V16.4375ZM14.5 7.85417V17.875L17.765 15.8344C17.9112 15.743 18 15.5828 18 15.4104V6.99652C18 6.80713 17.893 6.634 17.7236 6.5493L17.2595 6.31726C17.099 6.23702 16.9079 6.24892 16.7586 6.34845L14.5 7.85417Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 583 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.3584 13.2143C17.4293 13.9316 16.2645 14.3584 15 14.3584C11.9624 14.3584 9.5 11.8959 9.5 8.85836C9.5 7.59385 9.92673 6.42902 10.644 5.5C7.70505 6.12369 5.5 8.73352 5.5 11.8584C5.5 15.4482 8.41015 18.3584 12 18.3584C15.1248 18.3584 17.7347 16.1533 18.3584 13.2143Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 478 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.9984 14.669V5.53883C17.9984 5.36682 17.9174 5.20515 17.7808 5.10382C17.6433 5.00202 17.4672 4.97284 17.3068 5.02626L10.3692 7.29966C10.1508 7.37112 10.0023 7.5784 10.0023 7.81223V14.4569C10.0023 14.8509 9.68804 15.1711 9.30041 15.1711H8.52806C7.13186 15.1711 6 16.028 6 17.0853C6 18.1426 7.13186 19 8.52817 19C9.92438 19 11.0562 18.1426 11.0562 17.0853V17.0845L11.0606 17.0853V9.64029L16.9402 7.71378V12.0806C16.9309 12.4666 16.622 12.7774 16.2401 12.7774H15.4722C14.0759 12.7774 12.9441 13.6344 12.9441 14.6917C12.9441 15.7494 14.0757 16.6059 15.4722 16.6059C16.8685 16.6059 18 15.7494 18 14.6917C17.9998 14.6839 17.9986 14.6765 17.9985 14.669L17.9984 14.669Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 836 B

View File

@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 6H10C8.89543 6 8 6.89543 8 8V16C8 17.1046 8.89543 18 10 18H15C16.1046 18 17 17.1046 17 16V11H14C12.8954 11 12 10.1046 12 9V6Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M13 6L17 10H14C13.4477 10 13 9.55228 13 9V6Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 414 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.5 7.5C5.015 9.985 5.015 14.015 7.5 16.5C9.985 18.985 14.015 18.985 16.5 16.5C16.915 16.085 16.915 15.415 16.5 15C16.305 14.805 16.055 14.705 15.8 14.69C15.555 14.675 15.305 14.575 15.115 14.385C14.7 13.97 14.7 13.3 15.115 12.885L16 12C17.38 10.62 17.38 8.38 16 7C13.79 4.79 9.985 5.015 7.5 7.5ZM9.25 14.75C8.835 15.165 8.165 15.165 7.75 14.75C7.335 14.335 7.335 13.665 7.75 13.25C8.165 12.835 8.835 12.835 9.25 13.25C9.665 13.665 9.665 14.335 9.25 14.75ZM8.75 11.25C8.335 11.665 7.665 11.665 7.25 11.25C6.835 10.835 6.835 10.165 7.25 9.75C7.665 9.335 8.335 9.335 8.75 9.75C9.165 10.165 9.165 10.835 8.75 11.25ZM11.25 8.75C10.835 9.165 10.165 9.165 9.75 8.75C9.335 8.335 9.335 7.665 9.75 7.25C10.165 6.835 10.835 6.835 11.25 7.25C11.665 7.665 11.665 8.335 11.25 8.75ZM14.75 9.25C14.335 9.665 13.665 9.665 13.25 9.25C12.835 8.835 12.835 8.165 13.25 7.75C13.665 7.335 14.335 7.335 14.75 7.75C15.165 8.165 15.165 8.835 14.75 9.25Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,7 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.56759 9.4658C9.67409 9.48937 9.78284 9.50131 9.89192 9.50139C10.1981 9.50143 10.4963 9.4042 10.7436 9.22372C10.9314 9.0832 11.0875 8.90472 11.2017 8.6999C11.316 8.49507 11.3859 8.26849 11.4068 8.03489C11.462 7.59511 11.3558 7.15026 11.1079 6.78286C10.86 6.41545 10.4872 6.15044 10.0588 6.03697C9.85687 5.99077 9.64742 5.98842 9.44455 6.03009C9.24167 6.07175 9.0501 6.15645 8.88276 6.27847C8.69497 6.419 8.53887 6.59747 8.4246 6.8023C8.31033 7.00712 8.24045 7.2337 8.21951 7.4673C8.1642 7.90717 8.27036 8.35216 8.51828 8.71969C8.76619 9.08722 9.13903 9.35233 9.56759 9.4658Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M7.98106 11.6755C8.09901 11.4622 8.17327 11.2276 8.19951 10.9853C8.22575 10.743 8.20345 10.4979 8.13389 10.2644C8.03119 9.83298 7.76691 9.45742 7.39553 9.21507C7.02415 8.97273 6.57396 8.88208 6.13773 8.9618C5.94688 9.0112 5.76799 9.09867 5.61181 9.21896C5.45563 9.33926 5.32538 9.48989 5.22889 9.6618C5.1111 9.8749 5.03692 10.1093 5.01068 10.3514C4.98444 10.5935 5.00667 10.8383 5.07606 11.0717C5.17022 11.443 5.38111 11.7742 5.67768 12.0166C5.97425 12.259 6.34082 12.3997 6.72339 12.418C6.8414 12.4182 6.95896 12.4037 7.07339 12.3749C7.26398 12.3254 7.44261 12.238 7.59858 12.1178C7.75455 11.9976 7.88464 11.8471 7.98106 11.6755Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M13.2578 9.22431C13.5051 9.40458 13.8033 9.5016 14.1094 9.50139C14.2185 9.50112 14.3273 9.48899 14.4338 9.46523C14.8623 9.35175 15.2352 9.08664 15.4831 8.71911C15.731 8.35158 15.8371 7.90659 15.7818 7.46673C15.7609 7.23312 15.691 7.00654 15.5767 6.80172C15.4625 6.59689 15.3064 6.41842 15.1186 6.27789C14.9512 6.15594 14.7596 6.07128 14.5568 6.02962C14.3539 5.98796 14.1445 5.99027 13.9426 6.03639C13.514 6.14987 13.1412 6.41497 12.8933 6.7825C12.6454 7.15003 12.5392 7.59503 12.5945 8.03489C12.6154 8.2686 12.6852 8.49529 12.7995 8.70022C12.9138 8.90515 13.0699 9.08372 13.2578 9.22431Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M18.7727 9.66063C18.6762 9.48872 18.5459 9.33809 18.3898 9.2178C18.2336 9.09751 18.0547 9.01003 17.8638 8.96063C17.4275 8.88003 16.9769 8.97032 16.6053 9.2128C16.2337 9.45528 15.9696 9.83137 15.8677 10.2632C15.7981 10.4968 15.7758 10.7419 15.802 10.9841C15.8283 11.2264 15.9026 11.461 16.0205 11.6743C16.117 11.8462 16.2472 11.9968 16.4034 12.1171C16.5596 12.2374 16.7385 12.3249 16.9293 12.3743C17.0438 12.4031 17.1613 12.4176 17.2793 12.4175C17.6619 12.3992 18.0285 12.2585 18.3252 12.0163C18.6218 11.774 18.8329 11.4429 18.9272 11.0717C18.9965 10.838 19.0186 10.5929 18.992 10.3506C18.9655 10.1083 18.8909 9.87377 18.7727 9.66063Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M12.0009 11.6681C10.501 11.6681 7.33419 13.8846 7.33419 16.5014C7.33419 17.1202 7.58002 17.7137 8.01761 18.1513C8.45519 18.5889 9.04868 18.8347 9.66752 18.8347C10.1661 18.8444 10.6584 18.7227 11.0949 18.4818C11.3692 18.3211 11.6832 18.2413 12.0009 18.2514C12.3185 18.2413 12.6326 18.3211 12.9068 18.4818C13.3433 18.7227 13.8356 18.8444 14.3342 18.8347C14.953 18.8347 15.5465 18.5889 15.9841 18.1513C16.4217 17.7137 16.6675 17.1202 16.6675 16.5014C16.6675 13.8846 13.5007 11.6681 12.0009 11.6681Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -0,0 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.5 12C15.8807 12 17 10.8807 17 9.5C17 8.11929 15.8807 7 14.5 7C13.1193 7 12 8.11929 12 9.5C12 10.8807 13.1193 12 14.5 12Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M9.23464 16.1128C9.19966 16.2188 9.16839 16.3266 9.14097 16.4363C9.06939 16.7226 9.28595 17 9.58108 17H18.419C18.7142 17 18.9307 16.7226 18.8591 16.4363C18.3542 14.4168 16.5397 13 14.458 13H13.5421C12.6587 13 11.8234 13.2552 11.1158 13.7033C11.094 13.7171 11.0722 13.7311 11.0506 13.7454C10.2129 14.2962 9.56194 15.1209 9.23464 16.1128Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M10.7991 9.94361C10.7991 11.0793 9.87838 12 8.74267 12C7.60696 12 6.68628 11.0793 6.68628 9.94361C6.68628 8.8079 7.60696 7.88722 8.74267 7.88722C9.87838 7.88722 10.7991 8.8079 10.7991 9.94361Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M8.7081 12.8226C9.23097 12.8226 9.73334 12.9312 10.1908 13.1295C9.23552 13.856 8.51652 14.8915 8.19172 16.1128H4.69659C4.45382 16.1128 4.27569 15.8846 4.33457 15.6491C4.74986 13.9879 6.24244 12.8226 7.95476 12.8226H8.7081Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.39313 8.8693C7.00893 9.09743 6.49922 8.98436 6.28679 8.59126C6.11992 8.28248 6.19189 7.89584 6.47741 7.69173C9.6339 5.43522 14.3475 5.43609 17.5229 7.69434C17.8072 7.89654 17.878 8.28162 17.7096 8.58715C17.4984 8.97031 16.9995 9.07869 16.6232 8.85554C15.2062 8.0153 13.6032 7.61096 11.9999 7.61096C10.4029 7.61096 8.80594 8.03041 7.39313 8.8693ZM7.35627 10.5745C7.1146 10.136 7.2317 9.58032 7.66255 9.32529C10.3286 7.7472 13.6712 7.7472 16.3373 9.32529C16.7681 9.58032 16.8852 10.136 16.6435 10.5745L12.8757 17.411C12.4955 18.1007 11.5043 18.1007 11.1241 17.411L7.35627 10.5745ZM10.5 10.75C10.5 11.1642 10.1642 11.5 9.74995 11.5C9.33574 11.5 8.99995 11.1642 8.99995 10.75C8.99995 10.3358 9.33574 10 9.74995 10C10.1642 10 10.5 10.3358 10.5 10.75ZM13.75 11.5C14.1642 11.5 14.5 11.1642 14.5 10.75C14.5 10.3358 14.1642 10 13.75 10C13.3357 10 13 10.3358 13 10.75C13 11.1642 13.3357 11.5 13.75 11.5ZM11.75 14.5C12.1642 14.5 12.5 14.1642 12.5 13.75C12.5 13.3358 12.1642 13 11.75 13C11.3357 13 11 13.3358 11 13.75C11 14.1642 11.3357 14.5 11.75 14.5Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.63436 8.12156C8.90586 8.17195 9.16681 7.9927 9.2172 7.72119C9.26759 7.44969 9.08834 7.18874 8.81683 7.13835C8.02084 6.99062 7.28668 6.95417 6.6777 7.06327C6.07162 7.17184 5.51916 7.44015 5.2167 7.96402C4.92002 8.47789 4.95665 9.07751 5.1552 9.64609C5.35443 10.2166 5.73653 10.8212 6.24189 11.424C7.25538 12.6329 8.83777 13.9209 10.7438 15.0213L10.769 15.0359L10.7957 15.0474C12.8175 15.9196 14.1893 16.4064 15.2164 16.5517C15.7398 16.6257 16.1928 16.6143 16.6083 16.5103C17.026 16.4059 17.3764 16.2151 17.7024 15.9704C18.637 15.269 18.6829 13.9846 18.1563 13.0912C18.0161 12.8533 17.7095 12.7741 17.4716 12.9144C17.2338 13.0546 17.1546 13.3611 17.2948 13.599C17.6323 14.1716 17.5284 14.8506 17.1021 15.1707C16.8554 15.3558 16.6244 15.4755 16.3656 15.5402C16.1246 15.6005 15.8345 15.6194 15.4532 15.5741C16.1907 14.703 16.6355 13.576 16.6355 12.3451C16.6355 9.58368 14.3969 7.34511 11.6355 7.34511C9.45947 7.34511 7.60812 8.73517 6.92096 10.6757C6.5082 10.1668 6.23494 9.70486 6.09929 9.31641C5.95363 8.8993 5.98614 8.63132 6.08273 8.46402C6.18115 8.29355 6.40381 8.12825 6.85404 8.0476C7.30137 7.96746 7.9047 7.98614 8.63436 8.12156Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M11.6355 17.3451C8.95554 17.3451 6.76804 15.2367 6.6413 12.5881C7.6542 13.5837 8.97957 14.5802 10.4937 15.4544L10.5442 15.4835L10.5977 15.5066C11.9557 16.0924 13.0461 16.5163 13.9495 16.7785C13.2576 17.1404 12.4705 17.3451 11.6355 17.3451Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.5986 7.13471C13.2645 7.30179 13 7.63081 13 8.1875V9.1875H12V8.1875C12 7.63081 11.7355 7.30179 11.4014 7.13471C11.0422 6.9551 10.5828 6.9551 10.2236 7.13471C10.0852 7.20392 10.0326 7.29307 10.0059 7.45157C9.99056 7.5427 9.98676 7.64887 9.9891 7.77756C9.9898 7.81561 9.99156 7.86712 9.99347 7.92294L9.99347 7.92297C9.99655 8.01267 10 8.1135 10 8.1875C10 8.45131 10.013 8.65333 10.1404 8.81265C10.2535 8.95398 10.568 9.18673 11.4954 9.1875H12V13.1875H7.66636C7.61551 13.1609 7.56117 13.1401 7.50506 13.1258C6.63988 12.9055 6 12.1212 6 11.1875C6 10.0829 6.89543 9.1875 8 9.1875H9.19731C8.99899 8.80459 8.99964 8.41326 8.99997 8.21059L9 8.1875C9 8.11744 8.99783 8.06123 8.99532 7.99616C8.99317 7.94055 8.99077 7.87848 8.98927 7.79575C8.98654 7.64574 8.98916 7.46745 9.01976 7.2856C9.08497 6.89816 9.28236 6.4873 9.77639 6.24029C10.4172 5.9199 11.2078 5.91991 11.8486 6.24029C12.0958 6.36388 12.3181 6.53406 12.5 6.74747C12.6819 6.53406 12.9042 6.36388 13.1514 6.24029C13.7922 5.91991 14.5828 5.9199 15.2236 6.24029C15.7176 6.4873 15.915 6.89816 15.9802 7.2856C16.0108 7.46745 16.0135 7.64574 16.0107 7.79575C16.0092 7.87846 16.0068 7.94053 16.0047 7.99614V7.99615V7.99616V7.9962V7.99622C16.0022 8.06126 16 8.11746 16 8.1875L16 8.21059C16.0004 8.41326 16.001 8.80459 15.8027 9.1875H17C18.1046 9.1875 19 10.0829 19 11.1875C19 12.1212 18.3601 12.9055 17.4949 13.1258C17.4388 13.1401 17.3845 13.1609 17.3336 13.1875H13V9.1875H13.5046C14.432 9.18673 14.7465 8.95398 14.8596 8.81265C14.987 8.65333 15 8.45131 15 8.1875C15 8.11352 15.0035 8.01273 15.0065 7.92305L15.0065 7.923V7.92297C15.0084 7.86714 15.0102 7.81562 15.0109 7.77756C15.0132 7.64887 15.0094 7.5427 14.9941 7.45157C14.9674 7.29307 14.9148 7.20392 14.7764 7.13471C14.4172 6.9551 13.9578 6.9551 13.5986 7.13471ZM8 14.1875L8 16.1875C8 17.2921 8.89543 18.1875 10 18.1875H12V14.1875H8ZM13 18.1875H15C16.1046 18.1875 17 17.2921 17 16.1875V14.1875H13V18.1875Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.0294 13.8704L10.1257 6.13303C10.2172 5.96013 10.4629 5.95462 10.5621 6.12325L15.1135 13.8606C15.2115 14.0273 15.0914 14.2374 14.898 14.2374H6.25034C6.06195 14.2374 5.94125 14.0369 6.0294 13.8704Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M10.0912 15.2374C10.709 16.9852 12.3759 18.2374 14.3351 18.2374C16.8203 18.2374 18.8351 16.2227 18.8351 13.7374C18.8351 11.2521 16.8203 9.2374 14.3351 9.2374C14.0813 9.2374 13.8324 9.25839 13.5901 9.29877L15.9753 13.3536C16.4655 14.1869 15.8647 15.2374 14.8979 15.2374H10.0912Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 716 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.7909 7.41919C15.3465 6.9748 14.8272 6.62495 14.2509 6.38057C13.6524 6.12787 13.0179 6.0001 12.3638 6.0001L10.8461 6C10.1921 6 9.55752 6.12776 8.95905 6.38047C8.38138 6.62484 7.86346 6.97334 7.4191 7.4191C6.9747 7.86349 6.62486 8.38274 6.38047 8.95905C6.12777 9.55755 6 10.1921 6 10.8461C6 10.8573 6 10.8683 6.00136 10.8795L6.28738 14.6135C6.29706 15.2897 6.84975 15.8354 7.52737 15.8354H7.93834C8.14804 15.8354 8.31881 16.0049 8.31881 16.2159V17.7586C8.31881 18.4432 8.87419 18.9986 9.5588 18.9986H9.66157C9.95872 18.9986 10.2003 18.757 10.2003 18.4598V18.139C10.2003 17.9043 10.3836 17.7058 10.6182 17.7003C10.8613 17.6933 11.0612 17.8891 11.0612 18.1307V18.4598C11.0612 18.757 11.3027 18.9986 11.5999 18.9986H11.6111C11.9082 18.9986 12.1498 18.757 12.1498 18.4598V18.1405C12.1498 17.9058 12.3331 17.7072 12.5677 17.7017C12.8108 17.6947 13.0107 17.8906 13.0107 18.1321V18.4613C13.0107 18.7584 13.2522 19 13.5494 19H13.6522C14.3368 19 14.8922 18.4446 14.8922 17.76L14.8925 16.2158C14.8925 16.0061 15.0619 15.8353 15.2729 15.8353H15.6839C16.3615 15.8353 16.9142 15.2883 16.9239 14.6134L17.2099 10.8793C17.2113 10.8682 17.2113 10.8572 17.2113 10.846C17.2113 10.192 17.0835 9.5574 16.8308 8.95892C16.5852 8.38281 16.2352 7.8648 15.7909 7.41914L15.7909 7.41919ZM9.49642 14.2259C8.66189 14.2259 7.98707 13.5497 7.98707 12.7166C7.98707 11.882 8.66333 11.2072 9.49642 11.2072C10.331 11.2072 11.0058 11.8835 11.0058 12.7166C11.0071 13.5497 10.331 14.2259 9.49642 14.2259ZM11.757 16.224H11.4515C11.1599 16.224 10.942 15.956 11.0016 15.67L11.1502 14.9577C11.1947 14.7438 11.3821 14.5911 11.6001 14.5911H11.6071C11.8251 14.5911 12.0126 14.7438 12.057 14.9577L12.2056 15.67C12.2666 15.956 12.0486 16.224 11.757 16.224ZM13.7121 14.2259C12.8776 14.2259 12.2028 13.5497 12.2028 12.7166C12.2028 11.882 12.879 11.2072 13.7121 11.2072C14.5453 11.2072 15.2215 11.8835 15.2215 12.7166C15.2215 13.5497 14.5452 14.2259 13.7121 14.2259Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="6.5" y="6.5" width="11" height="11" rx="2" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 213 B

View File

@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 9.5C8 8.67157 8.67157 8 9.5 8H16.5C17.3284 8 18 8.67157 18 9.5V16.5C18 17.3284 17.3284 18 16.5 18H9.5C8.67157 18 8 17.3284 8 16.5V9.5Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M7.5 6C6.67157 6 6 6.67157 6 7.5V14.5C6 15.1531 6.4174 15.7087 7 15.9146V9.5C7 8.11929 8.11929 7 9.5 7H15.9146C15.7087 6.4174 15.1531 6 14.5 6H7.5Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 525 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.1053 10.25C13.8926 10.25 13.7031 10.1153 13.6332 9.91441L12.4722 6.58014C12.3164 6.1327 11.6836 6.13269 11.5278 6.58014L10.3668 9.91441C10.2969 10.1153 10.1074 10.25 9.89465 10.25H6.44986C5.97457 10.25 5.7674 10.8507 6.14165 11.1437L9.00336 13.3839C9.16873 13.5134 9.23558 13.7324 9.17069 13.9321L8.07245 17.3125C7.9228 17.7732 8.45003 18.1562 8.84188 17.8715L11.7061 15.7905C11.8814 15.6632 12.1186 15.6632 12.2939 15.7905L15.1581 17.8715C15.55 18.1562 16.0772 17.7732 15.9275 17.3125L14.8293 13.9321C14.7644 13.7324 14.8313 13.5134 14.9966 13.3839L17.8583 11.1437C18.2326 10.8507 18.0254 10.25 17.5501 10.25H14.1053Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 794 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.25 7C10.25 6.30964 10.8097 5.75 11.5 5.75C12.1904 5.75 12.75 6.30964 12.75 7V9.74469L14.8375 8.44C15.4229 8.07411 16.1941 8.25208 16.56 8.8375C16.9259 9.42292 16.7479 10.1941 16.1625 10.56L13.8585 12L16.1625 13.44C16.7479 13.8059 16.9259 14.5771 16.56 15.1625C16.1941 15.7479 15.4229 15.9259 14.8375 15.56L12.75 14.2553V17C12.75 17.6904 12.1904 18.25 11.5 18.25C10.8097 18.25 10.25 17.6904 10.25 17V14.2553L8.16251 15.56C7.57709 15.9259 6.8059 15.7479 6.44002 15.1625C6.07413 14.5771 6.2521 13.8059 6.83752 13.44L9.14152 12L6.83752 10.56C6.2521 10.1941 6.07413 9.42292 6.44002 8.8375C6.8059 8.25208 7.57709 8.07411 8.16251 8.44L10.25 9.74469V7Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 860 B

View File

@@ -0,0 +1,11 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.5 5.75C12.5 5.47386 12.2761 5.25 12 5.25C11.7239 5.25 11.5 5.47386 11.5 5.75V7.68023C11.5 7.94603 11.7342 8.14943 12 8.14943C12.2658 8.14943 12.5 7.94602 12.5 7.6802V5.75Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M12.5 16.3198C12.5 16.054 12.2658 15.8506 12 15.8506C11.7342 15.8506 11.5 16.054 11.5 16.3198V18.25C11.5 18.5261 11.7239 18.75 12 18.75C12.2761 18.75 12.5 18.5261 12.5 18.25V16.3198Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M18.25 12.5C18.5261 12.5 18.75 12.2761 18.75 12C18.75 11.7239 18.5261 11.5 18.25 11.5H16.3198C16.054 11.5 15.8506 11.7342 15.8506 12C15.8506 12.2658 16.054 12.5 16.3198 12.5H18.25Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M7.6802 12.5C7.94602 12.5 8.14943 12.2658 8.14943 12C8.14943 11.7342 7.94603 11.5 7.68023 11.5H5.75C5.47386 11.5 5.25 11.7239 5.25 12C5.25 12.2761 5.47386 12.5 5.75 12.5H7.6802Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M16.773 7.93413C16.9682 7.73887 16.9682 7.42229 16.773 7.22703C16.5777 7.03177 16.2611 7.03177 16.0659 7.22703L14.701 8.59191C14.5131 8.77986 14.5348 9.08927 14.7228 9.27722C14.9107 9.46519 15.2202 9.48696 15.4081 9.29899L16.773 7.93413Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M9.29901 15.4081C9.48697 15.2201 9.4652 14.9107 9.27724 14.7228C9.08929 14.5348 8.77988 14.513 8.59192 14.701L7.22705 16.0659C7.03178 16.2611 7.03178 16.5777 7.22705 16.773C7.42231 16.9682 7.73889 16.9682 7.93415 16.773L9.29901 15.4081Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M16.0658 16.773C16.2611 16.9682 16.5776 16.9682 16.7729 16.773C16.9682 16.5777 16.9682 16.2611 16.7729 16.0659L15.408 14.701C15.2201 14.513 14.9107 14.5348 14.7227 14.7228C14.5348 14.9107 14.513 15.2202 14.7009 15.4081L16.0658 16.773Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M8.59183 9.299C8.77979 9.48696 9.08922 9.46519 9.27719 9.27723C9.46514 9.08927 9.48691 8.77986 9.29895 8.59191L7.93408 7.22703C7.73881 7.03177 7.42223 7.03177 7.22697 7.22703C7.03171 7.42229 7.03171 7.73888 7.22697 7.93414L8.59183 9.299Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M15 12.0956C15 13.7524 13.6569 15.0956 12 15.0956C10.3431 15.0956 9 13.7524 9 12.0956C9 10.4387 10.3431 9.09555 12 9.09555C13.6569 9.09555 15 10.4387 15 12.0956Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.5 6C6.39543 6 5.5 6.89543 5.5 8V16C5.5 17.1046 6.39543 18 7.5 18H16.5C17.6046 18 18.5 17.1046 18.5 16V8C18.5 6.89543 17.6046 6 16.5 6H7.5ZM8.79289 10L7.14645 8.35355C6.95118 8.15829 6.95118 7.84171 7.14645 7.64645C7.34171 7.45118 7.65829 7.45118 7.85355 7.64645L9.85355 9.64645C10.0488 9.84171 10.0488 10.1583 9.85355 10.3536L7.85355 12.3536C7.65829 12.5488 7.34171 12.5488 7.14645 12.3536C6.95118 12.1583 6.95118 11.8417 7.14645 11.6464L8.79289 10ZM10 11.5C10 11.2239 10.2239 11 10.5 11H12C12.2761 11 12.5 11.2239 12.5 11.5C12.5 11.7761 12.2761 12 12 12H10.5C10.2239 12 10 11.7761 10 11.5Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 805 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.7562 6.39701C12.6731 6.27487 12.5606 6.17473 12.4287 6.10548C12.2968 6.03623 12.1496 6 12 6C11.8504 6 11.7032 6.03623 11.5713 6.10548C11.4394 6.17473 11.3269 6.27487 11.2438 6.39701L5.11726 16.7108C5.04052 16.8413 5.00008 16.9894 5 17.1401C4.99992 17.2908 5.04021 17.4389 5.11681 17.5695C5.19342 17.7001 5.30366 17.8086 5.43646 17.8842C5.56926 17.9598 5.71997 17.9997 5.87345 18H18.1265C18.28 17.9997 18.4307 17.9598 18.5635 17.8842C18.6963 17.8086 18.8066 17.7001 18.8832 17.5695C18.9598 17.4389 19.0001 17.2908 19 17.1401C18.9999 16.9894 18.9595 16.8413 18.8827 16.7108L12.7562 6.39701Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 764 B

View File

@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15 10.1287V12.885C15 12.958 15.0319 13.0273 15.0873 13.0748L17.2619 14.9388C17.7484 15.3558 18.5 15.0101 18.5 14.3693V8.95739C18.5 8.34736 17.8105 7.99252 17.3141 8.34709L15.1047 9.92522C15.039 9.97215 15 10.0479 15 10.1287Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M5.5 10C5.5 8.89543 6.39543 8 7.5 8H12.5C13.6046 8 14.5 8.89543 14.5 10V14C14.5 15.1046 13.6046 16 12.5 16H7.5C6.39543 16 5.5 15.1046 5.5 14V10Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 610 B

View File

@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15 12.7718H9.39231V10.7718H15V12.7718Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M16 15.6641C16.5523 15.6641 17 15.2164 17 14.6641V14.2394C17 14.0072 17.3796 13.832 17.6118 13.832C18.0429 13.832 18.3923 13.4826 18.3923 13.0515V10.6126C18.3923 10.1815 18.0429 9.83204 17.6118 9.83204C17.3796 9.83204 17 9.65686 17 9.42471V9C17 8.44772 16.5523 8 16 8C15.4477 8 15 8.44772 15 9V14.6641C15 15.2164 15.4477 15.6641 16 15.6641Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M8.39231 8C7.84003 8 7.39231 8.44772 7.39231 9V9.42471C7.39231 9.65686 7.01273 9.83204 6.78058 9.83204C6.34948 9.83204 6 10.1815 6 10.6126L6 13.0515C6 13.4826 6.34948 13.832 6.78058 13.832C7.01273 13.832 7.39231 14.0072 7.39231 14.2394V14.6641C7.39231 15.2164 7.84003 15.6641 8.39231 15.6641C8.9446 15.6641 9.39231 15.2164 9.39231 14.6641V9C9.39231 8.44772 8.9446 8 8.39231 8Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -429,4 +429,50 @@
skin/classic/browser/zen-icons/zoom-out.svg (../shared/zen-icons/lin/zoom-out.svg)
#endif
skin/classic/browser/zen-icons/urlbar-arrow.svg (../shared/zen-icons/common/urlbar-arrow.svg)
skin/classic/browser/zen-icons/selectable/airplane.svg (../shared/zen-icons/common/selectable/airplane.svg)
skin/classic/browser/zen-icons/selectable/baseball.svg (../shared/zen-icons/common/selectable/baseball.svg)
skin/classic/browser/zen-icons/selectable/basket.svg (../shared/zen-icons/common/selectable/basket.svg)
skin/classic/browser/zen-icons/selectable/bed.svg (../shared/zen-icons/common/selectable/bed.svg)
skin/classic/browser/zen-icons/selectable/bell.svg (../shared/zen-icons/common/selectable/bell.svg)
skin/classic/browser/zen-icons/selectable/bookmark.svg (../shared/zen-icons/common/selectable/bookmark.svg)
skin/classic/browser/zen-icons/selectable/book.svg (../shared/zen-icons/common/selectable/book.svg)
skin/classic/browser/zen-icons/selectable/chat.svg (../shared/zen-icons/common/selectable/chat.svg)
skin/classic/browser/zen-icons/selectable/checkbox.svg (../shared/zen-icons/common/selectable/checkbox.svg)
skin/classic/browser/zen-icons/selectable/circle.svg (../shared/zen-icons/common/selectable/circle.svg)
skin/classic/browser/zen-icons/selectable/cloud.svg (../shared/zen-icons/common/selectable/cloud.svg)
skin/classic/browser/zen-icons/selectable/code.svg (../shared/zen-icons/common/selectable/code.svg)
skin/classic/browser/zen-icons/selectable/coins.svg (../shared/zen-icons/common/selectable/coins.svg)
skin/classic/browser/zen-icons/selectable/cutlery.svg (../shared/zen-icons/common/selectable/cutlery.svg)
skin/classic/browser/zen-icons/selectable/egg.svg (../shared/zen-icons/common/selectable/egg.svg)
skin/classic/browser/zen-icons/selectable/flag.svg (../shared/zen-icons/common/selectable/flag.svg)
skin/classic/browser/zen-icons/selectable/folder.svg (../shared/zen-icons/common/selectable/folder.svg)
skin/classic/browser/zen-icons/selectable/globe.svg (../shared/zen-icons/common/selectable/globe.svg)
skin/classic/browser/zen-icons/selectable/grid-2x2.svg (../shared/zen-icons/common/selectable/grid-2x2.svg)
skin/classic/browser/zen-icons/selectable/grid-3x3.svg (../shared/zen-icons/common/selectable/grid-3x3.svg)
skin/classic/browser/zen-icons/selectable/heart.svg (../shared/zen-icons/common/selectable/heart.svg)
skin/classic/browser/zen-icons/selectable/inbox.svg (../shared/zen-icons/common/selectable/inbox.svg)
skin/classic/browser/zen-icons/selectable/layers.svg (../shared/zen-icons/common/selectable/layers.svg)
skin/classic/browser/zen-icons/selectable/lightning.svg (../shared/zen-icons/common/selectable/lightning.svg)
skin/classic/browser/zen-icons/selectable/mail.svg (../shared/zen-icons/common/selectable/mail.svg)
skin/classic/browser/zen-icons/selectable/map.svg (../shared/zen-icons/common/selectable/map.svg)
skin/classic/browser/zen-icons/selectable/moon.svg (../shared/zen-icons/common/selectable/moon.svg)
skin/classic/browser/zen-icons/selectable/music.svg (../shared/zen-icons/common/selectable/music.svg)
skin/classic/browser/zen-icons/selectable/page.svg (../shared/zen-icons/common/selectable/page.svg)
skin/classic/browser/zen-icons/selectable/palette.svg (../shared/zen-icons/common/selectable/palette.svg)
skin/classic/browser/zen-icons/selectable/paw.svg (../shared/zen-icons/common/selectable/paw.svg)
skin/classic/browser/zen-icons/selectable/people.svg (../shared/zen-icons/common/selectable/people.svg)
skin/classic/browser/zen-icons/selectable/pizza.svg (../shared/zen-icons/common/selectable/pizza.svg)
skin/classic/browser/zen-icons/selectable/planet.svg (../shared/zen-icons/common/selectable/planet.svg)
skin/classic/browser/zen-icons/selectable/present.svg (../shared/zen-icons/common/selectable/present.svg)
skin/classic/browser/zen-icons/selectable/shapes.svg (../shared/zen-icons/common/selectable/shapes.svg)
skin/classic/browser/zen-icons/selectable/skull.svg (../shared/zen-icons/common/selectable/skull.svg)
skin/classic/browser/zen-icons/selectable/squares.svg (../shared/zen-icons/common/selectable/squares.svg)
skin/classic/browser/zen-icons/selectable/square.svg (../shared/zen-icons/common/selectable/square.svg)
skin/classic/browser/zen-icons/selectable/star-2.svg (../shared/zen-icons/common/selectable/star-2.svg)
skin/classic/browser/zen-icons/selectable/star.svg (../shared/zen-icons/common/selectable/star.svg)
skin/classic/browser/zen-icons/selectable/sun.svg (../shared/zen-icons/common/selectable/sun.svg)
skin/classic/browser/zen-icons/selectable/terminal.svg (../shared/zen-icons/common/selectable/terminal.svg)
skin/classic/browser/zen-icons/selectable/triangle.svg (../shared/zen-icons/common/selectable/triangle.svg)
skin/classic/browser/zen-icons/selectable/video.svg (../shared/zen-icons/common/selectable/video.svg)
skin/classic/browser/zen-icons/selectable/weight.svg (../shared/zen-icons/common/selectable/weight.svg)
skin/classic/browser/zen-icons/icons.css (../shared/zen-icons/icons.css)

View File

@@ -34,6 +34,12 @@ do_common_icons() {
echo "Working on $filename"
echo " skin/classic/browser/zen-icons/$filename (../shared/zen-icons/common/$filename) " >> jar.inc.mn
done
for filename in common/selectable/*.svg; do
# remove the os/ prefix
filename=$(basename $filename)
echo "Working on $filename"
echo " skin/classic/browser/zen-icons/selectable/$filename (../shared/zen-icons/common/selectable/$filename) " >> jar.inc.mn
done
}
do_icons lin WIN

View File

@@ -543,33 +543,33 @@ var gZenVerticalTabsManager = {
return this.__topButtonsSeparatorElement;
},
animateTab(aTab) {
animateItemOpen(aItem) {
if (
!gZenUIManager.motion ||
!aTab ||
!aItem ||
!gZenUIManager._hasLoadedDOM ||
!aTab.isConnected ||
!aItem.isConnected ||
gZenUIManager.testingEnabled ||
!gZenStartup.isReady
) {
return;
}
// get next visible tab
const isLastTab = () => {
const visibleTabs = gBrowser.visibleTabs;
return visibleTabs[visibleTabs.length - 1] === aTab;
const isLastItem = () => {
const visibleItems = gBrowser.tabContainer.ariaFocusableItems;
return visibleItems[visibleItems.length - 1] === aItem;
};
try {
const tabSize = aTab.getBoundingClientRect().height;
const transform = `-${tabSize}px`;
const itemSize = aItem.getBoundingClientRect().height;
const transform = `-${itemSize}px`;
gZenUIManager.motion
.animate(
aTab,
aItem,
{
opacity: [0, 1],
transform: ['scale(0.95)', 'scale(1)'],
marginBottom: isLastTab() ? [] : [transform, '0px'],
marginBottom: isLastItem() ? [] : [transform, '0px'],
},
{
duration: 0.12,
@@ -581,13 +581,15 @@ var gZenVerticalTabsManager = {
console.error(err);
})
.finally(() => {
aTab.style.removeProperty('margin-bottom');
aTab.style.removeProperty('transform');
aTab.style.removeProperty('opacity');
aItem.style.removeProperty('margin-bottom');
aItem.style.removeProperty('transform');
aItem.style.removeProperty('opacity');
});
const itemLabel =
aItem.querySelector('.tab-group-label-container') || aItem.querySelector('.tab-content');
gZenUIManager.motion
.animate(
aTab.querySelector('.tab-content'),
itemLabel,
{
filter: ['blur(1px)', 'blur(0px)'],
},
@@ -601,7 +603,7 @@ var gZenVerticalTabsManager = {
console.error(err);
})
.finally(() => {
aTab.querySelector('.tab-stack').style.removeProperty('filter');
itemLabel.style.removeProperty('filter');
});
} catch (e) {
console.error(e);
@@ -1045,7 +1047,8 @@ var gZenVerticalTabsManager = {
return;
this._tabEdited =
event.target.closest('.tabbrowser-tab') ||
event.target.closest('.zen-current-workspace-indicator-name');
event.target.closest('.zen-current-workspace-indicator-name') ||
(event.explicit && event.target.closest('.tab-group-label'));
if (
!this._tabEdited ||
((!this._tabEdited.pinned || this._tabEdited.hasAttribute('zen-essential')) && isTab)

View File

@@ -2,7 +2,23 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
{
class ZenEmojiPicker extends nsZenDOMOperatedFeature {
// prettier-ignore
const SVG_ICONS = [
"airplane.svg", "baseball.svg", "basket.svg",
"bed.svg", "bell.svg", "bookmark.svg", "book.svg",
"chat.svg", "checkbox.svg", "circle.svg", "cloud.svg",
"code.svg", "coins.svg", "cutlery.svg", "egg.svg",
"flag.svg", "folder.svg", "globe.svg", "grid-2x2.svg",
"grid-3x3.svg", "heart.svg", "inbox.svg", "layers.svg",
"lightning.svg", "mail.svg", "map.svg", "moon.svg",
"music.svg", "page.svg", "palette.svg", "paw.svg",
"people.svg", "pizza.svg", "planet.svg", "present.svg",
"shapes.svg", "skull.svg", "squares.svg", "square.svg",
"star-2.svg", "star.svg", "sun.svg", "terminal.svg",
"triangle.svg", "video.svg", "weight.svg",
];
class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
#panel;
#anchor;
@@ -15,7 +31,7 @@
this.#panel = document.getElementById('PanelUI-zen-emojis-picker');
this.#panel.addEventListener('popupshowing', this);
this.#panel.addEventListener('popuphidden', this);
document.getElementById('PanelUI-zen-emojis-picker-none').addEventListener('command', this);
this.#panel.addEventListener('command', this);
this.searchInput.addEventListener('input', this);
}
@@ -30,6 +46,10 @@
case 'command':
if (event.target.id === 'PanelUI-zen-emojis-picker-none') {
this.#selectEmoji(null);
} else if (event.target.id === 'PanelUI-zen-emojis-picker-change-emojis') {
this.#changePage(false);
} else if (event.target.id === 'PanelUI-zen-emojis-picker-change-svg') {
this.#changePage(true);
}
break;
case 'input':
@@ -55,10 +75,35 @@
return document.getElementById('PanelUI-zen-emojis-picker-list');
}
get svgList() {
return document.getElementById('PanelUI-zen-emojis-picker-svgs');
}
get searchInput() {
return document.getElementById('PanelUI-zen-emojis-picker-search');
}
#changePage(toSvg = false) {
const itemToScroll = toSvg
? this.svgList
: document
.getElementById('PanelUI-zen-emojis-picker-pages')
.querySelector('[emojis="true"]');
itemToScroll.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'start',
});
const button = document.getElementById(
`PanelUI-zen-emojis-picker-change-${toSvg ? 'svg' : 'emojis'}`
);
const otherButton = document.getElementById(
`PanelUI-zen-emojis-picker-change-${toSvg ? 'emojis' : 'svg'}`
);
button.classList.add('selected');
otherButton.classList.remove('selected');
}
#clearEmojis() {
delete this._emojis;
}
@@ -88,29 +133,48 @@
async #onPopupShowing(event) {
if (event.target !== this.#panel) return;
this.searchInput.value = '';
const emojiList = this.emojiList;
for (const emoji of this.#emojis) {
const item = document.createXULElement('toolbarbutton');
item.className = 'toolbarbutton-1 zen-emojis-picker-emoji';
item.setAttribute('label', emoji.emoji);
item.setAttribute('tooltiptext', emoji.annotation);
item.addEventListener('command', () => {
this.#selectEmoji(emoji.emoji);
});
emojiList.appendChild(item);
const allowEmojis = !this.#panel.hasAttribute('only-svg-icons');
if (allowEmojis) {
const emojiList = this.emojiList;
for (const emoji of this.#emojis) {
const item = document.createXULElement('toolbarbutton');
item.className = 'toolbarbutton-1 zen-emojis-picker-emoji';
item.setAttribute('label', emoji.emoji);
item.addEventListener('command', () => {
this.#selectEmoji(emoji.emoji);
});
emojiList.appendChild(item);
}
setTimeout(() => {
this.searchInput.focus();
}, 500);
}
const svgList = this.svgList;
for (const icon of SVG_ICONS) {
const item = document.createXULElement('toolbarbutton');
item.className = 'toolbarbutton-1 zen-emojis-picker-svg';
item.setAttribute('label', icon);
item.setAttribute('tooltiptext', icon.replace('.svg', ''));
item.style.listStyleImage = `url(${this.getSVGURL(icon)})`;
item.setAttribute('icon', icon);
item.addEventListener('command', () => {
this.#selectEmoji(this.getSVGURL(icon));
});
svgList.appendChild(item);
}
setTimeout(() => {
this.searchInput.focus();
}, 500);
}
#onPopupHidden(event) {
if (event.target !== this.#panel) return;
this.#clearEmojis();
this.#changePage(false);
const emojiList = this.emojiList;
emojiList.innerHTML = '';
this.svgList.innerHTML = '';
if (this.#currentPromiseReject) {
this.#currentPromiseReject(new Error('Emoji picker closed without selection'));
}
@@ -128,7 +192,7 @@
this.#panel.hidePopup();
}
open(anchor) {
open(anchor, { onlySvgIcons = false } = {}) {
if (this.#currentPromise) {
return null;
}
@@ -138,10 +202,19 @@
});
this.#anchor = anchor;
this.#anchor.setAttribute('zen-emoji-open', 'true');
if (onlySvgIcons) {
this.#panel.setAttribute('only-svg-icons', 'true');
} else {
this.#panel.removeAttribute('only-svg-icons');
}
this.#panel.openPopup(anchor, 'after_start', 0, 0, false, false);
return this.#currentPromise;
}
getSVGURL(icon) {
return `chrome://browser/skin/zen-icons/selectable/${icon}`;
}
}
window.gZenEmojiPicker = new ZenEmojiPicker();
window.gZenEmojiPicker = new nsZenEmojiPicker();
}

View File

@@ -50,8 +50,31 @@ body > #confetti {
#PanelUI-zen-emojis-picker-header {
gap: 10px;
align-items: center;
padding: 10px;
padding-bottom: 5px;
padding: 0px 10px;
padding-bottom: 6px;
}
#PanelUI-zen-emojis-picker-pages {
overflow: hidden;
max-height: 230px;
& > * {
min-width: 100%;
}
}
&[only-svg-icons='true'] {
& #PanelUI-zen-emojis-picker-change-emojis {
display: none;
}
& #PanelUI-zen-emojis-picker-change-svg {
background: transparent !important;
}
& #PanelUI-zen-emojis-picker-pages > vbox[emojis='true'] {
display: none;
}
}
#PanelUI-zen-emojis-picker-none label {
@@ -63,11 +86,36 @@ body > #confetti {
width: 100%;
}
#PanelUI-zen-emojis-picker-list {
flex-wrap: wrap;
max-height: 265px;
#PanelUI-zen-picker-header {
padding: 6px 16px;
justify-content: space-between;
& #PanelUI-zen-emojis-buttons-wrapper {
gap: 4px;
& toolbarbutton {
appearance: none;
font-size: small;
padding: 0 6px;
border-radius: 4px;
font-weight: 600;
&:hover {
background-color: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1));
}
&.selected {
background-color: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1));
}
}
}
}
#PanelUI-zen-emojis-picker-list,
#PanelUI-zen-emojis-picker-svgs {
overflow-y: auto;
overflow-x: hidden;
width: 100%;
padding: 10px;
padding-top: 5px;
@@ -77,19 +125,31 @@ body > #confetti {
grid-template-columns: repeat(auto-fill, minmax(28px, 1fr));
.zen-emojis-picker-emoji {
appearance: none;
font-size: 14px;
padding: 0px !important;
& image {
display: none;
}
}
.zen-emojis-picker-svg {
padding: 0px !important;
& label {
display: none;
}
& .toolbarbutton-icon {
width: 22px;
}
}
}
.zen-emojis-picker-emoji,
.zen-emojis-picker-svg,
#PanelUI-zen-emojis-picker-none {
width: 22px;
height: 22px;
appearance: none;
&:hover {
background-color: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1));

View File

@@ -44,7 +44,7 @@
var(--zen-colors-tertiary) 99%
);
--zen-dialog-background: light-dark(var(--zen-colors-tertiary), var(--zen-branding-bg));
--zen-dialog-background: light-dark(rgb(219, 219, 219), rgb(31, 31, 31));
--zen-urlbar-background: color-mix(in srgb, var(--zen-primary-color) 3%, #f4f4f4 97%);
--zen-secondary-btn-color: var(--zen-colors-primary-foreground);

View File

@@ -110,6 +110,11 @@ document.addEventListener(
case 'cmd_zenOpenWorkspaceCreation':
gZenWorkspaces.openWorkspaceCreation(event);
break;
case 'cmd_zenOpenFolderCreation':
gZenFolders.createFolder([], {
renameFolder: true,
});
break;
default:
gZenGlanceManager.handleMainCommandSet(event);
if (event.target.id.startsWith('cmd_zenWorkspaceSwitch')) {

View File

@@ -0,0 +1,201 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
{
class ZenFolder extends MozTabbrowserTabGroup {
#initialized = false;
static markup = `
<hbox class="tab-group-label-container" pack="center">
<html:div class="tab-group-folder-icon"/>
<label class="tab-group-label" role="button"/>
</hbox>
<html:div class="tab-group-container">
<html:div class="zen-tab-group-start" />
</html:div>
`;
static rawIcon = new DOMParser().parseFromString(
`
<svg width="100%" height="100%" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="-67.409 -14.145 29.279 28.92">
<defs>
<linearGradient gradientUnits="userSpaceOnUse" x1="-53.05" y1="-3.8" x2="-53.05" y2="8.998" id="gradient-1">
<stop offset="0" style="stop-color: rgb(255, 255, 255);"/>
<stop offset="1" style="stop-color: rgb(0% 0% 0%)"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" x1="-40.286" y1="-3.091" x2="-40.286" y2="13.31" id="gradient-0" gradientTransform="matrix(1, 0, 0, 1, -12.717999, -4.409)">
<stop offset="0" style="stop-color: rgb(255, 255, 255);"/>
<stop offset="1" style="stop-color: rgb(0% 0% 0%)"/>
</linearGradient>
</defs>
<!--Back Folder (path)-->
<path shape-rendering="geometricPrecision" d="M -61.3 -5.25 C -61.3 -6.492 -60.293 -7.5 -59.05 -7.5 L -55.102 -7.5 C -54.591 -7.5 -54.096 -7.326 -53.697 -7.007 L -52.84 -6.321 C -52.175 -5.79 -51.349 -5.5 -50.498 -5.5 L -47.05 -5.5 C -45.807 -5.5 -44.8 -4.492 -44.8 -3.25 L -44.731 4.42 L -44.708 6.651 C -44.708 7.894 -45.715 8.901 -46.958 8.901 L -58.958 8.901 C -60.201 8.901 -61.208 7.894 -61.208 6.651 L -61.3 4.752 L -61.3 -5.25 Z" style="stroke-width: 1.3px; transform-box: fill-box; transform-origin: 50% 50%; fill: var(--zen-folder-behind-bgcolor); stroke: var(--zen-folder-stroke);">
<animateTransform type="skewX" additive="sum" attributeName="transform" values="-1;17" dur="0.15s" fill="freeze" keyTimes="0; 1" calcMode="spline" keySplines="0.42 0 0.58 1"/>
<animateTransform type="translate" additive="sum" attributeName="transform" values="0 0;-1.5 0" dur="0.15s" fill="freeze" keyTimes="0; 1" calcMode="spline" keySplines="0.42 0 0.58 1"/>
</path>
<path shape-rendering="geometricPrecision" d="M -61.3 -5.25 C -61.3 -6.492 -60.293 -7.5 -59.05 -7.5 L -55.102 -7.5 C -54.591 -7.5 -54.096 -7.326 -53.697 -7.007 L -52.84 -6.321 C -52.175 -5.79 -51.349 -5.5 -50.498 -5.5 L -47.05 -5.5 C -45.807 -5.5 -44.8 -4.492 -44.8 -3.25 L -44.731 4.42 L -44.708 6.651 C -44.708 7.894 -45.715 8.901 -46.958 8.901 L -58.958 8.901 C -60.201 8.901 -61.208 7.894 -61.208 6.651 L -61.3 4.752 L -61.3 -5.25 Z" style="stroke-width: 1.3px; fill-opacity: 0.1; fill: url(&quot;#gradient-0&quot;); transform-origin: -53.004px 0.701px;">
<animateTransform type="skewX" additive="sum" attributeName="transform" values="-1;17" dur="0.15s" fill="freeze" keyTimes="0; 1" calcMode="spline" keySplines="0.42 0 0.58 1"/>
<animateTransform type="translate" additive="sum" attributeName="transform" values="0 0;-1.5 0" dur="0.15s" fill="freeze" keyTimes="0; 1" calcMode="spline" keySplines="0.42 0 0.58 1"/>
</path>
<!--Front Folder (rect)-->
<rect shape-rendering="geometricPrecision" x="-61.301" y="-3.768" width="16.5" height="12.798" rx="2.25" style="stroke-width: 1.3px; transform-box: fill-box; transform-origin: 50% 50%; fill: var(--zen-folder-front-bgcolor); stroke: var(--zen-folder-stroke);" id="object-0">
<animateTransform type="skewX" additive="sum" attributeName="transform" values="1;-17" dur="0.15s" fill="freeze" keyTimes="0; 1" calcMode="spline" keySplines="0.42 0 0.58 1"/>
<animateTransform type="translate" additive="sum" attributeName="transform" values="0 0;3 0" dur="0.15s" fill="freeze" keyTimes="0; 1" calcMode="spline" keySplines="0.42 0 0.58 1"/>
</rect>
<rect shape-rendering="geometricPrecision" x="-61.3" y="-3.8" width="16.5" height="12.798" style="stroke-width: 1.3px; fill-opacity: 0.1; transform-origin: -53.05px 2.599px; fill: url(&quot;#gradient-1&quot;);" id="rect-1" rx="2.25">
<animateTransform type="skewX" additive="sum" attributeName="transform" values="1;-17" dur="0.15s" fill="freeze" keyTimes="0; 1" calcMode="spline" keySplines="0.42 0 0.58 1"/>
<animateTransform type="translate" additive="sum" attributeName="transform" values="0 0;3 0" dur="0.15s" fill="freeze" keyTimes="0; 1" calcMode="spline" keySplines="0.42 0 0.58 1"/>
</rect>
<!--Icon (g)-->
<g id="folder-icon" shape-rendering="geometricPrecision" style="fill-opacity: 1; transform-origin: -53.05px 5.399px; fill: var(--zen-folder-stroke);">
<image href="" height="18px" width="19px"/>
<animateTransform type="skewX" additive="sum" attributeName="transform" values="0;-17" dur="0.15s" fill="freeze" keyTimes="0; 1" calcMode="spline" keySplines="0.42 0 0.58 1"/>
<animateTransform type="translate" additive="sum" attributeName="transform" values="-10 -9;-7.5 -9" dur="0.15s" fill="freeze" keyTimes="0; 1" calcMode="spline" keySplines="0.42 0 0.58 1"/>
<animate attributeName="opacity" values="1;1" dur="0.15s" fill="freeze" keyTimes="0; 1" calcMode="spline" keySplines="0.42 0 0.58 1"/>
</g>
<!--End Icon (g)-->
<g id="folder-dots" style="fill-opacity: 1; fill: var(--zen-folder-stroke);">
<animateTransform type="skewX" additive="sum" attributeName="transform" values="1;-17" dur="0.2s" fill="freeze" keyTimes="0; 1" calcMode="spline" keySplines="0.42 0 0.58 1"/>
<animateTransform type="translate" additive="sum" attributeName="transform" values="0 0.5;5 -0.5" dur="0.2s" fill="freeze" keyTimes="0; 1" calcMode="spline" keySplines="0.42 0 0.58 1"/>
<animate attributeName="opacity" values="0;0" dur="0.2s" fill="freeze" keyTimes="0; 1" calcMode="spline" keySplines="0.42 0 0.58 1"/>
<path transform="translate(1.2, 0)" shape-rendering="geometricPrecision" d="M -59.363 2.243 C -59.363 2.074 -59.33 1.915 -59.262 1.76 C -59.192 1.612 -59.107 1.478 -58.996 1.373 C -58.885 1.256 -58.751 1.165 -58.598 1.101 C -58.448 1.033 -58.289 1 -58.114 1 C -57.945 1 -57.785 1.033 -57.636 1.101 C -57.482 1.165 -57.354 1.256 -57.244 1.373 C -57.131 1.478 -57.042 1.612 -56.972 1.76 C -56.904 1.915 -56.871 2.074 -56.871 2.243 C -56.871 2.414 -56.904 2.573 -56.972 2.727 C -57.042 2.876 -57.131 3.008 -57.244 3.125 C -57.354 3.232 -57.482 3.321 -57.636 3.385 C -57.785 3.455 -57.945 3.486 -58.114 3.486 C -58.289 3.486 -58.448 3.455 -58.598 3.385 C -58.751 3.321 -58.885 3.232 -58.996 3.125 C -59.107 3.008 -59.192 2.876 -59.262 2.727 C -59.33 2.573 -59.363 2.414 -59.363 2.243 Z"/>
<path shape-rendering="geometricPrecision" d="M -54.38 2.243 C -54.38 2.074 -54.347 1.915 -54.279 1.76 C -54.215 1.612 -54.124 1.478 -54.019 1.373 C -53.902 1.256 -53.769 1.165 -53.621 1.101 C -53.466 1.033 -53.306 1 -53.137 1 C -52.966 1 -52.807 1.033 -52.653 1.101 C -52.504 1.165 -52.372 1.256 -52.265 1.373 C -52.148 1.478 -52.059 1.612 -51.995 1.76 C -51.925 1.915 -51.894 2.074 -51.894 2.243 C -51.894 2.414 -51.925 2.573 -51.995 2.727 C -52.059 2.876 -52.148 3.008 -52.265 3.125 C -52.372 3.232 -52.504 3.321 -52.653 3.385 C -52.807 3.455 -52.966 3.486 -53.137 3.486 C -53.306 3.486 -53.466 3.455 -53.621 3.385 C -53.769 3.321 -53.902 3.232 -54.019 3.125 C -54.124 3.008 -54.215 2.876 -54.279 2.727 C -54.347 2.573 -54.38 2.414 -54.38 2.243 Z"/>
<path transform="translate(-1.2, 0)" shape-rendering="geometricPrecision" d="M -49.402 2.243 C -49.402 2.074 -49.37 1.915 -49.302 1.76 C -49.232 1.612 -49.147 1.478 -49.036 1.373 C -48.924 1.256 -48.791 1.165 -48.638 1.101 C -48.488 1.033 -48.329 1 -48.154 1 C -47.984 1 -47.824 1.033 -47.676 1.101 C -47.521 1.165 -47.395 1.256 -47.282 1.373 C -47.171 1.478 -47.082 1.612 -47.012 1.76 C -46.942 1.915 -46.911 2.074 -46.911 2.243 C -46.911 2.414 -46.942 2.573 -47.012 2.727 C -47.082 2.876 -47.171 3.008 -47.282 3.125 C -47.395 3.232 -47.521 3.321 -47.676 3.385 C -47.824 3.455 -47.984 3.486 -48.154 3.486 C -48.329 3.486 -48.488 3.455 -48.638 3.385 C -48.791 3.321 -48.924 3.232 -49.036 3.125 C -49.147 3.008 -49.232 2.876 -49.302 2.727 C -49.37 2.573 -49.402 2.414 -49.402 2.243 Z"/>
</g>
</svg>`,
'image/svg+xml'
).documentElement;
constructor() {
super();
}
connectedCallback() {
super.connectedCallback();
this.labelElement.pinned = true;
if (this.#initialized) {
return;
}
this.#initialized = true;
this.icon.appendChild(ZenFolder.rawIcon.cloneNode(true));
// Save original values for animations
this.icon.querySelectorAll('animate, animateTransform, animateMotion').forEach((anim) => {
const vals = anim.getAttribute('values');
if (vals) {
anim.dataset.origValues = vals;
}
});
this.labelElement.parentElement.setAttribute('context', 'zenFolderActions');
this.labelElement.onRenameFinished = (newLabel) => {
this.name = newLabel;
const event = new CustomEvent('ZenFolderRenamed', {
bubbles: true,
});
this.dispatchEvent(event);
};
if (this.collapsed) {
this.querySelector('.tab-group-container').setAttribute('hidden', true);
}
}
get icon() {
return this.querySelector('.tab-group-folder-icon');
}
/**
* Returns the group this folder belongs to.
* @returns {MozTabbrowserTabGroup|null} The group this folder belongs to, or null if it is not part of a group.
**/
get group() {
if (gBrowser.isTabGroup(this.parentElement?.parentElement)) {
return this.parentElement.parentElement;
}
return null;
}
get isZenFolder() {
return true;
}
get activeGroups() {
let activeGroups = [];
let currentGroup = this;
if (currentGroup?.hasAttribute('has-active')) activeGroups.push(currentGroup);
while (currentGroup?.group) {
currentGroup = currentGroup?.group;
if (currentGroup?.hasAttribute('has-active')) {
activeGroups.push(currentGroup);
}
}
return activeGroups;
}
// Dont expand the folder when the user selects a tab in it
on_TabSelect() {
this.collapsed = this.hasAttribute('has-active');
}
rename() {
gZenVerticalTabsManager.renameTabStart({
target: this.labelElement,
explicit: true,
});
}
async expandGroupTabs() {
for (let tab of this.allItems.reverse()) {
tab = tab.group.hasAttribute('split-view-group') ? tab.group : tab;
if (tab.hasAttribute('zen-empty-tab')) {
await ZenPinnedTabsStorage.removePin(tab.getAttribute('zen-pin-id'));
gBrowser.removeTab(tab);
} else {
gBrowser.ungroupTab(tab);
}
}
}
async delete() {
for (const tab of this.tabs) {
await ZenPinnedTabsStorage.removePin(tab.getAttribute('zen-pin-id'));
if (tab.hasAttribute('zen-empty-tab')) {
// Manually remove the empty tabs as removeTabs() inside removeTabGroup
// does ignore them.
gBrowser.removeTab(tab);
}
}
await gBrowser.removeTabGroup(this, { isUserTriggered: true });
}
get level() {
return this.group?.level + 1 || 0;
}
get allItems() {
return [...this.querySelector('.tab-group-container').children].filter(
(child) => !child.classList.contains('zen-tab-group-start')
);
}
get pinned() {
return this.isZenFolder;
}
/**
* Intentionally ignore attempts to change the pinned state.
* ZenFolder instances determine their "pinned" status based on their type (isZenFolder)
* and do not support being pinned or unpinned via this setter.
* This no-op setter ensures compatibility with interfaces expecting a pinned property,
* while preserving the invariant that ZenFolders cannot have their pinned state changed externally.
*/
set pinned(value) {}
get iconURL() {
return this.icon.querySelector('image')?.getAttribute('href') || '';
}
}
customElements.define('zen-folder', ZenFolder);
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,130 +4,136 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
tab-group {
& .tabbrowser-tab {
margin-inline-start: 0 !important;
}
}
tab-group[split-view-group] {
display: flex;
flex-wrap: nowrap;
border-radius: var(--border-radius-medium);
padding: 0 2px;
margin-inline: var(--tab-block-margin);
margin-block: var(--tab-block-margin);
min-height: var(--tab-min-height);
outline: var(--tab-outline);
outline-offset: var(--tab-outline-offset);
outline-color: var(--tab-selected-outline-color);
transition: scale 0.1s ease;
align-items: center;
display: block;
@media (prefers-reduced-motion: no-preference) {
#tabbrowser-tabs[movingtab] &:not(:active) {
transition: var(--tab-dragover-transition);
}
}
--zen-split-view-active-tab-bg: color-mix(
in srgb,
var(--zen-toolbar-element-bg),
transparent 40%
);
:root:not([zen-sidebar-expanded='true']) & {
& .tab-group-container {
display: flex;
flex-wrap: nowrap;
align-items: center;
border-radius: var(--border-radius-medium);
padding: 0 2px;
--tab-min-height: 30px;
--tab-collapsed-width: 38px;
margin: 2px 0;
--tab-min-width: 34px;
}
margin-inline: var(--tab-block-margin);
margin-block: var(--tab-block-margin);
min-height: var(--tab-min-height);
outline: var(--tab-outline);
outline-offset: var(--tab-outline-offset);
outline-color: var(--tab-selected-outline-color);
transition: scale 0.1s ease;
margin-inline-start: 0 !important;
& > .tabbrowser-tab {
--tab-selected-bgcolor: var(--zen-split-view-active-tab-bg);
--tab-hover-background-color: transparent;
--tab-selected-shadow: none;
--border-radius-medium: var(--tab-border-radius);
--zen-active-tab-scale: 1;
:root[zen-sidebar-expanded='true'] & {
--tab-min-height: 28px;
:root:not([zen-sidebar-expanded='true']) & {
padding: 0;
--tab-collapsed-width: 38px;
overflow: clip;
--tab-min-width: 34px;
outline: 2px solid var(--zen-colors-border-contrast);
outline-offset: -2px;
}
container-type: inline-size;
container-name: browser-tab;
flex: 1 !important;
padding-inline: 2px !important;
overflow: clip;
&:not(:last-child)::after {
content: '';
width: 1px;
height: 16px;
background-color: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.2));
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
}
:root:not([zen-sidebar-expanded='true']) &:not(:last-child)::after {
width: 16px;
height: 1px;
top: auto;
bottom: 0;
right: 50%;
transform: translateX(50%);
}
& .tab-content {
min-width: 0;
:root[zen-sidebar-expanded='true'] & {
justify-content: unset !important;
}
}
}
&:has(> tab:is([visuallyselected], [multiselected])) {
background-color: var(--tab-selected-bgcolor);
box-shadow: var(--tab-selected-shadow);
& > .tabbrowser-tab {
--tab-hover-background-color: var(--zen-split-view-active-tab-bg);
& .tab-background {
background-color: var(--zen-split-view-active-tab-bg) !important;
--tab-selected-bgcolor: var(--zen-split-view-active-tab-bg);
--tab-hover-background-color: transparent;
--tab-selected-shadow: none;
--border-radius-medium: var(--tab-border-radius);
--zen-active-tab-scale: 1;
:root[zen-sidebar-expanded='true'] & {
--tab-min-height: 28px;
}
&::after {
display: none;
container-type: inline-size;
container-name: browser-tab;
flex: 1 !important;
padding-inline: 2px !important;
overflow: clip;
&:not(:last-child)::after {
content: '';
width: 1px;
height: 16px;
background-color: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.2));
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
}
:root:not([zen-sidebar-expanded='true']) &:not(:last-child)::after {
width: 16px;
height: 1px;
top: auto;
bottom: 0;
right: 50%;
transform: translateX(50%);
}
& .tab-content {
min-width: 0;
:root[zen-sidebar-expanded='true'] & {
justify-content: unset !important;
}
}
}
}
&:active {
scale: var(--zen-active-tab-scale);
}
&:has(> tab:is([visuallyselected], [multiselected])) {
background-color: var(--tab-selected-bgcolor);
box-shadow: var(--tab-selected-shadow);
&:hover {
background-color: var(--zen-toolbar-element-bg);
}
& > .tabbrowser-tab {
--tab-hover-background-color: var(--zen-split-view-active-tab-bg);
& .tab-background {
background-color: var(--zen-split-view-active-tab-bg) !important;
}
& .tab-close-button,
& .tab-reset-button {
margin-inline-end: -3px !important;
display: none !important;
}
@container browser-tab (min-width: 70px) {
:root[zen-sidebar-expanded='true'] &:hover > .tabbrowser-tab:not([pinned]) .tab-close-button {
display: block !important;
&::after {
display: none;
}
}
}
}
@media (prefers-reduced-motion: no-preference) {
#tabbrowser-tabs[movingtab] & {
transition: var(--tab-dragover-transition);
&:active {
scale: var(--zen-active-tab-scale);
}
&:hover {
background-color: var(--zen-toolbar-element-bg);
}
& .tab-close-button,
& .tab-reset-button {
margin-inline-end: -3px !important;
display: none !important;
}
@container browser-tab (min-width: 70px) {
:root[zen-sidebar-expanded='true'] &:hover > .tabbrowser-tab:not([pinned]) .tab-close-button {
display: block !important;
}
}
@media (prefers-reduced-motion: no-preference) {
#tabbrowser-tabs[movingtab] & {
transition: var(--tab-dragover-transition);
}
}
}
}
:root:not([zen-sidebar-expanded='true']) {
tab-group {
tab-group .tab-group-container {
flex-direction: column;
}
}
@@ -139,6 +145,8 @@ tab-group[split-view-group] .tabbrowser-tab {
tab-group[split-view-group] .tab-group-label-container {
visibility: collapse;
padding: 0 !important;
margin: 0 !important;
}
tab-group[split-view-group] .tab-close-button {
@@ -151,50 +159,265 @@ tab-group[split-view-group] .tab-group-line {
background: transparent;
}
tab-group:not([split-view-group]) {
& .tab-group-label-container {
min-width: fit-content;
max-width: 100%;
height: fit-content !important;
display: flex;
justify-content: start;
margin-top: 10px;
margin-bottom: 10px;
}
& .tab-group-label {
text-align: start;
flex-grow: 1 !important;
min-width: fit-content;
max-width: 100%;
font-size: 14px !important;
display: block !important;
padding-right: 8px;
}
& .tab-group-line {
display: none !important;
}
&[collapsed] .tabbrowser-tab {
display: none !important;
}
&:not([collapsed]) .tabbrowser-tab {
margin-left: 10px;
}
&:not([collapsed]) .tabbrowser-tab::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 2px;
height: 100%;
background-color: var(--tab-group-color);
}
}
.tab-group-line {
display: none !important;
}
#tabbrowser-tabs[orient='vertical'][expanded] {
tab-group > :is(.tab-group-label-container, .tabbrowser-tab),
&[movingtab][movingtab-addToGroup]:not([movingtab-createGroup], [movingtab-ungroup])
.tabbrowser-tab:is(:active, [multiselected]) {
margin-inline-start: 0;
}
}
zen-folder {
display: flex;
flex-direction: column;
--zen-folder-behind-bgcolor: color-mix(
in srgb,
var(--zen-primary-color) 75%,
light-dark(black, white)
);
--zen-folder-front-bgcolor: light-dark(
color-mix(in srgb, var(--zen-primary-color), white 75%),
color-mix(in srgb, var(--zen-primary-color), black 40%)
);
--zen-folder-stroke: color-mix(in srgb, var(--zen-colors-primary) 10%, var(--toolbox-textcolor));
-moz-window-dragging: no-drag;
&[selected] > .tab-group-label-container::before {
background-color: color-mix(in srgb, var(--zen-colors-border) 60%, transparent);
}
&[collapsed][has-active] {
@media (prefers-reduced-motion: no-preference) {
#tabbrowser-tabs[movingtab] &:not(:active) {
transition: var(--tab-dragover-transition);
}
}
}
:root:not([zen-sidebar-expanded]) & {
width: var(--tab-min-width) !important;
margin: var(--tab-block-margin) auto !important;
& .tab-group-label {
visibility: hidden !important;
}
}
& > .tab-group-container {
:root[zen-sidebar-expanded] & > * {
margin-inline-start: var(--zen-folder-indent, 14px) !important;
}
& > zen-folder {
transition: margin-inline-start 0.15s ease-in-out;
}
& > tab::before {
background: none !important;
}
}
&[collapsed][has-active='true'] > .tab-group-container zen-folder {
margin-inline-start: 0 !important;
}
margin: 0 var(--tab-block-margin);
& > .tab-group-label-container {
flex: 0 0 auto !important;
top: 0 !important;
--tab-group-color-pale: transparent !important;
--tab-group-color: transparent !important;
padding-block-end: 0 !important;
margin: 0 !important;
height: 40px;
padding-inline: var(--tab-group-label-padding);
align-items: center;
:root:not([zen-sidebar-expanded]) {
width: var(--tab-min-width) !important;
}
.tab-group-folder-icon {
width: 30px;
height: 28px;
align-content: center;
pointer-events: none;
position: absolute;
pointer-events: none;
:root[zen-sidebar-expanded] & {
left: 2px;
}
& svg image {
fill-opacity: 0.9;
-moz-context-properties: fill, fill-opacity;
fill: var(--zen-folder-stroke);
}
}
&::before {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: calc(100% - 4px);
height: calc(100% - 4px);
background-color: transparent;
border-radius: var(--border-radius-medium);
z-index: -1;
}
&:hover::before {
background-color: var(--tab-hover-background-color) !important;
}
&:after {
display: none;
}
& > label {
width: 100% !important;
background: transparent !important;
border: none !important;
color: var(--sidebar-text-color) !important;
margin: 0 !important;
font-weight: 500;
padding: 0 0 0 30px !important;
display: flex;
align-items: center;
height: 100% !important;
text-align: start;
:root:not([zen-sidebar-expanded]) & {
padding: 0 !important;
max-width: 100% !important;
}
}
}
&[collapsed] {
& > .tabbrowser-tab:not([hidden]) {
display: flex;
}
& > .tab-group-container {
overflow-y: clip;
}
}
}
/* Tabs popup */
#zen-folder-tabs-popup {
--arrowpanel-padding: 0;
width: 250px;
& #zen-folder-tabs-search-no-results {
height: 100%;
align-items: center;
text-align: center;
padding: 2em 0;
justify-content: center;
max-width: calc(100% - 10px);
margin: 0 auto;
}
}
#zen-folder-tabs-popup * {
max-width: -moz-available;
}
#zen-folder-tabs-popup .tabs-list-header {
display: flex;
flex-direction: row;
padding: 6px;
border-bottom: 1px solid color-mix(in srgb, currentColor, transparent 90%);
margin-bottom: 2px;
}
#zen-folder-tabs-popup #zen-folder-tabs-list-search {
background-color: transparent;
border: none !important;
outline: none !important;
}
#zen-folder-tabs-popup #zen-folder-tabs-list {
padding: 4px;
}
#zen-folder-tabs-popup .zen-folder-tabs-list-search-icon {
width: 16px;
height: 16px;
margin: 10px 2px 10px 10px;
-moz-context-properties: fill;
fill: currentColor;
opacity: 0.7;
}
.folders-tabs-list-item-icon {
width: 16px;
height: 16px;
flex-shrink: 0;
margin-inline-end: 10px;
margin-inline-start: 4px;
}
.folders-tabs-list-item-labels {
display: flex;
flex-direction: column;
justify-content: center;
flex: 1;
min-width: 0;
color: var(--toolbox-textcolor);
}
.folders-tabs-list-item-label,
.tab-list-item-secondary-label {
position: relative;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 250px;
color: light-dark(black, white);
}
.tab-list-item-secondary-label {
opacity: 0.6;
font-size: 0.7em;
font-weight: 500;
}
#zen-folder-tabs-popup .tabs-list-scrollbox {
overflow: scroll;
}
.folders-tabs-list-item {
position: relative;
border-radius: var(--zen-border-radius);
cursor: default;
}
.folders-tabs-list-item-content {
position: relative;
height: 40px;
display: flex;
align-items: center;
direction: ltr;
padding: 0 var(--tab-inline-padding);
border-radius: 4px;
&:hover {
background-color: color-mix(in srgb, currentColor, transparent 90%);
}
&:last-child {
border-bottom-left-radius: max(calc(var(--panel-border-radius) - 4px), 4px);
border-bottom-right-radius: max(calc(var(--panel-border-radius) - 4px), 4px);
}
}

View File

@@ -877,7 +877,9 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
*/
removeGroup(groupIndex) {
const group = this._data[groupIndex];
gZenFolders.expandGroupTabs(group);
for (const tab of group.tabs.reverse()) {
gBrowser.ungroupTab(tab);
}
if (this.currentView === groupIndex) {
this.deactivateCurrentSplitView();
}
@@ -1867,8 +1869,8 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
for (const group of data) {
const groupElement = document.getElementById(group.groupId);
if (groupElement) {
const tabs = groupElement.querySelectorAll('tab');
this.splitTabs([...tabs], group.gridType);
const tabs = groupElement.tabs;
this.splitTabs(tabs, group.gridType);
}
}
delete this._sessionRestoring;

View File

@@ -5,7 +5,21 @@
const lazy = {};
class ZenPinnedTabsObserver {
static ALL_EVENTS = ['TabPinned', 'TabUnpinned', 'TabMove'];
static ALL_EVENTS = [
'TabPinned',
'TabUnpinned',
'TabMove',
'TabGroupCreate',
'TabGroupRemoved',
'TabGroupMoved',
'ZenFolderRenamed',
'ZenFolderIconChanged',
'TabGroupCollapse',
'TabGroupExpand',
'TabGrouped',
'TabUngrouped',
'ZenFolderChangedWorkspace',
];
#listeners = [];
@@ -93,16 +107,6 @@
tab.style.setProperty('--zen-essential-tab-icon', `url(${iconUrl})`);
}
}
// TODO: work on this
//if (tab.hasAttribute('zen-pinned-changed') || !this._pinsCache) {
// return;
//}
// Save if the url is the same as the pinned tab
//const pin = this._pinsCache.find((pin) => pin.uuid === tab.getAttribute('zen-pin-id'));
//if (pin) {
// pin.iconUrl = iconUrl;
// this.savePin(pin);
//}
}
_onTabResetPinButton(event, tab) {
@@ -135,27 +139,30 @@
}
await ZenPinnedTabsStorage.promiseInitialized;
await gZenWorkspaces.promiseSectionsInitialized;
await this._initializePinsCache();
await this.#initializePinsCache();
(async () => {
// Execute in a separate task to avoid blocking the main thread
await SessionStore.promiseAllWindowsRestored;
await gZenWorkspaces.promiseInitialized;
await this._initializePinnedTabs(init);
await this.#initializePinnedTabs(init);
if (init) {
this._hasFinishedLoading = true;
}
})();
}
async _initializePinsCache() {
async #initializePinsCache() {
try {
// Get pin data
const pins = await ZenPinnedTabsStorage.getPins();
// Enhance pins with favicons
const enhancedPins = await Promise.all(
this._pinsCache = await Promise.all(
pins.map(async (pin) => {
try {
if (pin.isGroup) {
return pin; // Skip groups for now
}
const image = await this.getFaviconAsBase64(Services.io.newURI(pin.url));
return {
...pin,
@@ -170,12 +177,6 @@
}
})
);
this._pinsCache = enhancedPins.sort((a, b) => {
if (!a.workspaceUuid && b.workspaceUuid) return -1;
if (a.workspaceUuid && !b.workspaceUuid) return 1;
return 0;
});
} catch (ex) {
console.error('Failed to initialize pins cache:', ex);
this._pinsCache = [];
@@ -185,7 +186,7 @@
return this._pinsCache;
}
async _initializePinnedTabs(init = false) {
async #initializePinnedTabs(init = false) {
const pins = this._pinsCache;
if (!pins?.length || !init) {
return;
@@ -215,6 +216,17 @@
}
}
for (const group of gZenWorkspaces.allTabGroups) {
const pinId = group.getAttribute('zen-pin-id');
if (!pinId) {
continue;
}
if (pinsToCreate.has(pinId)) {
// This is a valid pinned group that matches a pin
pinsToCreate.delete(pinId);
}
}
// Second pass: For every existing tab, update its label
// and set 'zen-has-static-label' attribute if it's been edited
for (let pin of pins) {
@@ -230,6 +242,8 @@
}
}
const groups = new Map();
// Third pass: create new tabs for pins that don't have tabs
for (let pin of pins) {
try {
@@ -237,6 +251,21 @@
continue; // Skip pins that already have tabs
}
if (pin.isGroup) {
const group = gZenFolders.createFolder([], {
label: pin.title,
collapsed: pin.isFolderCollapsed,
initialPinId: pin.uuid,
workspaceId: pin.workspaceUuid,
insertBefore:
groups.get(pin.parentUuid)?.querySelector('.tab-group-container')?.lastChild ||
null,
});
gZenFolders.setFolderUserIcon(group, pin.folderIcon);
groups.set(pin.uuid, group);
continue;
}
let params = {
skipAnimation: true,
allowInheritPrincipal: false,
@@ -294,18 +323,26 @@
this.log(`Created new pinned tab for pin ${pin.uuid} (isEssential: ${pin.isEssential})`);
gBrowser.pinTab(newTab);
if (!pin.isEssential) {
const container = gZenWorkspaces.workspaceElement(
pin.workspaceUuid
)?.pinnedTabsContainer;
if (container) {
container.insertBefore(newTab, container.lastChild);
}
} else {
gZenWorkspaces.getEssentialsSection(pin.containerTabId).appendChild(newTab);
}
gBrowser.tabContainer._invalidateCachedTabs();
newTab.initialize();
if (pin.parentUuid) {
const parentGroup = groups.get(pin.parentUuid);
if (parentGroup) {
parentGroup.querySelector('.tab-group-container').appendChild(newTab);
}
} else {
if (!pin.isEssential) {
const container = gZenWorkspaces.workspaceElement(
pin.workspaceUuid
)?.pinnedTabsContainer;
if (container) {
container.insertBefore(newTab, container.lastChild);
}
} else {
gZenWorkspaces.getEssentialsSection(pin.containerTabId).appendChild(newTab);
}
}
} catch (ex) {
console.error('Failed to initialize pinned tabs:', ex);
}
@@ -332,7 +369,29 @@
}
break;
case 'TabMove':
this._onTabMove(tab);
this.#onTabMove(tab);
break;
case 'TabGroupCreate':
this.#onTabGroupCreate(event);
break;
case 'TabGroupRemoved':
this.#onTabGroupRemoved(event);
break;
case 'TabGroupMoved':
this.#onTabGroupMoved(event);
break;
case 'ZenFolderRenamed':
case 'ZenFolderIconChanged':
case 'TabGroupCollapse':
case 'TabGroupExpand':
case 'ZenFolderChangedWorkspace':
this.#updateGroupInfo(event.originalTarget);
break;
case 'TabGrouped':
this.#onTabGrouped(event);
break;
case 'TabUngrouped':
this.#onTabUngrouped(event);
break;
default:
console.warn('ZenPinnedTabManager: Unhandled tab event', action);
@@ -340,15 +399,123 @@
}
}
async _onTabMove(tab) {
async #onTabGroupCreate(event) {
const group = event.originalTarget;
if (!group.isZenFolder) {
return;
}
if (group.hasAttribute('zen-pin-id')) {
return; // Group already exists in storage
}
const workspaceId = group.getAttribute('zen-workspace-id');
let id = await ZenPinnedTabsStorage.createGroup(
group.name,
group.iconURL,
group.collapsed,
workspaceId,
group.getAttribute('zen-pin-id'),
group.labelElement.elementIndex
);
group.setAttribute('zen-pin-id', id);
await this.refreshPinnedTabs();
}
async #onTabGrouped(event) {
const tab = event.detail;
const group = tab.group;
if (!group.isZenFolder) {
return;
}
const pinId = group.getAttribute('zen-pin-id');
const tabPinId = tab.getAttribute('zen-pin-id');
const tabPin = this._pinsCache?.find((p) => p.uuid === tabPinId);
if (!tabPin) {
return;
}
ZenPinnedTabsStorage.addTabToGroup(tabPinId, pinId, /* position */ tab._tPos);
}
async #onTabUngrouped(event) {
const tab = event.detail;
const group = tab.group;
if (!group?.isZenFolder) {
return;
}
const tabPinId = tab.getAttribute('zen-pin-id');
const tabPin = this._pinsCache?.find((p) => p.uuid === tabPinId);
if (!tabPin) {
return;
}
ZenPinnedTabsStorage.removeTabFromGroup(tabPinId, /* position */ tab._tPos);
}
async #updateGroupInfo(group) {
if (!group?.isZenFolder) {
return;
}
const pinId = group.getAttribute('zen-pin-id');
const groupPin = this._pinsCache?.find((p) => p.uuid === pinId);
if (groupPin) {
groupPin.title = group.name;
groupPin.folderIcon = group.iconURL;
groupPin.isFolderCollapsed = group.collapsed;
groupPin.position = group.labelElement.elementIndex;
groupPin.parentUuid = group.group?.getAttribute('zen-pin-id') || null;
groupPin.workspaceUuid = group.getAttribute('zen-workspace-id') || null;
await this.savePin(groupPin);
for (const item of group.allItems) {
if (gBrowser.isTabGroup(item)) {
await this.#updateGroupInfo(item);
} else {
await this.#onTabMove(item);
}
}
}
}
async #onTabGroupRemoved(event) {
const group = event.originalTarget;
if (!group.isZenFolder) {
return;
}
await ZenPinnedTabsStorage.removePin(group.getAttribute('zen-pin-id'));
group.removeAttribute('zen-pin-id');
}
async #onTabGroupMoved(event) {
const group = event.originalTarget;
if (!group.isZenFolder) {
return;
}
const newIndex = group.labelElement.elementIndex;
const pinId = group.getAttribute('zen-pin-id');
if (!pinId) {
return;
}
for (const tab of group.tabs) {
if (tab.pinned && tab.getAttribute('zen-pin-id') === pinId) {
const pin = this._pinsCache.find((p) => p.uuid === pinId);
if (pin) {
pin.position = tab._tPos;
await this.savePin(pin, false);
}
break;
}
}
const groupPin = this._pinsCache?.find((p) => p.uuid === pinId);
if (groupPin) {
groupPin.position = newIndex;
groupPin.parentUuid = group.group?.getAttribute('zen-pin-id');
await this.savePin(groupPin);
}
}
async #onTabMove(tab) {
if (!tab.pinned || !this._pinsCache) {
return;
}
// Recollect pinned tabs and essentials after a tab move
tab.position = tab._tPos;
for (let otherTab of gBrowser.tabs) {
for (let otherTab of [...gBrowser.tabs, ...gBrowser.tabGroups]) {
if (
otherTab.pinned &&
otherTab.getAttribute('zen-pin-id') !== tab.getAttribute('zen-pin-id')
@@ -360,6 +527,8 @@
continue;
}
actualPin.position = otherTab._tPos;
actualPin.workspaceUuid = otherTab.getAttribute('zen-workspace-id');
actualPin.parentUuid = otherTab.group?.getAttribute('zen-pin-id') || null;
await this.savePin(actualPin, false);
}
}
@@ -369,8 +538,10 @@
if (!actualPin) {
return;
}
actualPin.position = tab.position;
actualPin.position = tab._tPos;
actualPin.isEssential = tab.hasAttribute('zen-essential');
actualPin.parentUuid = tab.group?.getAttribute('zen-pin-id') || null;
actualPin.workspaceUuid = tab.getAttribute('zen-workspace-id') || null;
// There was a bug where the title and hasStaticLabel attribute were not being set
// This is a workaround to fix that
@@ -433,7 +604,11 @@
}
async _setPinnedAttributes(tab) {
if (tab.hasAttribute('zen-pin-id') || !this._hasFinishedLoading) {
if (
tab.hasAttribute('zen-pin-id') ||
!this._hasFinishedLoading ||
tab.hasAttribute('zen-empty-tab')
) {
return;
}
@@ -456,6 +631,7 @@
containerTabId: userContextId ? parseInt(userContextId, 10) : 0,
workspaceUuid: tab.getAttribute('zen-workspace-id'),
isEssential: tab.getAttribute('zen-essential') === 'true',
parentUuid: tab.group?.getAttribute('zen-pin-id') || null,
});
tab.setAttribute('zen-pin-id', uuid);
@@ -514,12 +690,11 @@
}
async savePin(pin, notifyObservers = true) {
await ZenPinnedTabsStorage.savePin(pin, notifyObservers);
// Update the cache
const existingPin = this._pinsCache.find((p) => p.uuid === pin.uuid);
if (existingPin) {
Object.assign(existingPin, pin);
}
await ZenPinnedTabsStorage.savePin(pin, notifyObservers);
}
async _onCloseTabShortcut(
@@ -683,7 +858,7 @@
if (tab.selected) {
gZenWorkspaces.switchTabIfNeeded(tab);
}
this._onTabMove(tab);
this.#onTabMove(tab);
this.onTabIconChanged(tab);
// Dispatch the event to update the UI
@@ -714,7 +889,7 @@
const pinContainer = gZenWorkspaces.pinnedTabsContainer;
pinContainer.prepend(tab);
gBrowser.tabContainer._invalidateCachedTabs();
this._onTabMove(tab);
this.#onTabMove(tab);
}
// Dispatch the event to update the UI
@@ -798,11 +973,13 @@
event.target.closest('.zen-current-workspace-indicator');
const essentialTabsTarget = event.target.closest('.zen-essentials-container');
const tabsTarget = event.target.closest('.zen-workspace-normal-tabs-section');
// TODO: Solve the issue of adding a tab between two groups
// Remove group labels from the moving tabs and replace it
// with the sub tabs
for (let i = 0; i < movingTabs.length; i++) {
const draggedTab = movingTabs[i];
if (draggedTab.classList.contains('tab-group-label')) {
if (gBrowser.isTabGroupLabel(draggedTab)) {
const group = draggedTab.group;
// remove label and add sub tabs to moving tabs
if (group) {
@@ -828,10 +1005,7 @@
}
// Check for essentials container
else if (essentialTabsTarget) {
if (
!draggedTab.hasAttribute('zen-essential') &&
!draggedTab?.group?.hasAttribute('split-view-group')
) {
if (!draggedTab.hasAttribute('zen-essential') && !draggedTab?.group) {
moved = true;
isVertical = false;
hasActuallyMoved = this.addToEssentials(draggedTab);
@@ -839,7 +1013,11 @@
}
// Check for normal tabs container
else if (tabsTarget || event.target.id === 'zen-tabs-wrapper') {
if (draggedTab.pinned && !draggedTab.hasAttribute('zen-essential')) {
if (
draggedTab.pinned &&
!draggedTab.hasAttribute('zen-essential') &&
!draggedTab?.group?.isZenFolder
) {
gBrowser.unpinTab(draggedTab);
moved = true;
isRegularTabs = true;
@@ -857,19 +1035,25 @@
// If the tab was moved, adjust its position relative to the target tab
if (hasActuallyMoved) {
const targetTab = event.target.closest('.tabbrowser-tab');
if (targetTab) {
const rect = targetTab.getBoundingClientRect();
let elementIndex = targetTab.elementIndex;
const targetFolder = event.target.closest('zen-folder');
let targetElem = targetTab || targetFolder?.labelElement;
if (targetElem?.group?.activeGroups?.length > 0) {
const activeGroup = targetElem.group.activeGroups.at(-1);
targetElem = activeGroup.labelElement;
}
if (targetElem) {
const rect = targetElem.getBoundingClientRect();
let elementIndex = targetElem.elementIndex;
if (isVertical || !this.expandedSidebarMode) {
const middleY = targetTab.screenY + rect.height / 2;
const middleY = targetElem.screenY + rect.height / 2;
if (!isRegularTabs && event.screenY > middleY) {
elementIndex++;
} else if (isRegularTabs && event.screenY < middleY) {
elementIndex--;
}
} else {
const middleX = targetTab.screenX + rect.width / 2;
const middleX = targetElem.screenX + rect.width / 2;
if (event.screenX > middleX) {
elementIndex++;
}
@@ -878,7 +1062,11 @@
if (tabsTarget === gBrowser.tabs.at(-1)) {
elementIndex++;
}
gBrowser.moveTabTo(draggedTab, { elementIndex, forceUngrouped: true });
gBrowser.moveTabTo(draggedTab, {
elementIndex,
forceUngrouped: targetElem?.group?.collapsed !== false,
});
}
}
}
@@ -931,13 +1119,15 @@
tab.style.setProperty('--zen-original-tab-icon', `url(${pin.iconUrl.spec})`);
}
removeTabContainersDragoverClass() {
removeTabContainersDragoverClass(hideIndicator = true) {
if (this._dragIndicator) {
Services.zen.playHapticFeedback();
}
this.dragIndicator.remove();
this._dragIndicator = null;
gZenWorkspaces.activeWorkspaceIndicator?.removeAttribute('open');
if (hideIndicator) {
gZenWorkspaces.activeWorkspaceIndicator?.removeAttribute('open');
}
}
get dragIndicator() {
@@ -1003,6 +1193,15 @@
if (!this.enabled) {
return;
}
if (
gBrowser.isTabGroupLabel(draggedTab) &&
!draggedTab?.group?.hasAttribute('split-view-group')
) {
// If the target is a tab group label, we don't want to apply the dragover class
this.removeTabContainersDragoverClass();
return;
}
const folderTarget = event.target.closest('zen-folder');
const pinnedTabsTarget = event.target.closest('.zen-workspace-pinned-tabs-section');
const essentialTabsTarget = event.target.closest('.zen-essentials-container');
const tabsTarget = event.target.closest('.zen-workspace-normal-tabs-section');
@@ -1011,25 +1210,59 @@
draggedTab = draggedTab?.group?.hasAttribute('split-view-group')
? draggedTab.group
: draggedTab;
if (event.target.closest('.zen-current-workspace-indicator')) {
this.removeTabContainersDragoverClass();
const isHoveringIndicator = !!event.target.closest('.zen-current-workspace-indicator');
if (isHoveringIndicator) {
this.removeTabContainersDragoverClass(false);
gZenWorkspaces.activeWorkspaceIndicator?.setAttribute('open', true);
} else {
gZenWorkspaces.activeWorkspaceIndicator?.removeAttribute('open');
}
// If there's no valid target tab, nothing to do
if (!targetTab) {
return;
if (draggedTab) {
gZenFolders.ungroupTabFromActiveGroups(draggedTab);
}
let shouldAddDragOverElement = false;
let isVertical = this.expandedSidebarMode;
// Decide whether we should show a dragover class for the given target
if (pinnedTabsTarget) {
if (!draggedTab.pinned || draggedTab.hasAttribute('zen-essential')) {
if (folderTarget && (!draggedTab.pinned || draggedTab.hasAttribute('zen-essential'))) {
shouldAddDragOverElement = true;
const isCollapsed = folderTarget.collapsed;
let groupElem = isCollapsed
? [folderTarget]
: folderTarget.childGroupsAndTabs
.filter((tab) => !tab.hasAttribute('zen-empty-tab'))
.map((tab) => {
if (gBrowser.isTabGroupLabel(tab) || tab.group.hasAttribute('split-view-group')) {
return tab.group;
}
return tab;
});
let newTarget = isCollapsed ? groupElem[0] : groupElem.at(-1);
for (const elem of groupElem) {
const rect = elem.getBoundingClientRect();
if (event.clientY < rect.top + rect.height / 2) {
newTarget = elem;
break;
}
}
targetTab = newTarget;
} else if (pinnedTabsTarget) {
if (draggedTab.hasAttribute('zen-essential')) {
shouldAddDragOverElement = true;
} else if (!draggedTab.pinned) {
if (draggedTab._dragData?.screenY) {
draggedTab._dragData['screenY'] = event.screenY + 10;
const tabs = draggedTab._dragData.movingTabs || [draggedTab];
for (const tab of tabs) {
gBrowser.pinTab(tab);
}
Services.zen.playHapticFeedback();
} else {
shouldAddDragOverElement = true;
}
}
} else if (essentialTabsTarget) {
if (!draggedTab.hasAttribute('zen-essential') && this.canEssentialBeAdded(draggedTab)) {
@@ -1037,13 +1270,24 @@
isVertical = false;
}
} else if (tabsTarget) {
if (draggedTab.pinned || draggedTab.hasAttribute('zen-essential')) {
if (draggedTab.hasAttribute('zen-essential')) {
shouldAddDragOverElement = true;
} else if (draggedTab.pinned) {
if (draggedTab._dragData?.screenY) {
draggedTab._dragData['screenY'] = event.screenY + 10;
const tabs = draggedTab._dragData.movingTabs || [draggedTab];
for (const tab of tabs) {
gBrowser.unpinTab(tab);
}
Services.zen.playHapticFeedback();
} else {
shouldAddDragOverElement = true;
}
}
}
if (!shouldAddDragOverElement) {
this.removeTabContainersDragoverClass();
if (!shouldAddDragOverElement || (!targetTab && !folderTarget)) {
this.removeTabContainersDragoverClass(!isHoveringIndicator);
return;
}

View File

@@ -20,10 +20,8 @@ var ZenPinnedTabsStorage = {
position INTEGER NOT NULL DEFAULT 0,
is_essential BOOLEAN NOT NULL DEFAULT 0,
is_group BOOLEAN NOT NULL DEFAULT 0,
parent_uuid TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY (parent_uuid) REFERENCES zen_pins(uuid) ON DELETE SET NULL
updated_at INTEGER NOT NULL
)
`);
@@ -37,19 +35,15 @@ var ZenPinnedTabsStorage = {
}
};
// Add edited_title column if it doesn't exist
await addColumnIfNotExists('edited_title', 'BOOLEAN NOT NULL DEFAULT 0');
await addColumnIfNotExists('is_folder_collapsed', 'BOOLEAN NOT NULL DEFAULT 0');
await addColumnIfNotExists('folder_icon', 'TEXT DEFAULT NULL');
await addColumnIfNotExists('folder_parent_uuid', 'TEXT DEFAULT NULL');
// Create indices
await db.execute(`
CREATE INDEX IF NOT EXISTS idx_zen_pins_uuid ON zen_pins(uuid)
`);
await db.execute(`
CREATE INDEX IF NOT EXISTS idx_zen_pins_parent_uuid ON zen_pins(parent_uuid)
`);
// Create the changes tracking table if it doesn't exist
await db.execute(`
CREATE TABLE IF NOT EXISTS zen_pins_changes (
uuid TEXT PRIMARY KEY,
@@ -57,7 +51,6 @@ var ZenPinnedTabsStorage = {
)
`);
// Create an index on the uuid column for changes tracking table
await db.execute(`
CREATE INDEX IF NOT EXISTS idx_zen_pins_changes_uuid ON zen_pins_changes(uuid)
`);
@@ -96,9 +89,9 @@ var ZenPinnedTabsStorage = {
`
SELECT MAX("position") as max_position
FROM zen_pins
WHERE COALESCE(parent_uuid, '') = COALESCE(:parent_uuid, '')
WHERE COALESCE(folder_parent_uuid, '') = COALESCE(:folder_parent_uuid, '')
`,
{ parent_uuid: pin.parentUuid || null }
{ folder_parent_uuid: pin.parentUuid || null }
);
const maxPosition = maxPositionResult[0].getResultByName('max_position') || 0;
newPosition = maxPosition + 1000;
@@ -109,27 +102,29 @@ var ZenPinnedTabsStorage = {
`
INSERT OR REPLACE INTO zen_pins (
uuid, title, url, container_id, workspace_uuid, position,
is_essential, is_group, parent_uuid, edited_title, created_at,
updated_at
is_essential, is_group, folder_parent_uuid, edited_title, created_at,
updated_at, is_folder_collapsed, folder_icon
) VALUES (
:uuid, :title, :url, :container_id, :workspace_uuid, :position,
:is_essential, :is_group, :parent_uuid, :edited_title,
:is_essential, :is_group, :folder_parent_uuid, :edited_title,
COALESCE((SELECT created_at FROM zen_pins WHERE uuid = :uuid), :now),
:now
:now, :is_folder_collapsed, :folder_icon
)
`,
{
uuid: pin.uuid,
title: pin.title,
url: pin.isGroup ? null : pin.url,
url: pin.isGroup ? '' : pin.url,
container_id: pin.containerTabId || null,
workspace_uuid: pin.workspaceUuid || null,
position: newPosition,
is_essential: pin.isEssential || false,
is_group: pin.isGroup || false,
parent_uuid: pin.parentUuid || null,
folder_parent_uuid: pin.parentUuid || null,
edited_title: pin.editedTitle || false,
now,
folder_icon: pin.folderIcon || null,
is_folder_collapsed: pin.isFolderCollapsed || false,
}
);
@@ -158,7 +153,7 @@ var ZenPinnedTabsStorage = {
const db = await PlacesUtils.promiseDBConnection();
const rows = await db.executeCached(`
SELECT * FROM zen_pins
ORDER BY parent_uuid NULLS FIRST, position ASC
ORDER BY position ASC
`);
return rows.map((row) => ({
uuid: row.getResultByName('uuid'),
@@ -169,34 +164,228 @@ var ZenPinnedTabsStorage = {
position: row.getResultByName('position'),
isEssential: Boolean(row.getResultByName('is_essential')),
isGroup: Boolean(row.getResultByName('is_group')),
parentUuid: row.getResultByName('parent_uuid'),
parentUuid: row.getResultByName('folder_parent_uuid'),
editedTitle: Boolean(row.getResultByName('edited_title')),
folderIcon: row.getResultByName('folder_icon'),
isFolderCollapsed: Boolean(row.getResultByName('is_folder_collapsed')),
}));
},
async getGroupChildren(groupUuid) {
const db = await PlacesUtils.promiseDBConnection();
const rows = await db.executeCached(
`
SELECT * FROM zen_pins
WHERE parent_uuid = :groupUuid
ORDER BY position ASC
`,
{ groupUuid }
/**
* Create a new group
* @param {string} title - The title of the group
* @param {string} workspaceUuid - The workspace UUID (optional)
* @param {string} parentUuid - The parent group UUID (optional, null for root level)
* @param {number} position - The position of the group (optional, will auto-calculate if not provided)
* @param {boolean} notifyObservers - Whether to notify observers (default: true)
* @returns {Promise<string>} The UUID of the created group
*/
async createGroup(
title,
icon = null,
isCollapsed = false,
workspaceUuid = null,
parentUuid = null,
position = null,
notifyObservers = true
) {
if (!title || typeof title !== 'string') {
throw new Error('Group title is required and must be a string');
}
const groupUuid = gZenUIManager.generateUuidv4();
const groupPin = {
uuid: groupUuid,
title,
folderIcon: icon || null,
isFolderCollapsed: isCollapsed || false,
workspaceUuid,
parentUuid,
position,
isGroup: true,
isEssential: false,
editedTitle: true, // Group titles are always considered edited
};
await this.savePin(groupPin, notifyObservers);
return groupUuid;
},
/**
* Add an existing tab/pin to a group
* @param {string} tabUuid - The UUID of the tab to add to the group
* @param {string} groupUuid - The UUID of the target group
* @param {number} position - The position within the group (optional, will append if not provided)
* @param {boolean} notifyObservers - Whether to notify observers (default: true)
*/
async addTabToGroup(tabUuid, groupUuid, position = null, notifyObservers = true) {
if (!tabUuid || !groupUuid) {
throw new Error('Both tabUuid and groupUuid are required');
}
const changedUUIDs = new Set();
await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage.addTabToGroup', async (db) => {
await db.executeTransaction(async () => {
// Verify the group exists and is actually a group
const groupCheck = await db.execute(
`SELECT is_group FROM zen_pins WHERE uuid = :groupUuid`,
{ groupUuid }
);
if (groupCheck.length === 0) {
throw new Error(`Group with UUID ${groupUuid} does not exist`);
}
if (!groupCheck[0].getResultByName('is_group')) {
throw new Error(`Pin with UUID ${groupUuid} is not a group`);
}
const tabCheck = await db.execute(`SELECT uuid FROM zen_pins WHERE uuid = :tabUuid`, {
tabUuid,
});
if (tabCheck.length === 0) {
throw new Error(`Tab with UUID ${tabUuid} does not exist`);
}
const now = Date.now();
let newPosition;
if (position !== null && Number.isFinite(position)) {
newPosition = position;
} else {
// Get the maximum position within the group
const maxPositionResult = await db.execute(
`SELECT MAX("position") as max_position FROM zen_pins WHERE folder_parent_uuid = :groupUuid`,
{ groupUuid }
);
const maxPosition = maxPositionResult[0].getResultByName('max_position') || 0;
newPosition = maxPosition + 1000;
}
await db.execute(
`
UPDATE zen_pins
SET folder_parent_uuid = :groupUuid,
position = :newPosition,
updated_at = :now
WHERE uuid = :tabUuid
`,
{
tabUuid,
groupUuid,
newPosition,
now,
}
);
changedUUIDs.add(tabUuid);
await db.execute(
`
INSERT OR REPLACE INTO zen_pins_changes (uuid, timestamp)
VALUES (:uuid, :timestamp)
`,
{
uuid: tabUuid,
timestamp: Math.floor(now / 1000),
}
);
await this.updateLastChangeTimestamp(db);
});
});
if (notifyObservers) {
this._notifyPinsChanged('zen-pin-updated', Array.from(changedUUIDs));
}
},
/**
* Remove a tab from its group (move to root level)
* @param {string} tabUuid - The UUID of the tab to remove from its group
* @param {number} newPosition - The new position at root level (optional, will append if not provided)
* @param {boolean} notifyObservers - Whether to notify observers (default: true)
*/
async removeTabFromGroup(tabUuid, newPosition = null, notifyObservers = true) {
if (!tabUuid) {
throw new Error('tabUuid is required');
}
const changedUUIDs = new Set();
await PlacesUtils.withConnectionWrapper(
'ZenPinnedTabsStorage.removeTabFromGroup',
async (db) => {
await db.executeTransaction(async () => {
// Verify the tab exists and is in a group
const tabCheck = await db.execute(
`SELECT folder_parent_uuid FROM zen_pins WHERE uuid = :tabUuid`,
{ tabUuid }
);
if (tabCheck.length === 0) {
throw new Error(`Tab with UUID ${tabUuid} does not exist`);
}
if (!tabCheck[0].getResultByName('folder_parent_uuid')) {
throw new Error(`Tab with UUID ${tabUuid} is not in a group`);
}
const now = Date.now();
let finalPosition;
if (newPosition !== null && Number.isFinite(newPosition)) {
finalPosition = newPosition;
} else {
// Get the maximum position at root level (where folder_parent_uuid is null)
const maxPositionResult = await db.execute(
`SELECT MAX("position") as max_position FROM zen_pins WHERE folder_parent_uuid IS NULL`
);
const maxPosition = maxPositionResult[0].getResultByName('max_position') || 0;
finalPosition = maxPosition + 1000;
}
// Update the tab to be at root level
await db.execute(
`
UPDATE zen_pins
SET folder_parent_uuid = NULL,
position = :newPosition,
updated_at = :now
WHERE uuid = :tabUuid
`,
{
tabUuid,
newPosition: finalPosition,
now,
}
);
changedUUIDs.add(tabUuid);
// Record the change
await db.execute(
`
INSERT OR REPLACE INTO zen_pins_changes (uuid, timestamp)
VALUES (:uuid, :timestamp)
`,
{
uuid: tabUuid,
timestamp: Math.floor(now / 1000),
}
);
await this.updateLastChangeTimestamp(db);
});
}
);
return rows.map((row) => ({
uuid: row.getResultByName('uuid'),
title: row.getResultByName('title'),
url: row.getResultByName('url'),
containerTabId: row.getResultByName('container_id'),
workspaceUuid: row.getResultByName('workspace_uuid'),
position: row.getResultByName('position'),
isEssential: Boolean(row.getResultByName('is_essential')),
isGroup: Boolean(row.getResultByName('is_group')),
parentUuid: row.getResultByName('parent_uuid'),
editedTitle: Boolean(row.getResultByName('edited_title')),
}));
if (notifyObservers) {
this._notifyPinsChanged('zen-pin-updated', Array.from(changedUUIDs));
}
},
async removePin(uuid, notifyObservers = true) {
@@ -205,18 +394,18 @@ var ZenPinnedTabsStorage = {
await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage.removePin', async (db) => {
await db.executeTransaction(async () => {
// Get all child UUIDs first for change tracking
const children = await db.execute(`SELECT uuid FROM zen_pins WHERE parent_uuid = :uuid`, {
uuid,
});
const children = await db.execute(
`SELECT uuid FROM zen_pins WHERE folder_parent_uuid = :uuid`,
{
uuid,
}
);
// Add child UUIDs to changedUUIDs array
for (const child of children) {
changedUUIDs.push(child.getResultByName('uuid'));
}
// Delete all children in a single statement
await db.execute(`DELETE FROM zen_pins WHERE parent_uuid = :uuid`, { uuid });
// Delete the pin/group itself
await db.execute(`DELETE FROM zen_pins WHERE uuid = :uuid`, { uuid });

View File

@@ -0,0 +1,6 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
["browser_folder_create.js"]
["browser_folder_subfolder.js"]

View File

@@ -0,0 +1,27 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
add_task(async function test_Create_Folder() {
const tab = BrowserTestUtils.addTab(gBrowser, 'about:blank');
const folder = await gZenFolders.createFolder([tab], {
renameFolder: false,
label: 'test',
});
ok(folder, 'Folder created successfully');
Assert.equal(
folder.tabs.length,
2,
'Folder contains the tab and the empty tab created by Zen Folders'
);
ok(tab.pinned, 'Tab is pinned after folder creation');
Assert.equal(folder.label, 'test', 'Folder label is set correctly');
ok(!folder.collapsed, 'Folder is expanded after creation');
const removeEvent = BrowserTestUtils.waitForEvent(window, 'TabGroupRemoved');
folder.delete();
await removeEvent;
Assert.equal(folder.tabs.length, 0, 'Folder is empty after deletion');
ok(!folder.parentElement, 'Folder is removed from the DOM');
ok(tab.closing, 'Tab is closing after folder deletion');
});

View File

@@ -0,0 +1,29 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
add_task(async function test_Create_Folder() {
const tab = BrowserTestUtils.addTab(gBrowser, 'data:text/html,tab1');
const tab2 = BrowserTestUtils.addTab(gBrowser, 'data:text/html,tab2');
const subfolder = await gZenFolders.createFolder([tab], {
renameFolder: false,
label: 'subfolder',
});
const parent = await gZenFolders.createFolder([tab2], {
renameFolder: false,
label: 'parent',
});
parent.tabs[0].after(subfolder);
Assert.equal(parent, subfolder.group, 'Parent folder is set correctly');
Assert.equal(
subfolder.tabs.length,
2,
'Subfolder contains the tab and the empty tab created by Zen Folders'
);
Assert.equal(parent.tabs.length, 4, 'Parent folder contains the subfolder');
const removeEvent = BrowserTestUtils.waitForEvent(window, 'TabGroupRemoved');
parent.delete();
await removeEvent;
});

View File

@@ -5,6 +5,7 @@
BROWSER_CHROME_MANIFESTS += [
"compact_mode/browser.toml",
"container_essentials/browser.toml",
"folders/browser.toml",
"glance/browser.toml",
"pinned/browser.toml",
"split_view/browser.toml",

View File

@@ -130,16 +130,29 @@ add_task(async function test_Welcome_Steps() {
setTimeout(async () => {
Assert.greater(
gBrowser._numZenEssentials,
2,
'There should be more than 2 Zen Essentials after the welcome process'
3,
'There should be more than 3 Zen Essentials after the welcome process'
);
Assert.equal(
gBrowser.tabs.filter((tab) => tab.pinned && !tab.hasAttribute('zen-essential')).length,
2,
'There should be 2 pinned tabs after the welcome process'
3,
'There should be 3 pinned tabs after the welcome process'
);
gBrowser.selectedTab = selectedTab;
const groups = gBrowser.tabGroups;
Assert.equal(groups.length, 1, 'There should be one tab group after the welcome process');
const group = groups[0];
Assert.equal(
group.tabs.length,
3,
'The first tab group should have 3 tabs after the welcome process'
);
Assert.equal(
group.label,
'Zen Basics',
'The first tab group should be labeled "Zen Basics" after the welcome process'
);
for (const tab of gBrowser.tabs) {
if (tab.pinned) {
if (!tab.hasAttribute('zen-essential')) {
@@ -147,13 +160,14 @@ add_task(async function test_Welcome_Steps() {
tab.hasAttribute('zen-workspace-id'),
'Pinned tabs should have a zen-workspace-id attribute'
);
Assert.equal(tab.group, group, 'Pinned tabs should belong to the first tab group');
}
ok(tab.hasAttribute('zen-pin-id'), 'Pinned tabs should have a zen-pin-id attribute');
await BrowserTestUtils.removeTab(tab);
}
}
group.delete();
resolve();
}, 8000); // Wait for the transition to complete
}, 5000); // Wait for the transition to complete
});
ok(true, 'Welcome process completed successfully');
});

View File

@@ -297,6 +297,10 @@
tab.removeAttribute('pending'); // Make it appear loaded
gZenPinnedTabManager.addToEssentials(tab);
}
gZenFolders.createFolder(_tabsToPin, {
renameFolder: false,
label: 'Zen Basics',
});
}
async animHeart() {

View File

@@ -15,6 +15,7 @@
return [
'cmd_zenOpenWorkspacePanel',
'cmd_zenOpenWorkspaceCreation',
'cmd_zenOpenFolderCreation',
'cmd_zenToggleSidebar',
'cmd_newNavigatorTab',
'cmd_newNavigatorTabNoEvent',
@@ -200,7 +201,7 @@
async onCreateButtonCommand() {
const workspace = await gZenWorkspaces.getActiveWorkspace();
workspace.name = this.inputName.value.trim();
workspace.icon = this.inputIcon.label || undefined;
workspace.icon = this.inputIcon.image || this.inputIcon.label || undefined;
workspace.containerTabId = this.currentProfile;
await gZenWorkspaces.saveWorkspace(workspace);
@@ -220,7 +221,16 @@
gZenEmojiPicker
.open(event.target)
.then(async (emoji) => {
this.inputIcon.label = emoji || '';
const isSvg = emoji && emoji.endsWith('.svg');
if (isSvg) {
this.inputIcon.label = '';
this.inputIcon.image = emoji;
this.inputIcon.setAttribute('has-svg-icon', 'true');
} else {
this.inputIcon.image = '';
this.inputIcon.label = emoji || '';
this.inputIcon.removeAttribute('has-svg-icon');
}
})
.catch((error) => {
console.warn('Error changing workspace icon:', error);

View File

@@ -105,12 +105,22 @@
button.setAttribute('context', 'zenWorkspaceMoreActions');
const icon = document.createXULElement('label');
icon.setAttribute('class', 'zen-workspace-icon');
const isSvgIcon = workspace.icon && workspace.icon.endsWith('.svg');
if (gZenWorkspaces.workspaceHasIcon(workspace)) {
icon.textContent = workspace.icon;
if (isSvgIcon) {
const image = document.createElement('img');
image.src = workspace.icon;
image.classList.add('zen-workspace-icon');
button.appendChild(image);
} else {
icon.textContent = workspace.icon;
}
} else {
icon.setAttribute('no-icon', true);
}
button.appendChild(icon);
if (!isSvgIcon) {
button.appendChild(icon);
}
button.addEventListener('command', this);
return button;
}

View File

@@ -316,7 +316,7 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature {
_initializeEmptyTab() {
for (const tab of gBrowser.tabs) {
// Check if session store has an empty tab
if (tab.hasAttribute('zen-empty-tab')) {
if (tab.hasAttribute('zen-empty-tab') && !tab.pinned) {
this.log('Found existing empty tab from session store!');
this._emptyTab = tab;
return;
@@ -506,28 +506,35 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature {
const workspaceTabs = Array.from(tabs).filter(
(tab) => tab.getAttribute('zen-workspace-id') === workspace.uuid
);
let firstNormalTab = null;
for (let tab of workspaceTabs) {
if (tab.hasAttribute('zen-essential')) {
continue; // Ignore essentials as they need to be in their own section
let folders = new Set();
const getFolderRoot = (tab) => {
let root = tab?.group;
while (root?.group) {
root = root?.group;
}
return root || tab;
};
for (let i = workspaceTabs.length - 1; i >= 0; i--) {
let tab = workspaceTabs[i];
if (tab.hasAttribute('zen-essential')) continue; // Ignore essentials as they need to be in their own section
// remove tab from list
tabs.splice(tabs.indexOf(tab), 1);
tab = tab.group ?? tab;
if (tab.pinned) {
pinnedSection.insertBefore(tab, pinnedSection.lastChild);
} else {
if (!firstNormalTab) {
firstNormalTab = tab;
}
section.insertBefore(tab, section.lastChild);
if (gBrowser.isTabGroup(tab)) {
let rootGroup = getFolderRoot(tab);
if (folders.has(rootGroup)) continue;
folders.add(rootGroup);
tab = rootGroup;
}
if (tab.pinned) {
pinnedSection.insertBefore(tab, pinnedSection.firstChild);
} else {
section.insertBefore(tab, section.firstChild);
}
}
// Kind of a hacky fix, but for some reason the first normal tab in the list
// created by session restore is added the the last position of the tab list
// let's just prepend it to the section
if (firstNormalTab) {
section.insertBefore(firstNormalTab, section.firstChild);
}
}
@@ -912,7 +919,7 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature {
this._initializeEmptyTab();
await gZenPinnedTabManager.refreshPinnedTabs({ init: true });
await this.changeWorkspace(activeWorkspace, { onInit: true });
this._fixTabPositions();
this.#fixTabPositions();
this.onWindowResize();
this._resolveInitialized();
this._clearAnyZombieTabs(); // Dont call with await
@@ -1217,10 +1224,14 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature {
item.setAttribute('zen-workspace-id', workspace.uuid);
item.setAttribute('disabled', workspace.uuid === this.activeWorkspace);
let name = workspace.name;
if (workspace.icon && workspace.icon !== '') {
const iconIsSvg = workspace.icon && workspace.icon.endsWith('.svg');
if (workspace.icon && workspace.icon !== '' && !iconIsSvg) {
name = `${workspace.icon} ${name}`;
}
item.setAttribute('label', name);
if (iconIsSvg) {
item.setAttribute('image', workspace.icon);
}
item.addEventListener('command', (e) => {
this.changeWorkspaceWithID(e.target.closest('menuitem').getAttribute('zen-workspace-id'));
});
@@ -1303,10 +1314,7 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature {
if (this.workspaceHasIcon(workspace)) {
return workspace.icon;
}
if (typeof Intl.Segmenter !== 'undefined') {
return new Intl.Segmenter().segment(workspace.name).containing().segment.toUpperCase();
}
return Array.from(workspace.name)[0].toUpperCase();
return '';
}
get shouldShowContainers() {
@@ -1587,14 +1595,26 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature {
container.insertBefore(emptyTab, container.firstChild);
}
}
this._fixTabPositions();
this.#fixTabPositions();
}
_fixTabPositions() {
#fixTabPositions() {
// Fix tabs _tPos values relative to the actual order
const tabs = gBrowser.tabs;
for (let i = 0; i < tabs.length; i++) {
tabs[i]._tPos = i;
const usedGroups = new Set();
let i = 0;
const recurseFolder = (tab) => {
if (tab.group) {
recurseFolder(tab.group);
if (!usedGroups.has(tab.group.id)) {
usedGroups.add(tab.group.id);
tab.group._tPos = i++;
}
}
};
for (const tab of tabs) {
recurseFolder(tab);
tab._tPos = i++;
}
}
@@ -1623,7 +1643,7 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature {
}
);
} else {
window.requestAnimationFrame(() => {
requestAnimationFrame(() => {
workspaceElement.style.paddingTop = essentialsHeight + 'px';
});
}
@@ -1731,7 +1751,15 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature {
} else {
indicatorIcon.setAttribute('no-icon', 'true');
}
indicatorIcon.textContent = this.getWorkspaceIcon(currentWorkspace);
const icon = this.getWorkspaceIcon(currentWorkspace);
indicatorIcon.innerHTML = '';
if (icon?.endsWith('.svg')) {
const img = document.createElement('img');
img.src = icon;
indicatorIcon.appendChild(img);
} else {
indicatorIcon.textContent = icon;
}
indicatorName.textContent = currentWorkspace.name;
}
@@ -2317,6 +2345,9 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature {
}
}
this.registerPinnedResizeObserver();
await this.updateTabsContainers({
target: this.workspaceElement(workspaceData.uuid).pinnedTabsContainer,
});
let changed = extraTabs.length > 0;
if (changed) {
gBrowser.tabContainer._invalidateCachedTabs();
@@ -2506,7 +2537,7 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature {
workspace.containerTabId = userContextId + 0; // +0 to convert to number
await this.saveWorkspace(workspace);
await this._organizeWorkspaceStripLocations(this.getActiveWorkspaceFromCache(), true);
await gZenWorkspaces.updateTabsContainers();
await this.updateTabsContainers();
this.tabContainer._invalidateCachedTabs();
}
@@ -2713,7 +2744,7 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature {
if (glance) {
tabs.push(glance);
}
} else if (tab.tagName == 'tab-group') {
} else if (gBrowser.isTabGroup(tab)) {
for (const groupTab of tab.tabs) {
tabs.push(groupTab);
const glance = groupTab.querySelector('.tabbrowser-tab[glance-id]');
@@ -2731,7 +2762,7 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature {
get allTabGroups() {
if (!this._hasInitializedTabsStrip) {
let children = this.tabboxChildren;
return children.filter((node) => node.tagName == 'tab-group');
return children.filter((node) => gBrowser.isTabGroup(node));
}
const pinnedContainers = [];
const normalContainers = [];
@@ -2748,6 +2779,9 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature {
for (const tabGroup of container.querySelectorAll('tab-group')) {
tabGroups.push(tabGroup);
}
for (const tabGroup of container.querySelectorAll('zen-folder')) {
tabGroups.push(tabGroup);
}
}
return tabGroups;
}

View File

@@ -66,8 +66,15 @@ zen-workspace-creation {
appearance: none;
align-content: center;
padding: 0;
fill-opacity: 0.6;
& image {
position: absolute;
-moz-context-properties: fill-opacity, fill;
fill: currentColor;
}
&:not([has-svg-icon='true']) image {
display: none;
}
@@ -77,6 +84,10 @@ zen-workspace-creation {
justify-content: center;
}
&[has-svg-icon='true'] label {
display: none;
}
&::before {
border: 1px dashed light-dark(rgba(0, 0, 0, 0.5), rgba(255, 255, 255, 0.5));
border-radius: 4px;

View File

@@ -40,6 +40,7 @@
padding: 0 !important;
align-items: center;
position: relative;
fill-opacity: 0.6;
& .zen-workspace-icon {
pointer-events: none;
@@ -91,7 +92,7 @@
& toolbarbutton {
margin: 0;
background: transparent;
background: transparent;
}
/* Inlcude separately since ther'es a bug in the
@@ -188,6 +189,9 @@
height: 16px;
justify-content: center;
align-items: center;
fill-opacity: 0.6;
-moz-context-properties: fill-opacity, fill;
fill: currentColor;
&[zen-emoji-open='true']::before {
border: 1px dashed light-dark(rgba(0, 0, 0, .5), rgba(255, 255, 255, .5));

View File

@@ -69,6 +69,7 @@ export default [
'gInitialPages',
'isInitialPage',
'browserWindows',
'MozTabbrowserTabGroup',
'updateBookmarkToolbarVisibility',
'gNavigatorBundle',
'updateFxaToolbarMenu',