mirror of
https://github.com/zen-browser/desktop.git
synced 2025-09-05 19:08:18 +00:00
feat: Implement new workspace management, b=no-bug, c=common, workspaces
This commit is contained in:
2
l10n
2
l10n
Submodule l10n updated: 7730ce9a6d...751793cd1f
@@ -55,3 +55,4 @@
|
||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenGlanceManager.mjs"></script>
|
||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenMediaController.mjs"></script>
|
||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenDownloadAnimation.mjs"></script>
|
||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenEmojiPicker.mjs"></script>
|
||||
|
@@ -12,6 +12,8 @@
|
||||
content/browser/zen-components/ZenUIMigration.mjs (../../zen/common/ZenUIMigration.mjs)
|
||||
content/browser/zen-components/ZenCommonUtils.mjs (../../zen/common/ZenCommonUtils.mjs)
|
||||
content/browser/zen-components/ZenSessionStore.mjs (../../zen/common/ZenSessionStore.mjs)
|
||||
content/browser/zen-components/ZenEmojisData.min.mjs (../../zen/common/emojis/ZenEmojisData.min.mjs)
|
||||
content/browser/zen-components/ZenEmojiPicker.mjs (../../zen/common/emojis/ZenEmojiPicker.mjs)
|
||||
|
||||
content/browser/zen-styles/zen-theme.css (../../zen/common/styles/zen-theme.css)
|
||||
content/browser/zen-styles/zen-buttons.css (../../zen/common/styles/zen-buttons.css)
|
||||
|
@@ -38,6 +38,8 @@
|
||||
|
||||
<command id="cmd_zenCtxDeleteWorkspace" />
|
||||
<command id="cmd_zenChangeWorkspaceName" />
|
||||
<command id="cmd_zenChangeWorkspaceIcon" />
|
||||
<command id="cmd_zenOpenWorkspacePanel" />
|
||||
|
||||
<command id="cmd_zenPinnedTabReset" />
|
||||
<command id="cmd_zenPinnedTabResetNoTab" />
|
||||
|
@@ -77,8 +77,17 @@
|
||||
</panelmultiview>
|
||||
</panel>
|
||||
|
||||
<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" />
|
||||
<toolbarbutton id="PanelUI-zen-emojis-picker-none" class="toolbarbutton-1" />
|
||||
</hbox>
|
||||
<hbox id="PanelUI-zen-emojis-picker-list" />
|
||||
</panel>
|
||||
|
||||
<menupopup id="zenWorkspaceMoreActions">
|
||||
<menuitem id="context_zenEditWorkspace" data-l10n-id="zen-workspaces-panel-change-name" command="cmd_zenChangeWorkspaceName"/>
|
||||
<menuitem id="context_zenEditWorkspaceIcon" data-l10n-id="zen-workspaces-panel-change-icon" command="cmd_zenChangeWorkspaceIcon"/>
|
||||
<menuitem class="zenToolbarThemePicker"
|
||||
data-l10n-id="zen-workspaces-change-gradient"
|
||||
command="cmd_zenOpenZenThemePicker"/>
|
||||
@@ -91,5 +100,7 @@
|
||||
<menupopup />
|
||||
</menu>
|
||||
<menuseparator/>
|
||||
<menuitem id="context_zenOpenWorkspacePanel" data-l10n-id="zen-workspaces-panel-context-manage" command="cmd_zenOpenWorkspacePanel"/>
|
||||
<menuseparator/>
|
||||
<menuitem id="context_zenDeleteWorkspace" data-l10n-id="zen-workspaces-panel-context-delete" command="cmd_zenCtxDeleteWorkspace"/>
|
||||
</menupopup>
|
||||
|
@@ -39,6 +39,7 @@
|
||||
.close-icon,
|
||||
#zen-sidebar-web-panel-close,
|
||||
#zen-glance-sidebar-close,
|
||||
#PanelUI-zen-emojis-picker-none,
|
||||
.zen-theme-picker-custom-list-item-remove {
|
||||
list-style-image: url('close.svg') !important;
|
||||
}
|
||||
|
@@ -1105,15 +1105,17 @@ var gZenVerticalTabsManager = {
|
||||
`);
|
||||
label.after(container);
|
||||
}
|
||||
const containerHtml = isTab
|
||||
? this._tabEdited.querySelector('.tab-editor-container')
|
||||
: this._tabEdited.parentNode;
|
||||
const input = document.createElement('input');
|
||||
input.id = 'tab-label-input';
|
||||
input.value = isTab ? this._tabEdited.label : this._tabEdited.textContent;
|
||||
input.addEventListener('keydown', this.renameTabKeydown.bind(this));
|
||||
|
||||
containerHtml.appendChild(input);
|
||||
if (isTab) {
|
||||
const containerHtml = this._tabEdited.querySelector('.tab-editor-container');
|
||||
containerHtml.appendChild(input);
|
||||
} else {
|
||||
this._tabEdited.after(input);
|
||||
}
|
||||
input.focus();
|
||||
input.select();
|
||||
|
||||
|
139
src/zen/common/emojis/ZenEmojiPicker.mjs
Normal file
139
src/zen/common/emojis/ZenEmojiPicker.mjs
Normal file
@@ -0,0 +1,139 @@
|
||||
{
|
||||
class ZenEmojiPicker extends ZenDOMOperatedFeature {
|
||||
#panel;
|
||||
|
||||
#anchor;
|
||||
|
||||
#currentPromise = null;
|
||||
#currentPromiseResolve = null;
|
||||
#currentPromiseReject = null;
|
||||
|
||||
init() {
|
||||
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.searchInput.addEventListener('input', this);
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case 'popupshowing':
|
||||
this.#onPopupShowing(event);
|
||||
break;
|
||||
case 'popuphidden':
|
||||
this.#onPopupHidden(event);
|
||||
break;
|
||||
case 'command':
|
||||
if (event.target.id === 'PanelUI-zen-emojis-picker-none') {
|
||||
this.#selectEmoji(null);
|
||||
}
|
||||
break;
|
||||
case 'input':
|
||||
this.#onSearchInput(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
get #emojis() {
|
||||
if (this._emojis) {
|
||||
return this._emojis;
|
||||
}
|
||||
const lazy = {};
|
||||
Services.scriptloader.loadSubScript(
|
||||
'chrome://browser/content/zen-components/ZenEmojisData.min.mjs',
|
||||
lazy
|
||||
);
|
||||
this._emojis = lazy.ZenEmojisData;
|
||||
return this._emojis;
|
||||
}
|
||||
|
||||
get emojiList() {
|
||||
return document.getElementById('PanelUI-zen-emojis-picker-list');
|
||||
}
|
||||
|
||||
get searchInput() {
|
||||
return document.getElementById('PanelUI-zen-emojis-picker-search');
|
||||
}
|
||||
|
||||
#clearEmojis() {
|
||||
delete this._emojis;
|
||||
}
|
||||
|
||||
#onSearchInput(event) {
|
||||
const input = event.target;
|
||||
const value = input.value.trim().toLowerCase();
|
||||
// search for emojis.tags and order by emojis.order
|
||||
const filteredEmojis = this.#emojis
|
||||
.filter((emoji) => {
|
||||
return emoji.tags.some((tag) => tag.toLowerCase().includes(value));
|
||||
})
|
||||
.sort((a, b) => a.order - b.order);
|
||||
for (const button of this.emojiList.children) {
|
||||
const buttonEmoji = button.getAttribute('label');
|
||||
const emojiObject = filteredEmojis.find((emoji) => emoji.emoji === buttonEmoji);
|
||||
if (emojiObject) {
|
||||
button.hidden = !emojiObject.tags.some((tag) => tag.toLowerCase().includes(value));
|
||||
button.style.order = emojiObject.order;
|
||||
} else {
|
||||
button.hidden = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#onPopupShowing(event) {
|
||||
if (event.target !== this.#panel) return;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
#onPopupHidden(event) {
|
||||
if (event.target !== this.#panel) return;
|
||||
this.#clearEmojis();
|
||||
|
||||
const emojiList = this.emojiList;
|
||||
emojiList.innerHTML = '';
|
||||
|
||||
if (this.#currentPromiseReject) {
|
||||
this.#currentPromiseReject(new Error('Emoji picker closed without selection'));
|
||||
}
|
||||
|
||||
this.#currentPromise = null;
|
||||
this.#currentPromiseResolve = null;
|
||||
this.#currentPromiseReject = null;
|
||||
|
||||
this.#anchor.removeAttribute('zen-emoji-open');
|
||||
this.#anchor = null;
|
||||
}
|
||||
|
||||
#selectEmoji(emoji) {
|
||||
this.#currentPromiseResolve?.(emoji);
|
||||
this.#panel.hidePopup();
|
||||
}
|
||||
|
||||
open(anchor) {
|
||||
if (this.#currentPromise) {
|
||||
return null;
|
||||
}
|
||||
this.#currentPromise = new Promise((resolve, reject) => {
|
||||
this.#currentPromiseResolve = resolve;
|
||||
this.#currentPromiseReject = reject;
|
||||
});
|
||||
this.#anchor = anchor;
|
||||
this.#anchor.setAttribute('zen-emoji-open', 'true');
|
||||
this.#panel.openPopup(anchor, 'after_start', 0, 0, false, false);
|
||||
return this.#currentPromise;
|
||||
}
|
||||
}
|
||||
|
||||
window.gZenEmojiPicker = new ZenEmojiPicker();
|
||||
}
|
1
src/zen/common/emojis/ZenEmojisData.min.mjs
Normal file
1
src/zen/common/emojis/ZenEmojisData.min.mjs
Normal file
File diff suppressed because one or more lines are too long
@@ -35,3 +35,52 @@ body > #confetti {
|
||||
#PersonalToolbar:not([collapsed='true']) {
|
||||
min-height: 30px;
|
||||
}
|
||||
|
||||
/* Emojis picker */
|
||||
|
||||
#PanelUI-zen-emojis-picker {
|
||||
--panel-width: 250px;
|
||||
--panel-padding: 10px;
|
||||
|
||||
&::part(content) {
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
#PanelUI-zen-emojis-picker-header {
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#PanelUI-zen-emojis-picker-none label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#PanelUI-zen-emojis-picker-list {
|
||||
flex-wrap: wrap;
|
||||
max-height: 265px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
gap: 5px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(22px, 1fr));
|
||||
|
||||
.zen-emojis-picker-emoji {
|
||||
& image {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.zen-emojis-picker-emoji,
|
||||
#PanelUI-zen-emojis-picker-none {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
|
||||
&:hover {
|
||||
background-color: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1));
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -94,6 +94,12 @@ document.addEventListener(
|
||||
case 'cmd_zenChangeWorkspaceName':
|
||||
gZenVerticalTabsManager.renameTabStart(event);
|
||||
break;
|
||||
case 'cmd_zenChangeWorkspaceIcon':
|
||||
gZenWorkspaces.changeWorkspaceIcon();
|
||||
break;
|
||||
case 'cmd_zenOpenWorkspacePanel':
|
||||
gZenWorkspaces.openWorkspacesDialog(event);
|
||||
break;
|
||||
default:
|
||||
if (event.target.id.startsWith('cmd_zenWorkspaceSwitch')) {
|
||||
const index = parseInt(event.target.id.replace('cmd_zenWorkspaceSwitch', ''), 10) - 1;
|
||||
|
@@ -7,7 +7,7 @@
|
||||
return `
|
||||
<vbox class="zen-workspace-tabs-section zen-current-workspace-indicator" flex="1">
|
||||
<hbox class="zen-current-workspace-indicator-icon"></hbox>
|
||||
<hbox class="zen-current-workspace-indicator-name"></hbox>
|
||||
<hbox class="zen-current-workspace-indicator-name" flex="1"></hbox>
|
||||
<toolbarbutton class="toolbarbutton-1 chromeclass-toolbar-additional zen-workspaces-actions" context="zenWorkspaceMoreActions"></toolbarbutton>
|
||||
</vbox>
|
||||
<arrowscrollbox orient="vertical" class="workspace-arrowscrollbox">
|
||||
|
@@ -21,7 +21,7 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
_lastScrollTime = 0;
|
||||
|
||||
#workspaceCreationArgs = {};
|
||||
|
||||
|
||||
bookmarkMenus = [
|
||||
'PlacesToolbar',
|
||||
'bookmarks-menu-button',
|
||||
@@ -467,7 +467,6 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
workspaceWrapper.pinnedTabsContainer,
|
||||
tabs
|
||||
);
|
||||
this.initIndicatorContextMenu(workspaceWrapper.indicator);
|
||||
resolve();
|
||||
},
|
||||
{ once: true }
|
||||
@@ -991,13 +990,32 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
}
|
||||
}
|
||||
|
||||
initIndicatorContextMenu(indicator) {
|
||||
const th = (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.openWorkspacesDialog(event);
|
||||
};
|
||||
indicator.addEventListener('contextmenu', th);
|
||||
changeWorkspaceIcon() {
|
||||
const anchor = this.activeWorkspaceIndicator?.querySelector(
|
||||
'.zen-current-workspace-indicator-icon'
|
||||
);
|
||||
if (!anchor) {
|
||||
return;
|
||||
}
|
||||
const hasNoIcon = anchor.hasAttribute('no-icon');
|
||||
anchor.removeAttribute('no-icon');
|
||||
gZenEmojiPicker
|
||||
.open(anchor)
|
||||
.then(async (emoji) => {
|
||||
const workspace = this.getActiveWorkspaceFromCache();
|
||||
if (!workspace) {
|
||||
console.warn('No active workspace found to change icon');
|
||||
return;
|
||||
}
|
||||
workspace.icon = emoji;
|
||||
await this.saveWorkspace(workspace);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn('Error changing workspace icon:', error);
|
||||
if (hasNoIcon) {
|
||||
anchor.setAttribute('no-icon', 'true');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
shouldCloseWindow() {
|
||||
@@ -1525,7 +1543,7 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
if (!this.workspaceEnabled || this.isPrivateWindow) {
|
||||
return;
|
||||
}
|
||||
let target = event.target.closest('.zen-current-workspace-indicator');
|
||||
let target = this.activeWorkspaceIndicator || event.target;
|
||||
let panel = document.getElementById('PanelUI-zen-workspaces');
|
||||
await this._propagateWorkspaceData({
|
||||
ignoreStrip: true,
|
||||
@@ -1797,10 +1815,11 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||
|
||||
if (this.workspaceHasIcon(currentWorkspace)) {
|
||||
indicatorIcon.removeAttribute('no-icon');
|
||||
indicatorIcon.textContent = this.getWorkspaceIcon(currentWorkspace);
|
||||
} else {
|
||||
indicatorIcon.setAttribute('no-icon', 'true');
|
||||
indicatorIcon.textContent = '';
|
||||
}
|
||||
indicatorIcon.textContent = this.getWorkspaceIcon(currentWorkspace);
|
||||
indicatorName.textContent = currentWorkspace.name;
|
||||
}
|
||||
|
||||
|
@@ -340,7 +340,7 @@
|
||||
|
||||
/* Mark workspaces indicator */
|
||||
.zen-current-workspace-indicator {
|
||||
padding: calc(4px + var(--tab-inline-padding) + var(--zen-toolbox-padding));
|
||||
padding: calc(4px + var(--tab-inline-padding) + var(--zen-toolbox-padding));
|
||||
font-weight: 600;
|
||||
position: relative;
|
||||
max-height: var(--zen-workspace-indicator-height);
|
||||
@@ -352,6 +352,7 @@ padding: calc(4px + var(--tab-inline-padding) + var(--zen-toolbox-padding));
|
||||
width: 100%;
|
||||
font-size: small;
|
||||
padding-right: 10px;
|
||||
-moz-window-dragging: no-drag;
|
||||
|
||||
&::before {
|
||||
border-radius: var(--border-radius-medium);
|
||||
@@ -376,8 +377,23 @@ padding: calc(4px + var(--tab-inline-padding) + var(--zen-toolbox-padding));
|
||||
}
|
||||
|
||||
& .zen-current-workspace-indicator-icon {
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
position: relative;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&[zen-emoji-open='true']::before {
|
||||
border: 1px dashed light-dark(rgba(0, 0, 0, .5), rgba(255, 255, 255, .5));
|
||||
border-radius: 6px;
|
||||
width: calc(100% + 6px);
|
||||
height: calc(100% + 6px);
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
left: -4px;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.zen-current-workspace-indicator-name {
|
||||
@@ -386,16 +402,20 @@ padding: calc(4px + var(--tab-inline-padding) + var(--zen-toolbox-padding));
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: text;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.zen-workspaces-actions {
|
||||
.zen-workspaces-actions {
|
||||
--toolbarbutton-inner-padding: 4px;
|
||||
margin-left: auto !important;
|
||||
opacity: 0;
|
||||
visibility: collapse;
|
||||
transition: opacity 0.1s;
|
||||
order: 5;
|
||||
|
||||
:root[zen-renaming-tab='true'] & {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
:root:not([zen-private-window]) &:hover .zen-workspaces-actions,
|
||||
|
Reference in New Issue
Block a user