mirror of
https://github.com/zen-browser/desktop.git
synced 2025-07-08 01:19:59 +02:00
chore: Continued working on containerized essentials, b=(no-bug), c=tabs, workspaces
This commit is contained in:
parent
8136387a75
commit
47fbae7e0d
3 changed files with 456 additions and 28 deletions
390
src/zen/@types/zen.d.ts
vendored
390
src/zen/@types/zen.d.ts
vendored
|
@ -49,3 +49,393 @@ interface nsIXPCComponents extends nsISupports {
|
||||||
readonly Constructor: (aClass: any, aIID: any, aFlags: any) => any;
|
readonly Constructor: (aClass: any, aIID: any, aFlags: any) => any;
|
||||||
returnCode: any;
|
returnCode: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TS-TODO - Needs typing.
|
||||||
|
*
|
||||||
|
* This file contains type stubs for loading things from Gecko. All of these
|
||||||
|
* types should be used in the correct places eventually.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Namespace anything that has its types mocked out here. These definitions are
|
||||||
|
* only "good enough" to get the type checking to pass in this directory.
|
||||||
|
* Eventually some more structured solution should be found. This namespace is
|
||||||
|
* global and makes sure that all the definitions inside do not clash with
|
||||||
|
* naming.
|
||||||
|
*/
|
||||||
|
declare namespace MockedExports {
|
||||||
|
/**
|
||||||
|
* This interface teaches ChromeUtils.importESModule how to find modules.
|
||||||
|
*/
|
||||||
|
interface KnownModules {
|
||||||
|
Services: typeof import('Services');
|
||||||
|
'resource://gre/modules/AppConstants.sys.mjs': typeof import('resource://gre/modules/AppConstants.sys.mjs');
|
||||||
|
'resource:///modules/CustomizableUI.sys.mjs': typeof import('resource:///modules/CustomizableUI.sys.mjs');
|
||||||
|
'resource:///modules/CustomizableWidgets.sys.mjs': typeof import('resource:///modules/CustomizableWidgets.sys.mjs');
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChromeUtils {
|
||||||
|
/**
|
||||||
|
* This function reads the KnownModules and resolves which import to use.
|
||||||
|
* If you are getting the TS2345 error:
|
||||||
|
*
|
||||||
|
* Argument of type '"resource:///.../file.sys.mjs"' is not assignable to
|
||||||
|
* parameter of type
|
||||||
|
*
|
||||||
|
* Then add the file path to the KnownModules above.
|
||||||
|
*/
|
||||||
|
importESModule: <S extends keyof KnownModules>(module: S) => KnownModules[S];
|
||||||
|
defineESModuleGetters: (target: any, mappings: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MessageManager {
|
||||||
|
loadFrameScript(url: string, flag: boolean): void;
|
||||||
|
sendAsyncMessage: (event: string, data: any) => void;
|
||||||
|
addMessageListener: (event: string, listener: (event: any) => void) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the thing in window.gBrowser, defined in
|
||||||
|
// https://searchfox.org/mozilla-central/source/browser/base/content/tabbrowser.js
|
||||||
|
interface Browser {
|
||||||
|
addWebTab: (url: string, options: any) => BrowserTab;
|
||||||
|
contentPrincipal: any;
|
||||||
|
selectedTab: BrowserTab;
|
||||||
|
selectedBrowser?: ChromeBrowser;
|
||||||
|
messageManager: MessageManager;
|
||||||
|
ownerDocument?: ChromeDocument;
|
||||||
|
tabs: BrowserTab[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BrowserGroup {
|
||||||
|
readonly tabs: BrowserTab[];
|
||||||
|
readonly group?: BrowserGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a tab in a browser, defined in
|
||||||
|
// https://searchfox.org/mozilla-central/rev/6b8a3f804789fb865f42af54e9d2fef9dd3ec74d/browser/base/content/tabbrowser.js#2580
|
||||||
|
interface BrowserTab extends XULElement {
|
||||||
|
linkedBrowser: ChromeBrowser;
|
||||||
|
readonly group?: BrowserGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BrowserWindow extends Window {
|
||||||
|
gBrowser: Browser;
|
||||||
|
focus(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The thing created in https://searchfox.org/mozilla-central/rev/6b8a3f804789fb865f42af54e9d2fef9dd3ec74d/browser/base/content/tabbrowser.js#2088
|
||||||
|
// This is linked to BrowserTab.
|
||||||
|
interface ChromeBrowser {
|
||||||
|
browsingContext?: BrowsingContext;
|
||||||
|
browserId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BrowsingContext {
|
||||||
|
/**
|
||||||
|
* A unique identifier for the browser element that is hosting this
|
||||||
|
* BrowsingContext tree. Every BrowsingContext in the element's tree will
|
||||||
|
* return the same ID in all processes and it will remain stable regardless of
|
||||||
|
* process changes. When a browser element's frameloader is switched to
|
||||||
|
* another browser element this ID will remain the same but hosted under the
|
||||||
|
* under the new browser element.
|
||||||
|
* We are using this identifier for getting the active tab ID and passing to
|
||||||
|
* the profiler back-end. See `getActiveBrowserID` for the usage.
|
||||||
|
*/
|
||||||
|
browserId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetPref<T> = (prefName: string, defaultValue?: T) => T;
|
||||||
|
type SetPref<T> = (prefName: string, value?: T) => T;
|
||||||
|
type nsIPrefBranch = {
|
||||||
|
clearUserPref: (prefName: string) => void;
|
||||||
|
getStringPref: GetPref<string>;
|
||||||
|
setStringPref: SetPref<string>;
|
||||||
|
getCharPref: GetPref<string>;
|
||||||
|
setCharPref: SetPref<string>;
|
||||||
|
getIntPref: GetPref<number>;
|
||||||
|
setIntPref: SetPref<number>;
|
||||||
|
getBoolPref: GetPref<boolean>;
|
||||||
|
setBoolPref: SetPref<boolean>;
|
||||||
|
addObserver: (aDomain: string, aObserver: PrefObserver, aHoldWeak?: boolean) => void;
|
||||||
|
removeObserver: (aDomain: string, aObserver: PrefObserver) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type PrefObserverFunction = (aSubject: nsIPrefBranch, aTopic: 'nsPref:changed', aData: string) => unknown;
|
||||||
|
type PrefObserver = PrefObserverFunction | { observe: PrefObserverFunction };
|
||||||
|
|
||||||
|
interface nsIURI {}
|
||||||
|
|
||||||
|
interface SharedLibrary {
|
||||||
|
start: number;
|
||||||
|
end: number;
|
||||||
|
offset: number;
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
debugName: string;
|
||||||
|
debugPath: string;
|
||||||
|
breakpadId: string;
|
||||||
|
arch: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProfileGenerationAdditionalInformation {
|
||||||
|
sharedLibraries: SharedLibrary[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProfileAndAdditionalInformation {
|
||||||
|
profile: ArrayBuffer;
|
||||||
|
additionalInformation?: ProfileGenerationAdditionalInformation;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Services = {
|
||||||
|
env: {
|
||||||
|
set: (name: string, value: string) => void;
|
||||||
|
get: (name: string) => string;
|
||||||
|
exists: (name: string) => boolean;
|
||||||
|
};
|
||||||
|
prefs: nsIPrefBranch;
|
||||||
|
profiler: {
|
||||||
|
StartProfiler: (
|
||||||
|
entryCount: number,
|
||||||
|
interval: number,
|
||||||
|
features: string[],
|
||||||
|
filters?: string[],
|
||||||
|
activeTabId?: number,
|
||||||
|
duration?: number
|
||||||
|
) => void;
|
||||||
|
StopProfiler: () => void;
|
||||||
|
IsPaused: () => boolean;
|
||||||
|
Pause: () => void;
|
||||||
|
Resume: () => void;
|
||||||
|
IsSamplingPaused: () => boolean;
|
||||||
|
PauseSampling: () => void;
|
||||||
|
ResumeSampling: () => void;
|
||||||
|
GetFeatures: () => string[];
|
||||||
|
getProfileDataAsync: (sinceTime?: number) => Promise<object>;
|
||||||
|
getProfileDataAsArrayBuffer: (sinceTime?: number) => Promise<ArrayBuffer>;
|
||||||
|
getProfileDataAsGzippedArrayBuffer: (sinceTime?: number) => Promise<ProfileAndAdditionalInformation>;
|
||||||
|
IsActive: () => boolean;
|
||||||
|
sharedLibraries: SharedLibrary[];
|
||||||
|
};
|
||||||
|
platform: string;
|
||||||
|
obs: {
|
||||||
|
addObserver: (observer: object, type: string) => void;
|
||||||
|
removeObserver: (observer: object, type: string) => void;
|
||||||
|
};
|
||||||
|
wm: {
|
||||||
|
getMostRecentWindow: (name: string) => BrowserWindow;
|
||||||
|
getMostRecentNonPBWindow: (name: string) => BrowserWindow;
|
||||||
|
};
|
||||||
|
focus: {
|
||||||
|
activeWindow: BrowserWindow;
|
||||||
|
};
|
||||||
|
io: {
|
||||||
|
newURI(url: string): nsIURI;
|
||||||
|
};
|
||||||
|
scriptSecurityManager: any;
|
||||||
|
startup: {
|
||||||
|
quit: (optionsBitmask: number) => void;
|
||||||
|
eForceQuit: number;
|
||||||
|
eRestart: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const EventEmitter: {
|
||||||
|
decorate: (target: object) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const AppConstantsSYSMJS: {
|
||||||
|
AppConstants: {
|
||||||
|
platform: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
interface BrowsingContextStub {}
|
||||||
|
interface PrincipalStub {}
|
||||||
|
|
||||||
|
interface WebChannelTarget {
|
||||||
|
browsingContext: BrowsingContextStub;
|
||||||
|
browser: Browser;
|
||||||
|
eventTarget: null;
|
||||||
|
principal: PrincipalStub;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FaviconData {
|
||||||
|
uri: nsIURI;
|
||||||
|
dataLen: number;
|
||||||
|
data: number[];
|
||||||
|
mimeType: string;
|
||||||
|
size: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PlaceUtilsSYSMJS: {
|
||||||
|
PlacesUtils: {
|
||||||
|
promiseFaviconData: (pageUrl: string | URL | nsIURI, preferredWidth?: number) => Promise<FaviconData>;
|
||||||
|
// TS-TODO: Add the rest.
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// TS-TODO
|
||||||
|
const CustomizableUISYSMJS: any;
|
||||||
|
const CustomizableWidgetsSYSMJS: any;
|
||||||
|
const PanelMultiViewSYSMJS: any;
|
||||||
|
|
||||||
|
const LoaderESM: {
|
||||||
|
require: (path: string) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Services: Services;
|
||||||
|
|
||||||
|
// This class is needed by the Cc importing mechanism. e.g.
|
||||||
|
// Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||||
|
class nsIFilePicker {}
|
||||||
|
|
||||||
|
interface FilePicker {
|
||||||
|
init: (browsingContext: BrowsingContext, title: string, mode: number) => void;
|
||||||
|
open: (callback: (rv: number) => unknown) => void;
|
||||||
|
// The following are enum values.
|
||||||
|
modeGetFolder: number;
|
||||||
|
returnOK: number;
|
||||||
|
file: {
|
||||||
|
path: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Cc {
|
||||||
|
'@mozilla.org/filepicker;1': {
|
||||||
|
createInstance(instance: nsIFilePicker): FilePicker;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Ci {
|
||||||
|
nsIFilePicker: nsIFilePicker;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Cu {
|
||||||
|
exportFunction: (fn: Function, scope: object, options?: object) => void;
|
||||||
|
cloneInto: (value: any, scope: object, options?: object) => void;
|
||||||
|
isInAutomation: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FluentLocalization {
|
||||||
|
/**
|
||||||
|
* This function sets the attributes data-l10n-id and possibly data-l10n-args
|
||||||
|
* on the element.
|
||||||
|
*/
|
||||||
|
setAttributes(target: Element, id?: string, args?: Record<string, string>): void;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PathUtilsInterface {
|
||||||
|
split: (path: string) => string[];
|
||||||
|
isAbsolute: (path: string) => boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'Services' {
|
||||||
|
export = MockedExports.Services;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'ChromeUtils' {
|
||||||
|
export = ChromeUtils;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare var ChromeUtils: MockedExports.ChromeUtils;
|
||||||
|
|
||||||
|
declare var PathUtils: PathUtilsInterface;
|
||||||
|
|
||||||
|
// These global objects can be used directly in JSM files only.
|
||||||
|
declare var Cu: MockedExports.Cu;
|
||||||
|
declare var Cc: MockedExports.Cc;
|
||||||
|
declare var Ci: MockedExports.Ci;
|
||||||
|
declare var Services: MockedExports.Services;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a variant on the normal Document, as it contains chrome-specific properties.
|
||||||
|
*/
|
||||||
|
declare interface ChromeDocument extends Document {
|
||||||
|
/**
|
||||||
|
* Create a XUL element of a specific type. Right now this function
|
||||||
|
* only refines iframes, but more tags could be added.
|
||||||
|
*/
|
||||||
|
createXULElement: ((type: 'iframe') => XULIframeElement) & ((type: string) => XULElement);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a fluent instance connected to this document.
|
||||||
|
*/
|
||||||
|
l10n: MockedExports.FluentLocalization;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a variant on the HTMLElement, as it contains chrome-specific properties.
|
||||||
|
*/
|
||||||
|
declare interface ChromeHTMLElement extends HTMLElement {
|
||||||
|
ownerDocument: ChromeDocument;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare interface XULIframeElement extends XULElement {
|
||||||
|
contentWindow: Window;
|
||||||
|
src: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// `declare interface Window` is TypeScript way to let us implicitely extend and
|
||||||
|
// augment the already existing Window interface defined in the TypeScript library.
|
||||||
|
// This makes it possible to define properties that exist in the window object
|
||||||
|
// while in a privileged context. We assume that all of the environments we run
|
||||||
|
// in this project will be pribileged, that's why we take this shortcut of
|
||||||
|
// globally extending the Window type.
|
||||||
|
// See the ChromeOnly attributes in https://searchfox.org/mozilla-central/rev/896042a1a71066254ceb5291f016ca3dbca21cb7/dom/webidl/Window.webidl#391
|
||||||
|
//
|
||||||
|
// openWebLinkIn and openTrustedLinkIn aren't in all privileged windows, but
|
||||||
|
// they're also defined in the privileged environments we're dealing with in
|
||||||
|
// this project, so they're defined here for convenience.
|
||||||
|
declare interface Window {
|
||||||
|
browsingContext: MockedExports.BrowsingContext;
|
||||||
|
openWebLinkIn: (
|
||||||
|
url: string,
|
||||||
|
where: 'current' | 'tab' | 'tabshifted' | 'window' | 'save',
|
||||||
|
options?: Partial<{
|
||||||
|
// Not all possible options are present, please add more if/when needed.
|
||||||
|
userContextId: number;
|
||||||
|
forceNonPrivate: boolean;
|
||||||
|
relatedToCurrent: boolean;
|
||||||
|
resolveOnContentBrowserCreated: (contentBrowser: MockedExports.ChromeBrowser) => unknown;
|
||||||
|
}>
|
||||||
|
) => void;
|
||||||
|
openTrustedLinkIn: (
|
||||||
|
url: string,
|
||||||
|
where: 'current' | 'tab' | 'tabshifted' | 'window' | 'save',
|
||||||
|
options?: Partial<{
|
||||||
|
// Not all possible options are present, please add more if/when needed.
|
||||||
|
userContextId: number;
|
||||||
|
forceNonPrivate: boolean;
|
||||||
|
relatedToCurrent: boolean;
|
||||||
|
resolveOnContentBrowserCreated: (contentBrowser: MockedExports.ChromeBrowser) => unknown;
|
||||||
|
}>
|
||||||
|
) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare class ChromeWorker extends Worker {}
|
||||||
|
|
||||||
|
declare interface MenuListElement extends XULElement {
|
||||||
|
value: string;
|
||||||
|
disabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare interface XULCommandEvent extends Event {
|
||||||
|
target: XULElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare interface XULElementWithCommandHandler {
|
||||||
|
addEventListener: (type: 'command', handler: (event: XULCommandEvent) => void, isCapture?: boolean) => void;
|
||||||
|
removeEventListener: (type: 'command', handler: (event: XULCommandEvent) => void, isCapture?: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type nsIPrefBranch = MockedExports.nsIPrefBranch;
|
||||||
|
|
||||||
|
// chrome context-only DOM isInstance method
|
||||||
|
// XXX: This hackishly extends Function because there is no way to extend DOM constructors.
|
||||||
|
// Callers should manually narrow the type when needed.
|
||||||
|
// See also https://github.com/microsoft/TypeScript-DOM-lib-generator/issues/222
|
||||||
|
interface Function {
|
||||||
|
isInstance(obj: any): boolean;
|
||||||
|
}
|
||||||
|
|
|
@ -800,7 +800,7 @@
|
||||||
if (tabsTarget === gBrowser.tabs.at(-1)) {
|
if (tabsTarget === gBrowser.tabs.at(-1)) {
|
||||||
newIndex++;
|
newIndex++;
|
||||||
}
|
}
|
||||||
gBrowser.moveTabTo(draggedTab, newIndex, { forceUngrouped: true });
|
gBrowser.moveTabTo(draggedTab, { tabIndex: newIndex, forceUngrouped: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -255,7 +255,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
if (!this.containerSpecificEssentials) {
|
if (!this.containerSpecificEssentials) {
|
||||||
container = 0;
|
container = 0;
|
||||||
}
|
}
|
||||||
let essentialsContainer = document.querySelector(`.zen-essentials-container[container="${container}"]`);
|
let essentialsContainer = document.querySelector(`.zen-essentials-container[container="${container}"]:not([clone])`);
|
||||||
if (!essentialsContainer) {
|
if (!essentialsContainer) {
|
||||||
essentialsContainer = document.createXULElement('vbox');
|
essentialsContainer = document.createXULElement('vbox');
|
||||||
essentialsContainer.className = 'zen-essentials-container zen-workspace-tabs-section';
|
essentialsContainer.className = 'zen-essentials-container zen-workspace-tabs-section';
|
||||||
|
@ -657,7 +657,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
if (this._initialTab) {
|
if (this._initialTab) {
|
||||||
this.moveTabToWorkspace(this._initialTab, this.activeWorkspace);
|
this.moveTabToWorkspace(this._initialTab, this.activeWorkspace);
|
||||||
gBrowser.selectedTab = this._initialTab;
|
gBrowser.selectedTab = this._initialTab;
|
||||||
gBrowser.moveTabTo(this._initialTab, 0, { forceUngrouped: true });
|
gBrowser.moveTabTo(this._initialTab, { forceUngrouped: true, tabIndex: 0 });
|
||||||
this._initialTab._possiblyEmpty = false;
|
this._initialTab._possiblyEmpty = false;
|
||||||
this._initialTab = null;
|
this._initialTab = null;
|
||||||
}
|
}
|
||||||
|
@ -1735,23 +1735,26 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
const workspaces = await this._workspaces();
|
const workspaces = await this._workspaces();
|
||||||
const newWorkspaceIndex = workspaces.workspaces.findIndex((w) => w.uuid === newWorkspace.uuid);
|
const newWorkspaceIndex = workspaces.workspaces.findIndex((w) => w.uuid === newWorkspace.uuid);
|
||||||
const clonedEssentials = [];
|
const clonedEssentials = [];
|
||||||
const essentialsContainerMap = {};
|
if (shouldAnimate && this.containerSpecificEssentials) {
|
||||||
if (shouldAnimate) {
|
|
||||||
for (const workspace of workspaces.workspaces) {
|
for (const workspace of workspaces.workspaces) {
|
||||||
const essentialsContainer = this.getEssentialsSection(workspace.containerTabId);
|
const essentialsContainer = this.getEssentialsSection(workspace.containerTabId);
|
||||||
|
if (clonedEssentials[clonedEssentials.length - 1]?.contextId == workspace.containerTabId) {
|
||||||
|
clonedEssentials[clonedEssentials.length - 1].repeat++;
|
||||||
|
clonedEssentials[clonedEssentials.length - 1].workspaces.push(workspace);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
essentialsContainer.setAttribute('hidden', 'true');
|
essentialsContainer.setAttribute('hidden', 'true');
|
||||||
const essentialsClone = essentialsContainer.cloneNode(true);
|
const essentialsClone = essentialsContainer.cloneNode(true);
|
||||||
essentialsClone.removeAttribute('hidden');
|
essentialsClone.removeAttribute('hidden');
|
||||||
|
essentialsClone.setAttribute('cloned', 'true');
|
||||||
clonedEssentials.push({
|
clonedEssentials.push({
|
||||||
container: essentialsClone,
|
container: essentialsClone,
|
||||||
workspaceId: workspace.uuid,
|
workspaces: [workspace],
|
||||||
contextId: workspace.containerTabId,
|
contextId: workspace.containerTabId,
|
||||||
originalContainer: essentialsContainer,
|
originalContainer: essentialsContainer,
|
||||||
repeat: 0,
|
repeat: 0,
|
||||||
});
|
});
|
||||||
essentialsContainer.parentNode.appendChild(essentialsClone);
|
essentialsContainer.parentNode.appendChild(essentialsClone);
|
||||||
// +0 to convert null to 0
|
|
||||||
essentialsContainerMap[workspace.containerTabId + 0] = essentialsContainer;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const element of document.querySelectorAll('.zen-workspace-tabs-section')) {
|
for (const element of document.querySelectorAll('.zen-workspace-tabs-section')) {
|
||||||
|
@ -1778,26 +1781,6 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
if (element.parentNode.id === 'zen-current-workspace-indicator-container') {
|
|
||||||
// Get essential container clone for this workspace
|
|
||||||
const clonedEssential = clonedEssentials.find((cloned) => cloned.workspaceId === elementWorkspaceId);
|
|
||||||
if (clonedEssential && !clonedEssential.animating) {
|
|
||||||
clonedEssential.animating = true; // Avoid motion hanging due to animating the same element twice
|
|
||||||
animations.push(
|
|
||||||
gZenUIManager.motion.animate(
|
|
||||||
clonedEssential.container,
|
|
||||||
{
|
|
||||||
transform: existingTransform ? [existingTransform, newTransform] : newTransform,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'spring',
|
|
||||||
bounce: 0,
|
|
||||||
duration: kGlobalAnimationDuration,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (offset === 0) {
|
if (offset === 0) {
|
||||||
element.setAttribute('active', 'true');
|
element.setAttribute('active', 'true');
|
||||||
|
@ -1808,6 +1791,58 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
element.removeAttribute('active');
|
element.removeAttribute('active');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (this.containerSpecificEssentials) {
|
||||||
|
// Animate essentials
|
||||||
|
for (const cloned of clonedEssentials) {
|
||||||
|
const container = cloned.container;
|
||||||
|
const essentialsWorkspacess = cloned.workspaces;
|
||||||
|
const repeats = cloned.repeat;
|
||||||
|
const containerId = cloned.contextId;
|
||||||
|
// Animate like the workspaces above expect essentials are a bit more
|
||||||
|
// complicated because they are not based on workspaces but on containers
|
||||||
|
// So, if we have the following arangement:
|
||||||
|
// | [workspace1] [workspace2] [workspace3] [workspace4]
|
||||||
|
// | [container1] [container1] [container2] [container1]
|
||||||
|
// And if we are changing from workspace 1 to workspace 4,
|
||||||
|
// we should be doing the following:
|
||||||
|
// First container (repeat 2 times) will stay in place until
|
||||||
|
// we reach container 3, then animate to the left and container 2
|
||||||
|
// also move to the left after that while container 1 in workspace 4
|
||||||
|
// will slide in from the right
|
||||||
|
|
||||||
|
// Get the index from first and last workspace
|
||||||
|
const firstWorkspaceIndex = workspaces.workspaces.findIndex((w) => w.uuid === essentialsWorkspacess[0].uuid);
|
||||||
|
const lastWorkspaceIndex = workspaces.workspaces.findIndex(
|
||||||
|
(w) => w.uuid === essentialsWorkspacess[essentialsWorkspacess.length - 1].uuid
|
||||||
|
);
|
||||||
|
const isGoingLeft = newWorkspaceIndex > lastWorkspaceIndex;
|
||||||
|
const isGoingInsideSameContainer = essentialsWorkspacess.some((w) => w.uuid === newWorkspace.uuid);
|
||||||
|
if (isGoingInsideSameContainer) {
|
||||||
|
continue; // We dont want to animate if we are going inside the same container
|
||||||
|
}
|
||||||
|
const firstOffset = -(newWorkspaceIndex - firstWorkspaceIndex - (isGoingLeft ? repeats : -repeats)) * 100;
|
||||||
|
const lastOffset = -(newWorkspaceIndex - lastWorkspaceIndex - (isGoingLeft ? repeats : -repeats)) * 100;
|
||||||
|
const newTransform = `translateX(${firstOffset}%)`;
|
||||||
|
const existingTransform = `translateX(${lastOffset}%)`;
|
||||||
|
const stepsInBetween = Math.abs(lastWorkspaceIndex - firstWorkspaceIndex);
|
||||||
|
if (shouldAnimate) {
|
||||||
|
container.style.transform = newTransform;
|
||||||
|
animations.push(
|
||||||
|
gZenUIManager.motion.animate(
|
||||||
|
container,
|
||||||
|
{
|
||||||
|
transform: [existingTransform, new Array(stepsInBetween).fill(newTransform).join(',')],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'spring',
|
||||||
|
bounce: 0,
|
||||||
|
duration: kGlobalAnimationDuration,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
await Promise.all(animations);
|
await Promise.all(animations);
|
||||||
if (shouldAnimate) {
|
if (shouldAnimate) {
|
||||||
for (const cloned of clonedEssentials) {
|
for (const cloned of clonedEssentials) {
|
||||||
|
@ -2391,6 +2426,9 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
}
|
}
|
||||||
const containers = [...essentialsContainer, ...pinnedContainers, ...normalContainers];
|
const containers = [...essentialsContainer, ...pinnedContainers, ...normalContainers];
|
||||||
for (const container of containers) {
|
for (const container of containers) {
|
||||||
|
if (container.hasAttribute('cloned')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
for (const tab of container.children) {
|
for (const tab of container.children) {
|
||||||
if (tab.tagName === 'tab') {
|
if (tab.tagName === 'tab') {
|
||||||
tabs.push(tab);
|
tabs.push(tab);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue