mirror of
https://github.com/zen-browser/components.git
synced 2025-07-08 01:10:12 +02:00
refactor: Remove unused type declarations and package.json dependencies
This commit is contained in:
parent
de5090fe9c
commit
828ecbbf10
10 changed files with 1326 additions and 2546 deletions
5
@types/common.d.ts
vendored
5
@types/common.d.ts
vendored
|
@ -1,5 +0,0 @@
|
|||
|
||||
export interface Config {
|
||||
debug: boolean;
|
||||
browserName: string;
|
||||
};
|
525
@types/gecko.d.ts
vendored
525
@types/gecko.d.ts
vendored
|
@ -1,525 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
// https://github.com/Floorp-Projects/Floorp-core/blob/95ce9eb44c06a682e9d6379eaa4a5d1bbd5db6cd/%40types/gecko.d.ts#L198
|
||||
|
||||
/**
|
||||
* 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.import how to find modules.
|
||||
*/
|
||||
interface KnownModules {
|
||||
"resource://gre/modules/FileUtils.sys.mjs": FileUtils;
|
||||
"resource://gre/modules/FileUtils.jsm": FileUtils;
|
||||
|
||||
Services: Services;
|
||||
"resource://gre/modules/Services.jsm": Services;
|
||||
"resource://gre/modules/AppConstants.sys.mjs": typeof AppConstantsSYSMJS;
|
||||
"resource:///modules/CustomizableUI.sys.mjs": typeof CustomizableUISYSMJS;
|
||||
"resource:///modules/CustomizableWidgets.sys.mjs": typeof CustomizableWidgetsSYSMJS;
|
||||
"resource://devtools/shared/loader/Loader.sys.mjs": typeof LoaderESM;
|
||||
//"resource://devtools/client/performance-new/shared/background.jsm.js": typeof import("resource://devtools/client/performance-new/shared/background.jsm.js");
|
||||
//"resource://devtools/client/performance-new/shared/symbolication.jsm.js": typeof import("resource://devtools/client/performance-new/shared/symbolication.jsm.js");
|
||||
"resource://devtools/shared/loader/browser-loader.js": any;
|
||||
//"resource://devtools/client/performance-new/popup/menu-button.jsm.js": typeof import("resource://devtools/client/performance-new/popup/menu-button.jsm.js");
|
||||
//"resource://devtools/client/performance-new/shared/typescript-lazy-load.jsm.js": typeof import("resource://devtools/client/performance-new/shared/typescript-lazy-load.jsm.js");
|
||||
//"resource://devtools/client/performance-new/popup/logic.jsm.js": typeof import("resource://devtools/client/performance-new/popup/logic.jsm.js");
|
||||
"resource:///modules/PanelMultiView.sys.mjs": typeof PanelMultiViewSYSMJS;
|
||||
}
|
||||
|
||||
type Services = {
|
||||
Services: import("firefox").Services;
|
||||
};
|
||||
|
||||
type FileUtils = {
|
||||
FileUtils: IFileUtils;
|
||||
};
|
||||
|
||||
interface IFileUtils {
|
||||
MODE_RDONLY: number;
|
||||
MODE_WRONLY: number;
|
||||
MODE_RDWR: number;
|
||||
MODE_CREATE: number;
|
||||
MODE_APPEND: number;
|
||||
MODE_TRUNCATE: number;
|
||||
|
||||
PERMS_FILE: number;
|
||||
PERMS_DIRECTORY: number;
|
||||
/**
|
||||
* Gets a file at the specified hierarchy under a nsIDirectoryService key.
|
||||
* @param key
|
||||
* The Directory Service Key to start from
|
||||
* @param pathArray
|
||||
* An array of path components to locate beneath the directory
|
||||
* specified by |key|. The last item in this array must be the
|
||||
* leaf name of a file.
|
||||
* @return nsIFile object for the file specified. The file is NOT created
|
||||
* if it does not exist, however all required directories along
|
||||
* the way are if pathArray has more than one item.
|
||||
*/
|
||||
getFile: (key: any, pathArray: any) => any;
|
||||
/**
|
||||
* Gets a directory at the specified hierarchy under a nsIDirectoryService
|
||||
* key.
|
||||
* @param key
|
||||
* The Directory Service Key to start from
|
||||
* @param pathArray
|
||||
* An array of path components to locate beneath the directory
|
||||
* specified by |key|
|
||||
* @param shouldCreate
|
||||
* true if the directory hierarchy specified in |pathArray|
|
||||
* should be created if it does not exist, false otherwise.
|
||||
* @return nsIFile object for the location specified.
|
||||
*/
|
||||
getDir: (key, pathArray, shouldCreate) => any;
|
||||
/**
|
||||
* Opens a file output stream for writing.
|
||||
* @param file
|
||||
* The file to write to.
|
||||
* @param modeFlags
|
||||
* (optional) File open flags. Can be undefined.
|
||||
* @returns nsIFileOutputStream to write to.
|
||||
* @note The stream is initialized with the DEFER_OPEN behavior flag.
|
||||
* See nsIFileOutputStream.
|
||||
*/
|
||||
openFileOutputStream: (file, modeFlags) => any;
|
||||
/**
|
||||
* Opens an atomic file output stream for writing.
|
||||
* @param file
|
||||
* The file to write to.
|
||||
* @param modeFlags
|
||||
* (optional) File open flags. Can be undefined.
|
||||
* @returns nsIFileOutputStream to write to.
|
||||
* @note The stream is initialized with the DEFER_OPEN behavior flag.
|
||||
* See nsIFileOutputStream.
|
||||
* OpeanAtomicFileOutputStream is generally better than openSafeFileOutputStream
|
||||
* baecause flushing is not needed in most of the issues.
|
||||
*/
|
||||
openAtomicFileOutputStream: (file, modeFlags) => any;
|
||||
/**
|
||||
* Opens a safe file output stream for writing.
|
||||
* @param file
|
||||
* The file to write to.
|
||||
* @param modeFlags
|
||||
* (optional) File open flags. Can be undefined.
|
||||
* @returns nsIFileOutputStream to write to.
|
||||
* @note The stream is initialized with the DEFER_OPEN behavior flag.
|
||||
* See nsIFileOutputStream.
|
||||
*/
|
||||
openSafeFileOutputStream: (file, modeFlags) => any;
|
||||
|
||||
_initFileOutputStream: (fos, file, modeFlags) => any;
|
||||
/**
|
||||
* Closes an atomic file output stream.
|
||||
* @param stream
|
||||
* The stream to close.
|
||||
*/
|
||||
|
||||
closeAtomicFileOutputStream: <
|
||||
Stream extends
|
||||
import("firefox").Components_Interfaces["nsISafeOutputStream"] & any,
|
||||
>(
|
||||
stream: Stream
|
||||
) => void;
|
||||
/**
|
||||
* Closes a safe file output stream.
|
||||
* @param stream
|
||||
* The stream to close.
|
||||
*/
|
||||
closeSafeFileOutputStream: <
|
||||
Stream extends
|
||||
import("firefox").Components_Interfaces["nsISafeOutputStream"] & any,
|
||||
>(
|
||||
stream: Stream
|
||||
) => void;
|
||||
File: any;
|
||||
}
|
||||
|
||||
//TODO: add to window
|
||||
interface MozXULElement {
|
||||
/**
|
||||
* Allows eager deterministic construction of XUL elements with XBL attached, by
|
||||
* parsing an element tree and returning a DOM fragment to be inserted in the
|
||||
* document before any of the inner elements is referenced by JavaScript.
|
||||
*
|
||||
* This process is required instead of calling the createElement method directly
|
||||
* because bindings get attached when:
|
||||
*
|
||||
* 1. the node gets a layout frame constructed, or
|
||||
* 2. the node gets its JavaScript reflector created, if it's in the document,
|
||||
*
|
||||
* whichever happens first. The createElement method would return a JavaScript
|
||||
* reflector, but the element wouldn't be in the document, so the node wouldn't
|
||||
* get XBL attached. After that point, even if the node is inserted into a
|
||||
* document, it won't get XBL attached until either the frame is constructed or
|
||||
* the reflector is garbage collected and the element is touched again.
|
||||
*
|
||||
* @param {string} str
|
||||
* String with the XML representation of XUL elements.
|
||||
* @param {string[]} [entities]
|
||||
* An array of DTD URLs containing entity definitions.
|
||||
*
|
||||
* @return {DocumentFragment} `DocumentFragment` instance containing
|
||||
* the corresponding element tree, including element nodes
|
||||
* but excluding any text node.
|
||||
*/
|
||||
parseXULToFragment: (str: string, entities: string[]) => DocumentFragment;
|
||||
}
|
||||
|
||||
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.jsm"' is not assignable to parameter
|
||||
* of type
|
||||
*
|
||||
* Then add the file path to the KnownModules above.
|
||||
*/
|
||||
import: <S extends keyof KnownModules>(module: S) => KnownModules[S];
|
||||
importESModule: <S extends keyof KnownModules>(
|
||||
module: S
|
||||
) => KnownModules[S];
|
||||
defineModuleGetter: (target: any, variable: string, path: string) => void;
|
||||
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;
|
||||
}
|
||||
|
||||
interface Browser extends HTMLElement {
|
||||
addWebTab: (url: string, options: any) => BrowserTab;
|
||||
contentPrincipal: any;
|
||||
selectedTab: BrowserTab;
|
||||
selectedBrowser?: ChromeBrowser;
|
||||
messageManager: MessageManager;
|
||||
ownerDocument?: ChromeDocument;
|
||||
}
|
||||
|
||||
interface BrowserTab extends HTMLElement {
|
||||
linkedBrowser: Browser;
|
||||
}
|
||||
|
||||
interface ChromeWindow {
|
||||
gBrowser: Browser;
|
||||
focus(): void;
|
||||
openWebLinkIn(
|
||||
url: string,
|
||||
where: "current" | "tab" | "window",
|
||||
options: Partial<{
|
||||
// Not all possible options are present, please add more if/when needed.
|
||||
userContextId: number;
|
||||
forceNonPrivate: boolean;
|
||||
resolveOnContentBrowserCreated: (
|
||||
contentBrowser: ChromeBrowser
|
||||
) => unknown;
|
||||
}>
|
||||
): void;
|
||||
}
|
||||
|
||||
interface ChromeBrowser {
|
||||
browsingContext?: BrowsingContext;
|
||||
}
|
||||
|
||||
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 PrefObserverFunction = (
|
||||
aSubject: import("./firefox/modules/libpref/nsIPrefBranch").nsIPrefBranch,
|
||||
aTopic: "nsPref:changed",
|
||||
aData: string
|
||||
) => unknown;
|
||||
type PrefObserver = PrefObserverFunction | { observe: PrefObserverFunction };
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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: (window: Window, 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 {
|
||||
// /**
|
||||
// * This function reads the KnownModules and resolves which import to use.
|
||||
// * If you are getting the TS2345 error:
|
||||
// *
|
||||
// * Argument of type '"resource:///.../file.jsm"' is not assignable to parameter
|
||||
// * of type
|
||||
// *
|
||||
// * Then add the file path to the KnownModules above.
|
||||
// */
|
||||
// import: <S extends keyof KnownModules>(module: S) => KnownModules[S];
|
||||
// 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;
|
||||
}
|
||||
|
||||
// export module "resource://devtools/client/shared/vendor/react.js" {
|
||||
// import * as React from "react";
|
||||
// export = React;
|
||||
// }
|
||||
|
||||
// declare module "resource://devtools/client/shared/vendor/react-dom-factories.js" {
|
||||
// import * as ReactDomFactories from "react-dom-factories";
|
||||
// export = ReactDomFactories;
|
||||
// }
|
||||
|
||||
// declare module "resource://devtools/client/shared/vendor/redux.js" {
|
||||
// import * as Redux from "redux";
|
||||
// export = Redux;
|
||||
// }
|
||||
|
||||
// declare module "resource://devtools/client/shared/vendor/react-redux.js" {
|
||||
// import * as ReactRedux from "react-redux";
|
||||
// export = ReactRedux;
|
||||
// }
|
||||
|
||||
// declare module "resource://devtools/shared/event-emitter2.js" {
|
||||
// export = MockedExports.EventEmitter;
|
||||
// }
|
||||
|
||||
// declare module "Services" {
|
||||
// export = Services;
|
||||
// }
|
||||
|
||||
// declare module "ChromeUtils" {
|
||||
// export = ChromeUtils;
|
||||
// }
|
||||
|
||||
// declare module "resource://gre/modules/AppConstants.sys.mjs" {
|
||||
// export = MockedExports.AppConstantsSYSMJS;
|
||||
// }
|
||||
|
||||
// declare module "resource://devtools/client/performance-new/shared/background.jsm.js" {
|
||||
// import * as Background from "devtools/client/performance-new/shared/background.jsm.js";
|
||||
// export = Background;
|
||||
// }
|
||||
|
||||
// declare module "resource://devtools/client/performance-new/shared/symbolication.jsm.js" {
|
||||
// import * as PerfSymbolication from "devtools/client/performance-new/shared/symbolication.jsm.js";
|
||||
// export = PerfSymbolication;
|
||||
// }
|
||||
|
||||
// declare module "resource:///modules/CustomizableUI.sys.mjs" {
|
||||
// export = MockedExports.CustomizableUISYSMJS;
|
||||
// }
|
||||
|
||||
// declare module "resource:///modules/CustomizableWidgets.sys.mjs" {
|
||||
// export = MockedExports.CustomizableWidgetsSYSMJS;
|
||||
// }
|
||||
|
||||
// declare module "resource:///modules/PanelMultiView.sys.mjs" {
|
||||
// export = MockedExports.PanelMultiViewSYSMJS;
|
||||
// }
|
||||
|
||||
// declare module "resource://devtools/shared/loader/Loader.sys.mjs" {
|
||||
// export = MockedExports.LoaderESM;
|
||||
// }
|
||||
|
||||
declare var ChromeUtils: MockedExports.ChromeUtils;
|
||||
|
||||
declare var PathUtils: PathUtilsInterface;
|
||||
|
||||
/**
|
||||
* 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 XULElement extends HTMLElement {
|
||||
ownerDocument: ChromeDocument;
|
||||
}
|
||||
|
||||
declare interface XULIframeElement extends XULElement {
|
||||
contentWindow: ChromeWindow;
|
||||
src: string;
|
||||
}
|
||||
|
||||
declare interface ChromeWindow extends Window {
|
||||
openWebLinkIn: (
|
||||
url: string,
|
||||
where: "current" | "tab" | "tabshifted" | "window" | "save",
|
||||
// TS-TODO
|
||||
params?: unknown
|
||||
) => void;
|
||||
openTrustedLinkIn: (
|
||||
url: string,
|
||||
where: "current" | "tab" | "tabshifted" | "window" | "save",
|
||||
// TS-TODO
|
||||
params?: 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;
|
||||
}
|
||||
|
||||
declare module "resource://gre/modules/FileUtils.sys.mjs" {
|
||||
const FileUtils: MockedExports.IFileUtils;
|
||||
export { FileUtils };
|
||||
}
|
||||
|
||||
// Extend "window" to extend "ChromeWindow"
|
||||
declare global {
|
||||
interface Window extends ChromeWindow {}
|
||||
}
|
13
@types/split-views.d.ts
vendored
13
@types/split-views.d.ts
vendored
|
@ -1,13 +0,0 @@
|
|||
|
||||
declare type SplitType = 'horizontal' | 'vertical' | 'grid';
|
||||
|
||||
declare interface SplitViewConfig extends Config {
|
||||
splitIndicator: string; // e.g. "split-tab='true'"
|
||||
defaultSplitView: SplitType;
|
||||
};
|
||||
|
||||
declare interface SplitView {
|
||||
type: SplitType;
|
||||
tabs: MockedExports.BrowserTab[];
|
||||
id: number;
|
||||
};
|
1551
package-lock.json
generated
1551
package-lock.json
generated
File diff suppressed because it is too large
Load diff
16
package.json
16
package.json
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"name": "components",
|
||||
"version": "1.0.0",
|
||||
"description": "Some components used by @zen-browser and @Floorp-Projects as an attempt to make firefox forks a better place",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"check": "tsc"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"typescript": "^5.5.4"
|
||||
}
|
||||
}
|
551
src/ZenSidebarManager.mjs
Normal file
551
src/ZenSidebarManager.mjs
Normal file
|
@ -0,0 +1,551 @@
|
|||
|
||||
|
||||
|
||||
export var gZenBrowserManagerSidebar = {
|
||||
_sidebarElement: null,
|
||||
_currentPanel: null,
|
||||
_lastOpenedPanel: null,
|
||||
_hasChangedConfig: true,
|
||||
_splitterElement: null,
|
||||
_hSplitterElement: null,
|
||||
_hasRegisteredPinnedClickOutside: false,
|
||||
_isDragging: false,
|
||||
contextTab: null,
|
||||
|
||||
DEFAULT_MOBILE_USER_AGENT: "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36 Edg/114.0.1823.79",
|
||||
MAX_SIDEBAR_PANELS: 8, // +1 for the add panel button
|
||||
MAX_RUNS: 3,
|
||||
|
||||
init() {
|
||||
this.update();
|
||||
this.close(); // avoid caching
|
||||
this.listenForPrefChanges();
|
||||
this.insertIntoContextMenu();
|
||||
},
|
||||
|
||||
get sidebarData() {
|
||||
let services = Services.prefs.getStringPref("zen.sidebar.data");
|
||||
if (services === "") {
|
||||
return {};
|
||||
}
|
||||
return JSON.parse(services);
|
||||
},
|
||||
|
||||
get shouldCloseOnBlur() {
|
||||
return Services.prefs.getBoolPref("zen.sidebar.close-on-blur");
|
||||
},
|
||||
|
||||
listenForPrefChanges() {
|
||||
Services.prefs.addObserver("zen.sidebar.data", this.handleEvent.bind(this));
|
||||
Services.prefs.addObserver("zen.sidebar.enabled", this.handleEvent.bind(this));
|
||||
|
||||
let sidebar = document.getElementById("zen-sidebar-web-panel");
|
||||
this.splitterElement.addEventListener("mousedown", (function(event) {
|
||||
let computedStyle = window.getComputedStyle(sidebar);
|
||||
let maxWidth = parseInt(computedStyle.getPropertyValue("max-width").replace("px", ""));
|
||||
let minWidth = parseInt(computedStyle.getPropertyValue("min-width").replace("px", ""));
|
||||
|
||||
if (!this._isDragging) { // Prevent multiple resizes
|
||||
this._isDragging = true;
|
||||
let sidebarWidth = sidebar.getBoundingClientRect().width;
|
||||
let startX = event.clientX;
|
||||
let startWidth = sidebarWidth;
|
||||
let mouseMove = (function(e) {
|
||||
let newWidth = startWidth + e.clientX - startX;
|
||||
if (newWidth <= minWidth+10) {
|
||||
newWidth = minWidth+1;
|
||||
} else if (newWidth >= maxWidth-10) {
|
||||
newWidth = maxWidth-1;
|
||||
}
|
||||
sidebar.style.width = `${newWidth}px`;
|
||||
});
|
||||
let mouseUp = (function() {
|
||||
this.handleEvent();
|
||||
this._isDragging = false;
|
||||
document.removeEventListener("mousemove", mouseMove);
|
||||
document.removeEventListener("mouseup", mouseUp);
|
||||
}).bind(this);
|
||||
document.addEventListener("mousemove", mouseMove);
|
||||
document.addEventListener("mouseup", mouseUp);
|
||||
}
|
||||
}).bind(this));
|
||||
|
||||
this.hSplitterElement.addEventListener("mousedown", (function(event) {
|
||||
let computedStyle = window.getComputedStyle(sidebar);
|
||||
const parent = sidebar.parentElement;
|
||||
// relative to avoid the top margin
|
||||
// 20px is the padding
|
||||
let parentRelativeHeight = parent.getBoundingClientRect().height - parent.getBoundingClientRect().top + 20;
|
||||
let minHeight = parseInt(computedStyle.getPropertyValue("min-height").replace("px", ""));
|
||||
if (!this._isDragging) { // Prevent multiple resizes
|
||||
this._isDragging = true;
|
||||
let sidebarHeight = sidebar.getBoundingClientRect().height;
|
||||
let startY = event.clientY;
|
||||
let startHeight = sidebarHeight;
|
||||
let mouseMove = (function(e) {
|
||||
let newHeight = startHeight + e.clientY - startY;
|
||||
if (newHeight <= minHeight+10) {
|
||||
newHeight = minHeight+1;
|
||||
} else if (newHeight >= parentRelativeHeight) { // 10px is the padding
|
||||
newHeight = parentRelativeHeight;
|
||||
}
|
||||
sidebar.style.height = `${newHeight}px`;
|
||||
});
|
||||
let mouseUp = (function() {
|
||||
this.handleEvent();
|
||||
this._isDragging = false;
|
||||
document.removeEventListener("mousemove", mouseMove);
|
||||
document.removeEventListener("mouseup", mouseUp);
|
||||
}).bind(this);
|
||||
document.addEventListener("mousemove", mouseMove);
|
||||
document.addEventListener("mouseup", mouseUp);
|
||||
}
|
||||
}).bind(this));
|
||||
|
||||
this.handleEvent();
|
||||
},
|
||||
|
||||
get isFloating() {
|
||||
return document.getElementById("zen-sidebar-web-panel").hasAttribute("pinned");
|
||||
},
|
||||
|
||||
handleEvent() {
|
||||
this._hasChangedConfig = true;
|
||||
this.update();
|
||||
this._hasChangedConfig = false;
|
||||
|
||||
// https://stackoverflow.com/questions/11565471/removing-event-listener-which-was-added-with-bind
|
||||
var clickOutsideHandler = this._handleClickOutside.bind(this);
|
||||
let isFloating = this.isFloating;
|
||||
if (isFloating && !this._hasRegisteredPinnedClickOutside) {
|
||||
document.addEventListener("mouseup", clickOutsideHandler);
|
||||
this._hasRegisteredPinnedClickOutside = true;
|
||||
} else if (!isFloating && this._hasRegisteredPinnedClickOutside) {
|
||||
document.removeEventListener("mouseup", clickOutsideHandler);
|
||||
this._hasRegisteredPinnedClickOutside = false;
|
||||
}
|
||||
|
||||
const button = document.getElementById("zen-sidepanel-button");
|
||||
if (Services.prefs.getBoolPref("zen.sidebar.enabled")) {
|
||||
button.removeAttribute("hidden");
|
||||
} else {
|
||||
button.setAttribute("hidden", "true");
|
||||
this._closeSidebarPanel();
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
_handleClickOutside(event) {
|
||||
let sidebar = document.getElementById("zen-sidebar-web-panel");
|
||||
if (!sidebar.hasAttribute("pinned") || this._isDragging || !this.shouldCloseOnBlur) {
|
||||
return;
|
||||
}
|
||||
let target = event.target;
|
||||
const closestSelector = [
|
||||
"#zen-sidebar-web-panel",
|
||||
"#zen-sidebar-panels-wrapper",
|
||||
"#zenWebPanelContextMenu",
|
||||
"#zen-sidebar-web-panel-splitter",
|
||||
"#contentAreaContextMenu"
|
||||
].join(", ");
|
||||
if (target.closest(closestSelector)) {
|
||||
return;
|
||||
}
|
||||
this.close();
|
||||
},
|
||||
|
||||
toggle() {
|
||||
if (!this._currentPanel) {
|
||||
this._currentPanel = this._lastOpenedPanel;
|
||||
}
|
||||
if (document.getElementById("zen-sidebar-web-panel").hasAttribute("hidden")) {
|
||||
this.open();
|
||||
return;
|
||||
}
|
||||
this.close();
|
||||
},
|
||||
|
||||
open() {
|
||||
let sidebar = document.getElementById("zen-sidebar-web-panel");
|
||||
sidebar.removeAttribute("hidden");
|
||||
this.update();
|
||||
},
|
||||
|
||||
update() {
|
||||
this._updateWebPanels();
|
||||
this._updateSidebarButton();
|
||||
this._updateWebPanel();
|
||||
this._updateButtons();
|
||||
},
|
||||
|
||||
_updateSidebarButton() {
|
||||
let button = document.getElementById("zen-sidepanel-button");
|
||||
if (!document.getElementById("zen-sidebar-web-panel").hasAttribute("hidden")) {
|
||||
button.setAttribute("open", "true");
|
||||
} else {
|
||||
button.removeAttribute("open");
|
||||
}
|
||||
},
|
||||
|
||||
_updateWebPanels() {
|
||||
if (Services.prefs.getBoolPref("zen.sidebar.enabled")) {
|
||||
this.sidebarElement.removeAttribute("hidden");
|
||||
} else {
|
||||
this.sidebarElement.setAttribute("hidden", "true");
|
||||
this._closeSidebarPanel();
|
||||
return;
|
||||
}
|
||||
|
||||
let data = this.sidebarData;
|
||||
if (!data.data || !data.index) {
|
||||
return;
|
||||
}
|
||||
this.sidebarElement.innerHTML = "";
|
||||
for (let site of data.index) {
|
||||
let panel = data.data[site];
|
||||
if (!panel || !panel.url) {
|
||||
continue;
|
||||
}
|
||||
let button = document.createXULElement("toolbarbutton");
|
||||
button.classList.add("zen-sidebar-panel-button", "toolbarbutton-1", "chromeclass-toolbar-additional");
|
||||
button.setAttribute("flex", "1");
|
||||
button.setAttribute("zen-sidebar-id", site);
|
||||
button.setAttribute("context", "zenWebPanelContextMenu");
|
||||
this._getWebPanelIcon(panel.url, button);
|
||||
button.addEventListener("click", this._handleClick.bind(this));
|
||||
this.sidebarElement.appendChild(button);
|
||||
}
|
||||
const addButton = document.getElementById("zen-sidebar-add-panel-button");
|
||||
if (data.index.length < this.MAX_SIDEBAR_PANELS) {
|
||||
addButton.removeAttribute("hidden");
|
||||
} else {
|
||||
addButton.setAttribute("hidden", "true");
|
||||
}
|
||||
},
|
||||
|
||||
async _openAddPanelDialog() {
|
||||
let dialogURL = "chrome://browser/content/places/zenNewWebPanel.xhtml";
|
||||
let features = "centerscreen,chrome,modal,resizable=no";
|
||||
let aParentWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
|
||||
if (aParentWindow?.gDialogBox) {
|
||||
await aParentWindow.gDialogBox.open(dialogURL, {});
|
||||
} else {
|
||||
aParentWindow.openDialog(dialogURL, "", features, {});
|
||||
}
|
||||
},
|
||||
|
||||
_setPinnedToElements() {
|
||||
let sidebar = document.getElementById("zen-sidebar-web-panel");
|
||||
sidebar.setAttribute("pinned", "true");
|
||||
document.getElementById("zen-sidebar-web-panel-pinned").setAttribute("pinned", "true");
|
||||
},
|
||||
|
||||
_removePinnedFromElements() {
|
||||
let sidebar = document.getElementById("zen-sidebar-web-panel");
|
||||
sidebar.removeAttribute("pinned");
|
||||
document.getElementById("zen-sidebar-web-panel-pinned").removeAttribute("pinned");
|
||||
},
|
||||
|
||||
_closeSidebarPanel() {
|
||||
let sidebar = document.getElementById("zen-sidebar-web-panel");
|
||||
sidebar.setAttribute("hidden", "true");
|
||||
this._lastOpenedPanel = this._currentPanel;
|
||||
this._currentPanel = null;
|
||||
},
|
||||
|
||||
_handleClick(event) {
|
||||
let target = event.target;
|
||||
let panelId = target.getAttribute("zen-sidebar-id");
|
||||
if (this._currentPanel === panelId) {
|
||||
return;
|
||||
}
|
||||
this._currentPanel = panelId;
|
||||
this._updateWebPanel();
|
||||
},
|
||||
|
||||
_createNewPanel(url) {
|
||||
let data = this.sidebarData;
|
||||
let newName = "p" + new Date().getTime();
|
||||
data.index.push(newName);
|
||||
data.data[newName] = {
|
||||
url: url,
|
||||
ua: false,
|
||||
};
|
||||
Services.prefs.setStringPref("zen.sidebar.data", JSON.stringify(data));
|
||||
this._currentPanel = newName;
|
||||
this.open();
|
||||
},
|
||||
|
||||
_updateButtons() {
|
||||
for (let button of this.sidebarElement.querySelectorAll(".zen-sidebar-panel-button")) {
|
||||
if (button.getAttribute("zen-sidebar-id") === this._currentPanel) {
|
||||
button.setAttribute("selected", "true");
|
||||
} else {
|
||||
button.removeAttribute("selected");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_hideAllWebPanels() {
|
||||
let sidebar = document.getElementById("zen-sidebar-web-panel");
|
||||
for (let browser of sidebar.querySelectorAll("browser[zen-sidebar-id]")) {
|
||||
browser.setAttribute("hidden", "true");
|
||||
browser.docShellIsActive = false;
|
||||
}
|
||||
},
|
||||
|
||||
get introductionPanel() {
|
||||
return document.getElementById("zen-sidebar-introduction-panel");
|
||||
},
|
||||
|
||||
_updateWebPanel() {
|
||||
this._updateButtons();
|
||||
let sidebar = document.getElementById("zen-sidebar-web-panel");
|
||||
this._hideAllWebPanels();
|
||||
if (!this._currentPanel) {
|
||||
this.introductionPanel.removeAttribute("hidden");
|
||||
return;
|
||||
}
|
||||
this.introductionPanel.setAttribute("hidden", "true");
|
||||
let existantWebview = this._getCurrentBrowser();
|
||||
if (existantWebview) {
|
||||
existantWebview.docShellIsActive = true;
|
||||
existantWebview.removeAttribute("hidden");
|
||||
document.getElementById("zen-sidebar-web-panel-title").textContent = existantWebview.contentTitle;
|
||||
return;
|
||||
}
|
||||
let data = this._getWebPanelData(this._currentPanel);
|
||||
let browser = this._createWebPanelBrowser(data);
|
||||
let browserContainers = document.getElementById("zen-sidebar-web-panel-browser-containers");
|
||||
browserContainers.appendChild(browser);
|
||||
if (data.ua) {
|
||||
browser.browsingContext.customUserAgent = this.DEFAULT_MOBILE_USER_AGENT;
|
||||
}
|
||||
browser.docShellIsActive = true;
|
||||
},
|
||||
|
||||
_getWebPanelData(id) {
|
||||
let data = this.sidebarData;
|
||||
let panel = data.data[id];
|
||||
if (!panel || !panel.url) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
id: id,
|
||||
...panel,
|
||||
};
|
||||
},
|
||||
|
||||
_createWebPanelBrowser(data) {
|
||||
const titleContainer = document.getElementById("zen-sidebar-web-panel-title");
|
||||
titleContainer.textContent = "Loading...";
|
||||
let browser = gBrowser.createBrowser({});
|
||||
browser.setAttribute("disablefullscreen", "true");
|
||||
browser.setAttribute("src", data.url);
|
||||
browser.setAttribute("zen-sidebar-id", data.id);
|
||||
browser.setAttribute("disableglobalhistory", "true");
|
||||
browser.setAttribute("autoscroll", "false");
|
||||
browser.setAttribute("autocompletepopup", "PopupAutoComplete");
|
||||
browser.setAttribute("contextmenu", "contentAreaContextMenu");
|
||||
browser.setAttribute("disablesecurity", "true");
|
||||
browser.addEventListener("pagetitlechanged", (function(event) {
|
||||
let browser = event.target;
|
||||
let title = browser.contentTitle;
|
||||
if (!title) {
|
||||
return;
|
||||
}
|
||||
let id = browser.getAttribute("zen-sidebar-id");
|
||||
if (id === this._currentPanel) {
|
||||
titleContainer.textContent = title;
|
||||
}
|
||||
}).bind(this));
|
||||
return browser;
|
||||
},
|
||||
|
||||
_getWebPanelIcon(url, element) {
|
||||
let { preferredURI } = Services.uriFixup.getFixupURIInfo(url);
|
||||
element.setAttribute("image", `page-icon:${preferredURI.spec}`);
|
||||
fetch(`https://s2.googleusercontent.com/s2/favicons?domain_url=${preferredURI.spec}`).then(async response => {
|
||||
if (response.ok) {
|
||||
let blob = await response.blob();
|
||||
let reader = new FileReader();
|
||||
reader.onload = function() {
|
||||
element.setAttribute("image", reader.result);
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_getBrowserById(id) {
|
||||
let sidebar = document.getElementById("zen-sidebar-web-panel");
|
||||
return sidebar.querySelector(`browser[zen-sidebar-id="${id}"]`);
|
||||
},
|
||||
|
||||
_getCurrentBrowser() {
|
||||
return this._getBrowserById(this._currentPanel);
|
||||
},
|
||||
|
||||
reload() {
|
||||
let browser = this._getCurrentBrowser();
|
||||
if (browser) {
|
||||
browser.reload();
|
||||
}
|
||||
},
|
||||
|
||||
forward() {
|
||||
let browser = this._getCurrentBrowser();
|
||||
if (browser) {
|
||||
browser.goForward();
|
||||
}
|
||||
},
|
||||
|
||||
back() {
|
||||
let browser = this._getCurrentBrowser();
|
||||
if (browser) {
|
||||
browser.goBack();
|
||||
}
|
||||
},
|
||||
|
||||
home() {
|
||||
let browser = this._getCurrentBrowser();
|
||||
if (browser) {
|
||||
browser.gotoIndex();
|
||||
}
|
||||
},
|
||||
|
||||
close() {
|
||||
this._hideAllWebPanels();
|
||||
this._closeSidebarPanel();
|
||||
this._updateSidebarButton();
|
||||
},
|
||||
|
||||
togglePinned(elem) {
|
||||
let sidebar = document.getElementById("zen-sidebar-web-panel");
|
||||
if (sidebar.hasAttribute("pinned")) {
|
||||
this._removePinnedFromElements();
|
||||
} else {
|
||||
this._setPinnedToElements();
|
||||
}
|
||||
this.update();
|
||||
},
|
||||
|
||||
get sidebarElement() {
|
||||
if (!this._sidebarElement) {
|
||||
this._sidebarElement = document.getElementById("zen-sidebar-panels-sites");
|
||||
}
|
||||
return this._sidebarElement;
|
||||
},
|
||||
|
||||
get splitterElement() {
|
||||
if (!this._splitterElement) {
|
||||
this._splitterElement = document.getElementById("zen-sidebar-web-panel-splitter");
|
||||
}
|
||||
return this._splitterElement;
|
||||
},
|
||||
|
||||
get hSplitterElement() {
|
||||
if (!this._hSplitterElement) {
|
||||
this._hSplitterElement = document.getElementById("zen-sidebar-web-panel-hsplitter");
|
||||
}
|
||||
return this._hSplitterElement;
|
||||
},
|
||||
|
||||
// Context menu
|
||||
|
||||
updateContextMenu(aPopupMenu) {
|
||||
let panel =
|
||||
aPopupMenu.triggerNode &&
|
||||
(aPopupMenu.triggerNode || aPopupMenu.triggerNode.closest("toolbarbutton[zen-sidebar-id]"));
|
||||
if (!panel) {
|
||||
return;
|
||||
}
|
||||
let id = panel.getAttribute("zen-sidebar-id");
|
||||
this.contextTab = id;
|
||||
let data = this._getWebPanelData(id);
|
||||
let browser = this._getBrowserById(id);
|
||||
let isMuted = browser && browser.audioMuted;
|
||||
let mutedContextItem = document.getElementById("context_zenToggleMuteWebPanel");
|
||||
document.l10n.setAttributes(mutedContextItem,
|
||||
!isMuted ? "zen-web-side-panel-context-mute-panel" : "zen-web-side-panel-context-unmute-panel");
|
||||
if (!isMuted) {
|
||||
mutedContextItem.setAttribute("muted", "true");
|
||||
} else {
|
||||
mutedContextItem.removeAttribute("muted");
|
||||
}
|
||||
document.l10n.setAttributes(document.getElementById("context_zenToogleUAWebPanel"),
|
||||
data.ua ? "zen-web-side-panel-context-disable-ua" : "zen-web-side-panel-context-enable-ua");
|
||||
if (!browser) {
|
||||
document.getElementById("context_zenUnloadWebPanel").setAttribute("disabled", "true");
|
||||
} else {
|
||||
document.getElementById("context_zenUnloadWebPanel").removeAttribute("disabled");
|
||||
}
|
||||
},
|
||||
|
||||
contextOpenNewTab() {
|
||||
let browser = this._getBrowserById(this.contextTab);
|
||||
let data = this.sidebarData;
|
||||
let panel = data.data[this.contextTab];
|
||||
let url = (browser == null) ? panel.url : browser.currentURI.spec;
|
||||
gZenUIManager.openAndChangeToTab(url);
|
||||
this.close();
|
||||
},
|
||||
|
||||
contextToggleMuteAudio() {
|
||||
let browser = this._getBrowserById(this.contextTab);
|
||||
if (browser.audioMuted) {
|
||||
browser.unmute();
|
||||
} else {
|
||||
browser.mute();
|
||||
}
|
||||
},
|
||||
|
||||
contextToggleUserAgent() {
|
||||
let browser = this._getBrowserById(this.contextTab);
|
||||
browser.browsingContext.customUserAgent = browser.browsingContext.customUserAgent ? null : this.DEFAULT_MOBILE_USER_AGENT;
|
||||
let data = this.sidebarData;
|
||||
data.data[this.contextTab].ua = !data.data[this.contextTab].ua;
|
||||
Services.prefs.setStringPref("zen.sidebar.data", JSON.stringify(data));
|
||||
browser.reload();
|
||||
},
|
||||
|
||||
contextDelete() {
|
||||
let data = this.sidebarData;
|
||||
delete data.data[this.contextTab];
|
||||
data.index = data.index.filter(id => id !== this.contextTab);
|
||||
let browser = this._getBrowserById(this.contextTab);
|
||||
if (browser) {
|
||||
browser.remove();
|
||||
}
|
||||
this._currentPanel = null;
|
||||
this._lastOpenedPanel = null;
|
||||
this.update();
|
||||
Services.prefs.setStringPref("zen.sidebar.data", JSON.stringify(data));
|
||||
},
|
||||
|
||||
contextUnload() {
|
||||
let browser = this._getBrowserById(this.contextTab);
|
||||
browser.remove();
|
||||
this._closeSidebarPanel();
|
||||
this.close();
|
||||
this._lastOpenedPanel = null;
|
||||
},
|
||||
|
||||
insertIntoContextMenu() {
|
||||
const sibling = document.getElementById("context-stripOnShareLink");
|
||||
const menuitem = document.createXULElement("menuitem");
|
||||
menuitem.setAttribute("id", "context-zenAddToWebPanel");
|
||||
menuitem.setAttribute("hidden", "true");
|
||||
menuitem.setAttribute("oncommand", "gZenBrowserManagerSidebar.addPanelFromContextMenu();");
|
||||
menuitem.setAttribute("data-l10n-id", "zen-web-side-panel-context-add-to-panel");
|
||||
sibling.insertAdjacentElement("afterend", menuitem);
|
||||
},
|
||||
|
||||
addPanelFromContextMenu() {
|
||||
const url = gContextMenu.linkURL || gContextMenu.target.ownerDocument.location.href;
|
||||
this._createNewPanel(url);
|
||||
},
|
||||
};
|
||||
|
||||
gZenBrowserManagerSidebar.init();
|
349
src/ZenViewSplitter.mjs
Normal file
349
src/ZenViewSplitter.mjs
Normal file
|
@ -0,0 +1,349 @@
|
|||
|
||||
export var gZenViewSplitter = {
|
||||
/**
|
||||
* [
|
||||
* {
|
||||
* tabs: [
|
||||
* tab1,
|
||||
* tab2,
|
||||
* tab3,
|
||||
* ],
|
||||
* gridType: "vsep" | "hsep" | "grid",
|
||||
* }
|
||||
* ]
|
||||
*/
|
||||
_data: [],
|
||||
currentView: -1,
|
||||
|
||||
init() {
|
||||
Services.prefs.setBoolPref("zen.splitView.working", false);
|
||||
window.addEventListener("TabClose", this);
|
||||
this.initializeUI();
|
||||
console.log("ZenViewSplitter initialized");
|
||||
},
|
||||
|
||||
initializeUI() {
|
||||
this.insertIntoContextMenu();
|
||||
this.initializeUpdateContextMenuItems();
|
||||
this.initializeTabContextMenu();
|
||||
this.initializeZenSplitBox();
|
||||
},
|
||||
|
||||
initializeZenSplitBox() {
|
||||
const fragment = window.MozXULElement.parseXULToFragment(`
|
||||
<hbox id="zen-split-views-box"
|
||||
hidden="true"
|
||||
role="button"
|
||||
class="urlbar-page-action"
|
||||
onclick="gZenViewSplitter.openSplitViewPanel(event);">
|
||||
<image id="zen-split-views-button"
|
||||
class="urlbar-icon"/>
|
||||
</hbox>`);
|
||||
document.getElementById("star-button-box").after(fragment);
|
||||
},
|
||||
|
||||
initializeTabContextMenu() {
|
||||
const fragment = window.MozXULElement.parseXULToFragment(`
|
||||
<menuseparator/>
|
||||
<menuitem id="context_zenSplitTabs"
|
||||
data-lazy-l10n-id="tab-zen-split-tabs"
|
||||
oncommand="gZenViewSplitter.contextSplitTabs();"/>
|
||||
`);
|
||||
document.getElementById("tabContextMenu").appendChild(fragment);
|
||||
},
|
||||
|
||||
initializeUpdateContextMenuItems() {
|
||||
const contentAreaContextMenu = document.getElementById("tabContextMenu");
|
||||
contentAreaContextMenu.addEventListener("popupshowing", () => {
|
||||
const tabCountInfo = JSON.stringify({
|
||||
tabCount: window.gBrowser.selectedTabs.length,
|
||||
});
|
||||
document
|
||||
.getElementById("context_zenSplitTabs")
|
||||
.setAttribute("data-l10n-args", tabCountInfo);
|
||||
document.getElementById("context_zenSplitTabs").disabled =
|
||||
!this.contextCanSplitTabs();
|
||||
});
|
||||
},
|
||||
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case "TabClose":
|
||||
this.onTabClose(event);
|
||||
}
|
||||
},
|
||||
|
||||
insertIntoContextMenu() {
|
||||
const sibling = document.getElementById("context-stripOnShareLink");
|
||||
const menuitem = document.createXULElement("menuitem");
|
||||
menuitem.setAttribute("id", "context-zenSplitLink");
|
||||
menuitem.setAttribute("hidden", "true");
|
||||
menuitem.setAttribute("oncommand", "gZenViewSplitter.contextSplitLink();");
|
||||
menuitem.setAttribute("data-l10n-id", "zen-split-link");
|
||||
const separator = document.createXULElement("menuseparator");
|
||||
sibling.insertAdjacentElement("afterend", menuitem);
|
||||
sibling.insertAdjacentElement("afterend", separator);
|
||||
},
|
||||
|
||||
get tabBrowserPanel() {
|
||||
if (!this._tabBrowserPanel) {
|
||||
this._tabBrowserPanel = document.getElementById("tabbrowser-tabpanels");
|
||||
}
|
||||
return this._tabBrowserPanel;
|
||||
},
|
||||
|
||||
onTabClose(event) {
|
||||
const tab = event.target;
|
||||
let index = this._data.findIndex((group) => group.tabs.includes(tab));
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
let dataTab = this._data[index].tabs;
|
||||
dataTab.splice(dataTab.indexOf(tab), 1);
|
||||
tab._zenSplitted = false;
|
||||
tab.linkedBrowser.zenModeActive = false;
|
||||
let container = tab.linkedBrowser.closest(".browserSidebarContainer");
|
||||
container.removeAttribute("zen-split");
|
||||
if (!event.forUnsplit) {
|
||||
tab.linkedBrowser.docShellIsActive = false;
|
||||
container.style.display = "none";
|
||||
} else {
|
||||
container.style.gridArea = "1 / 1";
|
||||
}
|
||||
if (dataTab.length < 2) {
|
||||
this._data.splice(index, 1);
|
||||
if (this.currentView == index) {
|
||||
console.assert(dataTab.length == 1, "Data tab length is not 1");
|
||||
this.currentView = -1;
|
||||
this.tabBrowserPanel.removeAttribute("zen-split-view");
|
||||
this.tabBrowserPanel.style.gridTemplateAreas = "";
|
||||
this.tabBrowserPanel.style.gridGap = "0px";
|
||||
Services.prefs.setBoolPref("zen.splitView.working", false);
|
||||
for (const tab of dataTab) {
|
||||
let container = tab.linkedBrowser.closest(".browserSidebarContainer");
|
||||
container.removeAttribute("zen-split");
|
||||
container.style.gridArea = "1 / 1";
|
||||
tab._zenSplitted = false;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
let lastTab = dataTab[dataTab.length - 1];
|
||||
this._showSplitView(lastTab);
|
||||
},
|
||||
|
||||
contextSplitLink() {
|
||||
const url = gContextMenu.linkURL || gContextMenu.target.ownerDocument.location.href;
|
||||
const tab = gBrowser.selectedTab;
|
||||
const newTab = gZenUIManager.openAndChangeToTab(url);
|
||||
this.splitTabs([tab, newTab]);
|
||||
},
|
||||
|
||||
onLocationChange(browser) {
|
||||
let tab = gBrowser.getTabForBrowser(browser);
|
||||
this.updateSplitViewButton(!(tab && tab._zenSplitted));
|
||||
if (!tab) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._showSplitView(tab);
|
||||
},
|
||||
|
||||
splitTabs(tabs) {
|
||||
if (tabs.length < 2) {
|
||||
return;
|
||||
}
|
||||
// Check if any tab is already split
|
||||
for (const tab of tabs) {
|
||||
if (tab._zenSplitted) {
|
||||
let index = this._data.findIndex((group) => group.tabs.includes(tab));
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
this._showSplitView(tab);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this._data.push({
|
||||
tabs,
|
||||
gridType: "grid",
|
||||
});
|
||||
gBrowser.selectedTab = tabs[0];
|
||||
this._showSplitView(tabs[0]);
|
||||
},
|
||||
|
||||
_showSplitView(tab) {
|
||||
const splitData = this._data.find((group) => group.tabs.includes(tab));
|
||||
function modifyDecks(tabs, add) {
|
||||
for (const tab of tabs) {
|
||||
tab.linkedBrowser.zenModeActive = add;
|
||||
tab.linkedBrowser.docShellIsActive = add;
|
||||
let browser = tab.linkedBrowser.closest(".browserSidebarContainer");
|
||||
if (add) {
|
||||
browser.setAttribute("zen-split", "true");
|
||||
continue;
|
||||
}
|
||||
browser.removeAttribute("zen-split");
|
||||
}
|
||||
}
|
||||
const handleClick = (tab) => {
|
||||
return ((event) => {
|
||||
gBrowser.selectedTab = tab;
|
||||
})
|
||||
};
|
||||
if (!splitData || (this.currentView >= 0 && !this._data[this.currentView].tabs.includes(tab))) {
|
||||
this.updateSplitViewButton(true);
|
||||
if (this.currentView < 0) {
|
||||
return;
|
||||
}
|
||||
for (const tab of this._data[this.currentView].tabs) {
|
||||
//tab._zenSplitted = false;
|
||||
let container = tab.linkedBrowser.closest(".browserSidebarContainer");
|
||||
container.removeAttribute("zen-split-active");
|
||||
container.classList.remove("deck-selected");
|
||||
console.assert(container, "No container found for tab");
|
||||
container.removeEventListener("click", handleClick(tab));
|
||||
container.style.gridArea = "";
|
||||
}
|
||||
this.tabBrowserPanel.removeAttribute("zen-split-view");
|
||||
this.tabBrowserPanel.style.gridTemplateAreas = "";
|
||||
Services.prefs.setBoolPref("zen.splitView.working", false);
|
||||
modifyDecks(this._data[this.currentView].tabs, false);
|
||||
// console.log("Setting the active tab to be active", gBrowser.selectedTab);
|
||||
gBrowser.selectedTab.linkedBrowser.docShellIsActive = true; // Make sure the active tab is active
|
||||
this.currentView = -1;
|
||||
if (!splitData) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.tabBrowserPanel.setAttribute("zen-split-view", "true");
|
||||
Services.prefs.setBoolPref("zen.splitView.working", true);
|
||||
this.currentView = this._data.indexOf(splitData);
|
||||
let gridType = splitData.gridType || "grid"; // TODO: let user decide the grid type
|
||||
let i = 0;
|
||||
// 2 rows, infinite columns
|
||||
let currentRowGridArea = ["", ""/* first row, second row */];
|
||||
let numberOfRows = 0;
|
||||
for (const _tab of splitData.tabs) {
|
||||
_tab._zenSplitted = true;
|
||||
let container = _tab.linkedBrowser.closest(".browserSidebarContainer");
|
||||
console.assert(container, "No container found for tab");
|
||||
container.removeAttribute("zen-split-active");
|
||||
if (_tab == tab) {
|
||||
container.setAttribute("zen-split-active", "true");
|
||||
}
|
||||
container.setAttribute("zen-split-anim", "true");
|
||||
container.addEventListener("click", handleClick(_tab));
|
||||
// Set the grid type for the container. If the grid type is not "grid", then set the grid type contain
|
||||
// each column or row. If it's "grid", then try to create
|
||||
if (gridType == "grid") {
|
||||
// Each 2 tabs, create a new row
|
||||
if (i % 2 == 0) {
|
||||
currentRowGridArea[0] += ` tab${i + 1}`;
|
||||
} else {
|
||||
currentRowGridArea[1] += ` tab${i + 1}`;
|
||||
numberOfRows++;
|
||||
}
|
||||
container.style.gridArea = `tab${i + 1}`;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (gridType == "grid") {
|
||||
if ((numberOfRows < splitData.tabs.length / 2) && (splitData.tabs.length != 2)) {
|
||||
// Make the last tab occupy the last row
|
||||
currentRowGridArea[1] += ` tab${i}`;
|
||||
}
|
||||
if (gridType == "grid" && (splitData.tabs.length === 2)) {
|
||||
currentRowGridArea[0] = `tab1 tab2`;
|
||||
currentRowGridArea[1] = "";
|
||||
}
|
||||
this.tabBrowserPanel.style.gridTemplateAreas = `'${currentRowGridArea[0]}'`;
|
||||
if (currentRowGridArea[1] != "") {
|
||||
this.tabBrowserPanel.style.gridTemplateAreas += ` '${currentRowGridArea[1]}'`;
|
||||
}
|
||||
} else if (gridType == "vsep") {
|
||||
this.tabBrowserPanel.style.gridTemplateAreas = `'${splitData.tabs.map((_, i) => `tab${i + 1}`).join(" ")}'`;
|
||||
} else if (gridType == "hsep") {
|
||||
this.tabBrowserPanel.style.gridTemplateAreas = `${splitData.tabs.map((_, i) => `'tab${i + 1}'`).join(" ")}`;
|
||||
}
|
||||
modifyDecks(splitData.tabs, true);
|
||||
this.updateSplitViewButton(false);
|
||||
},
|
||||
|
||||
contextSplitTabs() {
|
||||
let tabs = gBrowser.selectedTabs;
|
||||
this.splitTabs(tabs);
|
||||
},
|
||||
|
||||
contextCanSplitTabs() {
|
||||
if (gBrowser.selectedTabs.length < 2) {
|
||||
return false;
|
||||
}
|
||||
// Check if any tab is already split
|
||||
for (const tab of gBrowser.selectedTabs) {
|
||||
if (tab._zenSplitted) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
// Panel and url button
|
||||
|
||||
updateSplitViewButton(hidden) {
|
||||
let button = document.getElementById("zen-split-views-box");
|
||||
if (hidden) {
|
||||
button.setAttribute("hidden", "true");
|
||||
return;
|
||||
}
|
||||
button.removeAttribute("hidden");
|
||||
},
|
||||
|
||||
get _modifierElement() {
|
||||
if (!this.__modifierElement) {
|
||||
let wrapper = document.getElementById("template-zen-split-view-modifier");
|
||||
const panel = wrapper.content.firstElementChild;
|
||||
wrapper.replaceWith(wrapper.content);
|
||||
this.__modifierElement = panel;
|
||||
}
|
||||
return this.__modifierElement;
|
||||
},
|
||||
|
||||
async openSplitViewPanel(event) {
|
||||
let panel = this._modifierElement;
|
||||
let target = event.target.parentNode;
|
||||
for (const gridType of ["hsep", "vsep", "grid", "unsplit"]) {
|
||||
let selector = panel.querySelector(`.zen-split-view-modifier-preview.${gridType}`);
|
||||
selector.classList.remove("active");
|
||||
if (this.currentView >= 0 && this._data[this.currentView].gridType == gridType) {
|
||||
selector.classList.add("active");
|
||||
}
|
||||
if (this.__hasSetMenuListener) {
|
||||
continue;
|
||||
}
|
||||
selector.addEventListener("click", ((gridType) => {
|
||||
if (gridType === "unsplit") {
|
||||
let currentTab = gBrowser.selectedTab;
|
||||
let tabs = this._data[this.currentView].tabs;
|
||||
for (const tab of tabs) {
|
||||
this.onTabClose({ target: tab, forUnsplit: true });
|
||||
}
|
||||
gBrowser.selectedTab = currentTab;
|
||||
panel.hidePopup();
|
||||
this.updateSplitViewButton(true);
|
||||
return;
|
||||
}
|
||||
this._data[this.currentView].gridType = gridType;
|
||||
this._showSplitView(gBrowser.selectedTab);
|
||||
panel.hidePopup();
|
||||
}).bind(this, gridType));
|
||||
}
|
||||
this.__hasSetMenuListener = true;
|
||||
PanelMultiView.openPopup(panel, target, {
|
||||
position: "bottomright topright",
|
||||
triggerEvent: event,
|
||||
}).catch(console.error);
|
||||
},
|
||||
};
|
||||
|
||||
gZenViewSplitter.init();
|
426
src/ZenWorkspaces.mjs
Normal file
426
src/ZenWorkspaces.mjs
Normal file
|
@ -0,0 +1,426 @@
|
|||
|
||||
export var ZenWorkspaces = {
|
||||
async init() {
|
||||
let docElement = document.documentElement;
|
||||
if (docElement.getAttribute("chromehidden").includes("toolbar")
|
||||
|| docElement.getAttribute("chromehidden").includes("menubar")
|
||||
|| docElement.hasAttribute("privatebrowsingmode")) {
|
||||
console.warn("ZenWorkspaces: !!! ZenWorkspaces is disabled in hidden windows !!!");
|
||||
return; // We are in a hidden window, don't initialize ZenWorkspaces
|
||||
}
|
||||
console.log("ZenWorkspaces: Initializing ZenWorkspaces...");
|
||||
await this.initializeWorkspaces();
|
||||
console.log("ZenWorkspaces: ZenWorkspaces initialized");
|
||||
},
|
||||
|
||||
get workspaceEnabled() {
|
||||
return Services.prefs.getBoolPref("zen.workspaces.enabled", false);
|
||||
},
|
||||
|
||||
// Wrorkspaces saving/loading
|
||||
get _storeFile() {
|
||||
return PathUtils.join(
|
||||
PathUtils.profileDir,
|
||||
"zen-workspaces",
|
||||
"Workspaces.json",
|
||||
);
|
||||
},
|
||||
|
||||
async _workspaces() {
|
||||
if (!this._workspaceCache) {
|
||||
this._workspaceCache = await IOUtils.readJSON(this._storeFile);
|
||||
if (!this._workspaceCache.workspaces) {
|
||||
this._workspaceCache.workspaces = [];
|
||||
}
|
||||
}
|
||||
return this._workspaceCache;
|
||||
},
|
||||
|
||||
onWorkspacesEnabledChanged() {
|
||||
if (this.workspaceEnabled) {
|
||||
this.initializeWorkspaces();
|
||||
} else {
|
||||
this._workspaceCache = null;
|
||||
document.getElementById("zen-workspaces-button")?.remove();
|
||||
for (let tab of gBrowser.tabs) {
|
||||
gBrowser.showTab(tab);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async initializeWorkspaces() {
|
||||
Services.prefs.addObserver("zen.workspaces.enabled", this.onWorkspacesEnabledChanged.bind(this));
|
||||
this.initializeWorkspacesButton();
|
||||
let file = new FileUtils.File(this._storeFile);
|
||||
if (!file.exists()) {
|
||||
await IOUtils.writeJSON(this._storeFile, {});
|
||||
}
|
||||
if (this.workspaceEnabled) {
|
||||
let workspaces = await this._workspaces();
|
||||
if (workspaces.workspaces.length === 0) {
|
||||
await this.createAndSaveWorkspace("Default Workspace", true);
|
||||
} else {
|
||||
let activeWorkspace = workspaces.workspaces.find(workspace => workspace.default);
|
||||
if (!activeWorkspace) {
|
||||
activeWorkspace = workspaces.workspaces.find(workspace => workspace.used);
|
||||
activeWorkspace.used = true;
|
||||
await this.saveWorkspaces();
|
||||
}
|
||||
if (!activeWorkspace) {
|
||||
activeWorkspace = workspaces.workspaces[0];
|
||||
activeWorkspace.used = true;
|
||||
await this.saveWorkspaces();
|
||||
}
|
||||
await this.changeWorkspace(activeWorkspace);
|
||||
}
|
||||
this._initializeWorkspaceIcons();
|
||||
}
|
||||
},
|
||||
|
||||
_initializeWorkspaceIcons() {
|
||||
const kIcons = ["🏠", "📄", "💹", "💼", "📧", "✅", "👥"];
|
||||
let container = document.getElementById("PanelUI-zen-workspaces-create-icons-container");
|
||||
for (let icon of kIcons) {
|
||||
let button = document.createXULElement("toolbarbutton");
|
||||
button.className = "toolbarbutton-1";
|
||||
button.setAttribute("label", icon);
|
||||
button.onclick = ((event) => {
|
||||
for (let button of container.children) {
|
||||
button.removeAttribute("selected");
|
||||
}
|
||||
button.setAttribute("selected", "true");
|
||||
}).bind(this, button);
|
||||
container.appendChild(button);
|
||||
}
|
||||
},
|
||||
|
||||
async saveWorkspace(workspaceData) {
|
||||
let json = await IOUtils.readJSON(this._storeFile);
|
||||
if (typeof json.workspaces === "undefined") {
|
||||
json.workspaces = [];
|
||||
}
|
||||
json.workspaces.push(workspaceData);
|
||||
console.log("ZenWorkspaces: Saving workspace", workspaceData);
|
||||
await IOUtils.writeJSON(this._storeFile, json);
|
||||
this._workspaceCache = null;
|
||||
},
|
||||
|
||||
async removeWorkspace(windowID) {
|
||||
let json = await this._workspaces();
|
||||
console.log("ZenWorkspaces: Removing workspace", windowID);
|
||||
await this.changeWorkspace(json.workspaces.find(workspace => workspace.uuid !== windowID));
|
||||
this._deleteAllTabsInWorkspace(windowID);
|
||||
json.workspaces = json.workspaces.filter(workspace => workspace.uuid !== windowID);
|
||||
await this.unsafeSaveWorkspaces(json);
|
||||
await this._propagateWorkspaceData();
|
||||
},
|
||||
|
||||
async saveWorkspaces() {
|
||||
await IOUtils.writeJSON(this._storeFile, await this._workspaces());
|
||||
this._workspaceCache = null;
|
||||
},
|
||||
|
||||
async unsafeSaveWorkspaces(workspaces) {
|
||||
await IOUtils.writeJSON(this._storeFile, workspaces);
|
||||
this._workspaceCache = null;
|
||||
},
|
||||
|
||||
// Workspaces dialog UI management
|
||||
|
||||
openSaveDialog() {
|
||||
let parentPanel = document.getElementById("PanelUI-zen-workspaces-multiview");
|
||||
PanelUI.showSubView("PanelUI-zen-workspaces-create", parentPanel);
|
||||
},
|
||||
|
||||
cancelWorkspaceCreation() {
|
||||
let parentPanel = document.getElementById("PanelUI-zen-workspaces-multiview");
|
||||
parentPanel.goBack();
|
||||
},
|
||||
|
||||
workspaceHasIcon(workspace) {
|
||||
return typeof workspace.icon !== "undefined" && workspace.icon !== "";
|
||||
},
|
||||
|
||||
getWorkspaceIcon(workspace) {
|
||||
if (this.workspaceHasIcon(workspace)) {
|
||||
return workspace.icon;
|
||||
}
|
||||
return workspace.name[0].toUpperCase();
|
||||
},
|
||||
|
||||
async _propagateWorkspaceData() {
|
||||
let currentContainer = document.getElementById("PanelUI-zen-workspaces-current-info");
|
||||
let workspaceList = document.getElementById("PanelUI-zen-workspaces-list");
|
||||
const createWorkspaceElement = (workspace) => {
|
||||
let element = document.createXULElement("toolbarbutton");
|
||||
element.className = "subviewbutton";
|
||||
element.setAttribute("tooltiptext", workspace.name);
|
||||
element.setAttribute("zen-workspace-id", workspace.uuid);
|
||||
//element.setAttribute("context", "zenWorkspaceActionsMenu");
|
||||
let childs = window.MozXULElement.parseXULToFragment(`
|
||||
<div class="zen-workspace-icon">
|
||||
${this.getWorkspaceIcon(workspace)}
|
||||
</div>
|
||||
<div class="zen-workspace-name">
|
||||
${workspace.name}
|
||||
</div>
|
||||
<toolbarbutton closemenu="none" class="toolbarbutton-1 zen-workspace-actions">
|
||||
<image class="toolbarbutton-icon" id="zen-workspace-actions-menu-icon"></image>
|
||||
</toolbarbutton>
|
||||
`);
|
||||
childs.querySelector(".zen-workspace-actions").addEventListener("command", ((event) => {
|
||||
let button = event.target;
|
||||
this._contextMenuId = button.closest("toolbarbutton[zen-workspace-id]").getAttribute("zen-workspace-id");
|
||||
const popup = button.ownerDocument.getElementById(
|
||||
"zenWorkspaceActionsMenu"
|
||||
);
|
||||
popup.openPopup(button, "after_end");
|
||||
}).bind(this));
|
||||
element.appendChild(childs);
|
||||
element.onclick = (async () => {
|
||||
if (event.target.closest(".zen-workspace-actions")) {
|
||||
return; // Ignore clicks on the actions button
|
||||
}
|
||||
await this.changeWorkspace(workspace)
|
||||
let panel = document.getElementById("PanelUI-zen-workspaces");
|
||||
PanelMultiView.hidePopup(panel);
|
||||
}).bind(this, workspace);
|
||||
return element;
|
||||
}
|
||||
let workspaces = await this._workspaces();
|
||||
let activeWorkspace = workspaces.workspaces.find(workspace => workspace.used);
|
||||
currentContainer.innerHTML = "";
|
||||
workspaceList.innerHTML = "";
|
||||
workspaceList.parentNode.style.display = "flex";
|
||||
if (workspaces.workspaces.length - 1 <= 0) {
|
||||
workspaceList.innerHTML = "No workspaces available";
|
||||
workspaceList.setAttribute("empty", "true");
|
||||
} else {
|
||||
workspaceList.removeAttribute("empty");
|
||||
}
|
||||
if (activeWorkspace) {
|
||||
let currentWorkspace = createWorkspaceElement(activeWorkspace);
|
||||
currentContainer.appendChild(currentWorkspace);
|
||||
}
|
||||
for (let workspace of workspaces.workspaces) {
|
||||
if (workspace.used) {
|
||||
continue;
|
||||
}
|
||||
let workspaceElement = createWorkspaceElement(workspace);
|
||||
workspaceList.appendChild(workspaceElement);
|
||||
}
|
||||
},
|
||||
|
||||
async openWorkspacesDialog(event) {
|
||||
if (!this.workspaceEnabled) {
|
||||
return;
|
||||
}
|
||||
let target = event.target;
|
||||
let panel = document.getElementById("PanelUI-zen-workspaces");
|
||||
await this._propagateWorkspaceData();
|
||||
PanelMultiView.openPopup(panel, target, {
|
||||
position: "bottomright topright",
|
||||
triggerEvent: event,
|
||||
}).catch(console.error);
|
||||
},
|
||||
|
||||
initializeWorkspacesButton() {
|
||||
if (!this.workspaceEnabled) {
|
||||
return;
|
||||
} else if (document.getElementById("zen-workspaces-button")) {
|
||||
let button = document.getElementById("zen-workspaces-button");
|
||||
button.removeAttribute("hidden");
|
||||
return;
|
||||
}
|
||||
let browserTabs = document.getElementById("tabbrowser-tabs");
|
||||
let button = document.createElement("toolbarbutton");
|
||||
button.id = "zen-workspaces-button";
|
||||
button.className = "toolbarbutton-1 chromeclass-toolbar-additional";
|
||||
button.setAttribute("label", "Workspaces");
|
||||
button.setAttribute("tooltiptext", "Workspaces");
|
||||
button.onclick = this.openWorkspacesDialog.bind(this);
|
||||
browserTabs.insertAdjacentElement("beforebegin", button);
|
||||
},
|
||||
|
||||
async _updateWorkspacesButton() {
|
||||
let button = document.getElementById("zen-workspaces-button");
|
||||
if (!button) {
|
||||
return;
|
||||
}
|
||||
let activeWorkspace = (await this._workspaces()).workspaces.find(workspace => workspace.used);
|
||||
if (activeWorkspace) {
|
||||
button.innerHTML = `
|
||||
<div class="zen-workspace-sidebar-icon">
|
||||
${this.getWorkspaceIcon(activeWorkspace)}
|
||||
</div>
|
||||
<div class="zen-workspace-sidebar-name">
|
||||
${activeWorkspace.name}
|
||||
</div>
|
||||
`;
|
||||
if (!this.workspaceHasIcon(activeWorkspace)) {
|
||||
button.querySelector(".zen-workspace-sidebar-icon").setAttribute("no-icon", "true");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Workspaces management
|
||||
|
||||
get _workspaceInput() {
|
||||
return document.getElementById("PanelUI-zen-workspaces-create-input");
|
||||
},
|
||||
|
||||
_deleteAllTabsInWorkspace(workspaceID) {
|
||||
for (let tab of gBrowser.tabs) {
|
||||
if (tab.getAttribute("zen-workspace-id") === workspaceID) {
|
||||
gBrowser.removeTab(tab, {
|
||||
animate: true,
|
||||
skipSessionStore: true,
|
||||
closeWindowWithLastTab: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_prepareNewWorkspace(window) {
|
||||
document.documentElement.setAttribute("zen-workspace-id", window.uuid);
|
||||
let tabCount = 0;
|
||||
for (let tab of gBrowser.tabs) {
|
||||
if (!tab.hasAttribute("zen-workspace-id")) {
|
||||
tab.setAttribute("zen-workspace-id", window.uuid);
|
||||
tabCount++;
|
||||
}
|
||||
}
|
||||
if (tabCount === 0) {
|
||||
this._createNewTabForWorkspace(window);
|
||||
}
|
||||
},
|
||||
|
||||
_createNewTabForWorkspace(window) {
|
||||
let tab = gZenUIManager.openAndChangeToTab(Services.prefs.getStringPref("browser.startup.homepage"));
|
||||
tab.setAttribute("zen-workspace-id", window.uuid);
|
||||
},
|
||||
|
||||
async saveWorkspaceFromInput() {
|
||||
// Go to the next view
|
||||
let parentPanel = document.getElementById("PanelUI-zen-workspaces-multiview");
|
||||
PanelUI.showSubView("PanelUI-zen-workspaces-create-icons", parentPanel);
|
||||
},
|
||||
|
||||
async saveWorkspaceFromIcon() {
|
||||
let workspaceName = this._workspaceInput.value;
|
||||
if (!workspaceName) {
|
||||
return;
|
||||
}
|
||||
this._workspaceInput.value = "";
|
||||
let icon = document.querySelector("#PanelUI-zen-workspaces-create-icons-container [selected]");
|
||||
icon?.removeAttribute("selected");
|
||||
await this.createAndSaveWorkspace(workspaceName, false, icon?.label);
|
||||
document.getElementById("PanelUI-zen-workspaces").hidePopup(true);
|
||||
},
|
||||
|
||||
onWorkspaceNameChange(event) {
|
||||
let button = document.getElementById("PanelUI-zen-workspaces-create-save");
|
||||
if (this._workspaceInput.value === "") {
|
||||
button.setAttribute("disabled", "true");
|
||||
return;
|
||||
}
|
||||
button.removeAttribute("disabled");
|
||||
},
|
||||
|
||||
async changeWorkspace(window) {
|
||||
if (!this.workspaceEnabled) {
|
||||
return;
|
||||
}
|
||||
let firstTab = undefined;
|
||||
let workspaces = await this._workspaces();
|
||||
for (let workspace of workspaces.workspaces) {
|
||||
workspace.used = workspace.uuid === window.uuid;
|
||||
}
|
||||
this.unsafeSaveWorkspaces(workspaces);
|
||||
console.log("ZenWorkspaces: Changing workspace to", window.uuid);
|
||||
for (let tab of gBrowser.tabs) {
|
||||
if (tab.getAttribute("zen-workspace-id") === window.uuid && !tab.pinned) {
|
||||
if (!firstTab) {
|
||||
firstTab = tab;
|
||||
gBrowser.selectedTab = firstTab;
|
||||
}
|
||||
gBrowser.showTab(tab);
|
||||
}
|
||||
}
|
||||
if (typeof firstTab === "undefined") {
|
||||
this._createNewTabForWorkspace(window);
|
||||
}
|
||||
for (let tab of gBrowser.tabs) {
|
||||
if (tab.getAttribute("zen-workspace-id") !== window.uuid) {
|
||||
gBrowser.hideTab(tab);
|
||||
}
|
||||
}
|
||||
document.documentElement.setAttribute("zen-workspace-id", window.uuid);
|
||||
await this.saveWorkspaces();
|
||||
await this._updateWorkspacesButton();
|
||||
await this._propagateWorkspaceData();
|
||||
},
|
||||
|
||||
_createWorkspaceData(name, isDefault, icon) {
|
||||
let window = {
|
||||
uuid: gZenUIManager.generateUuidv4(),
|
||||
default: isDefault,
|
||||
used: true,
|
||||
icon: icon,
|
||||
name: name,
|
||||
};
|
||||
this._prepareNewWorkspace(window);
|
||||
return window;
|
||||
},
|
||||
|
||||
async createAndSaveWorkspace(name = "New Workspace", isDefault = false, icon = undefined) {
|
||||
if (!this.workspaceEnabled) {
|
||||
return;
|
||||
}
|
||||
let workspaceData = this._createWorkspaceData(name, isDefault, icon);
|
||||
await this.saveWorkspace(workspaceData);
|
||||
await this.changeWorkspace(workspaceData);
|
||||
},
|
||||
|
||||
async onLocationChange(browser) {
|
||||
let tab = gBrowser.getTabForBrowser(browser);
|
||||
let workspaceID = tab.getAttribute("zen-workspace-id");
|
||||
if (!workspaceID) {
|
||||
let workspaces = await this._workspaces();
|
||||
let activeWorkspace = workspaces.workspaces.find(workspace => workspace.used);
|
||||
if (!activeWorkspace) {
|
||||
return;
|
||||
}
|
||||
tab.setAttribute("zen-workspace-id", activeWorkspace.uuid);
|
||||
}
|
||||
},
|
||||
|
||||
// Context menu management
|
||||
|
||||
_contextMenuId: null,
|
||||
async updateContextMenu(_) {
|
||||
console.assert(this._contextMenuId, "No context menu ID set");
|
||||
document.querySelector(`#PanelUI-zen-workspaces [zen-workspace-id="${this._contextMenuId}"] .zen-workspace-actions`).setAttribute("active", "true");
|
||||
const workspaces = await this._workspaces();
|
||||
let deleteMenuItem = document.getElementById("context_zenDeleteWorkspace");
|
||||
if (workspaces.workspaces.length <= 1 || workspaces.workspaces.find(workspace => workspace.uuid === this._contextMenuId).default) {
|
||||
deleteMenuItem.setAttribute("disabled", "true");
|
||||
} else {
|
||||
deleteMenuItem.removeAttribute("disabled");
|
||||
}
|
||||
},
|
||||
|
||||
onContextMenuClose() {
|
||||
let target = document.querySelector(`#PanelUI-zen-workspaces [zen-workspace-id="${this._contextMenuId}"] .zen-workspace-actions`);
|
||||
if (target) {
|
||||
target.removeAttribute("active");
|
||||
}
|
||||
this._contextMenuId = null;
|
||||
},
|
||||
|
||||
async contextDelete() {
|
||||
await this.removeWorkspace(this._contextMenuId);
|
||||
}
|
||||
};
|
||||
|
||||
ZenWorkspaces.init();
|
|
@ -1,330 +0,0 @@
|
|||
|
||||
class SplitViewsUtils {
|
||||
/**
|
||||
* @returns {HTMLDivElement}
|
||||
*/
|
||||
get tabBrowser() {
|
||||
if (!this._tabBrowser) {
|
||||
this._tabBrowser = document.getElementById('tabbrowser-tabpanels');
|
||||
}
|
||||
// @ts-ignore
|
||||
return this._tabBrowser;
|
||||
}
|
||||
}
|
||||
|
||||
class SplitViewsBase extends SplitViewsUtils {
|
||||
/**
|
||||
* @type {SplitView[]}
|
||||
*/
|
||||
data;
|
||||
|
||||
/**
|
||||
* @param {SplitViewConfig} config
|
||||
*/
|
||||
constructor(config) {
|
||||
super();
|
||||
this.config = config;
|
||||
this.data = [];
|
||||
this.currentView = -1;
|
||||
this.globalIdCounter = 0;
|
||||
// Added to "navigator-toolbox" element
|
||||
this.parentSplitIndicator = this.config.splitIndicator + '-view';
|
||||
this.log('SplitViewsBase initialized');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} message
|
||||
* @protected
|
||||
*/
|
||||
log(message) {
|
||||
console.log(`SplitViews: ${message}`);
|
||||
}
|
||||
|
||||
get isActivated() {
|
||||
return this.currentView !== -1;
|
||||
}
|
||||
|
||||
get activeView() {
|
||||
if (!this.isActivated) {
|
||||
throw new Error('No active view');
|
||||
}
|
||||
return this.data[this.currentView];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MockedExports.BrowserTab} tab
|
||||
*/
|
||||
getTabView(tab) {
|
||||
return this.data.find(view => view.tabs.includes(tab));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MockedExports.BrowserTab} tab
|
||||
*/
|
||||
isTabSplit(tab) {
|
||||
return tab.hasAttribute(this.config.splitIndicator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MockedExports.BrowserTab} tab
|
||||
* @param {SplitType} type
|
||||
* @param {MockedExports.BrowserTab[]} tabs
|
||||
*/
|
||||
changeSplitViewBase(tab, type, tabs) {
|
||||
let view = this.getTabView(tab);
|
||||
if (!view) {
|
||||
return -1;
|
||||
}
|
||||
view.type = type;
|
||||
view.tabs.push(...tabs.filter(t => !view.tabs.includes(t)));
|
||||
return view.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MockedExports.BrowserTab[]} tabs
|
||||
* @param {SplitType} type
|
||||
*/
|
||||
createSplitViewBase(tabs, type) {
|
||||
let view = {
|
||||
id: this.globalIdCounter++,
|
||||
type,
|
||||
tabs,
|
||||
};
|
||||
this.data.push(view);
|
||||
this.currentView = this.data.length - 1;
|
||||
return view.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the grid layout to the tabs.
|
||||
*
|
||||
* @param {MockedExports.BrowserTab[]} tabs - The tabs to apply the grid layout to.
|
||||
* @param {string} gridType - The type of grid layout.
|
||||
* @param {MockedExports.BrowserTab} activeTab - The active tab.
|
||||
*/
|
||||
applyGridLayout(tabs, gridType, activeTab) {
|
||||
const gridAreas = this.calculateGridAreas(tabs, gridType);
|
||||
this.tabBrowser.style.gridTemplateAreas = gridAreas;
|
||||
|
||||
tabs.forEach((tab, index) => {
|
||||
tab.setAttribute(this.config.splitIndicator, "true");
|
||||
const container = tab.linkedBrowser.closest(".browserSidebarContainer");
|
||||
if (!container) {
|
||||
throw new Error("Container not found");
|
||||
}
|
||||
this.styleContainer(container, tab === activeTab, index, gridType);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Styles the container for a tab.
|
||||
*
|
||||
* @param {Element} container - The container element.
|
||||
* @param {boolean} isActive - Indicates if the tab is active.
|
||||
* @param {number} index - The index of the tab.
|
||||
* @param {string} gridType - The type of grid layout.
|
||||
*/
|
||||
styleContainer(container, isActive, index, gridType) {
|
||||
container.removeAttribute("split-active");
|
||||
container.setAttribute(this.config.splitIndicator, "true");
|
||||
if (isActive) {
|
||||
container.setAttribute("split-active", "true");
|
||||
}
|
||||
container.setAttribute("split-anim", "true");
|
||||
container.addEventListener("click", this.handleTabClick);
|
||||
|
||||
if (gridType === "grid") {
|
||||
// @ts-ignore
|
||||
container.style.gridArea = `tab${index + 1}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the grid areas for the tabs.
|
||||
*
|
||||
* @param {MockedExports.BrowserTab[]} tabs - The tabs.
|
||||
* @param {string} gridType - The type of grid layout.
|
||||
* @returns {string} The calculated grid areas.
|
||||
*/
|
||||
calculateGridAreas(tabs, gridType) {
|
||||
if (gridType === "grid") {
|
||||
return this.calculateGridAreasForGrid(tabs);
|
||||
}
|
||||
if (gridType === "vsep") {
|
||||
return `'${tabs.map((_, j) => `tab${j + 1}`).join(" ")}'`;
|
||||
}
|
||||
if (gridType === "hsep") {
|
||||
return tabs.map((_, j) => `'tab${j + 1}'`).join(" ");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the tab click event.
|
||||
*
|
||||
* @param {Event} event - The click event.
|
||||
*/
|
||||
handleTabClick(event) {
|
||||
const container = event.currentTarget;
|
||||
// @ts-ignore
|
||||
const tab = window.gBrowser.tabs.find(
|
||||
// @ts-ignore
|
||||
t => t.linkedBrowser.closest(".browserSidebarContainer") === container
|
||||
);
|
||||
if (tab) {
|
||||
// @ts-ignore
|
||||
window.gBrowser.selectedTab = tab;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculates the grid areas for the tabs in a grid layout.
|
||||
*
|
||||
* @param {MockedExports.BrowserTab[]} tabs - The tabs.
|
||||
* @returns {string} The calculated grid areas.
|
||||
*/
|
||||
calculateGridAreasForGrid(tabs) {
|
||||
const rows = ["", ""];
|
||||
tabs.forEach((_, i) => {
|
||||
if (i % 2 === 0) {
|
||||
rows[0] += ` tab${i + 1}`;
|
||||
} else {
|
||||
rows[1] += ` tab${i + 1}`;
|
||||
}
|
||||
});
|
||||
|
||||
if (tabs.length === 2) {
|
||||
return "'tab1 tab2'";
|
||||
}
|
||||
|
||||
if (tabs.length % 2 !== 0) {
|
||||
rows[1] += ` tab${tabs.length}`;
|
||||
}
|
||||
|
||||
return `'${rows[0].trim()}' '${rows[1].trim()}'`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} viewId
|
||||
* @protected
|
||||
*/
|
||||
updateSplitView(viewId) {
|
||||
let view = this.data.find(view => view.id === viewId);
|
||||
this.log(`updateSplitView: ${viewId}`);
|
||||
this.currentView = viewId;
|
||||
if (!view) {
|
||||
this.tabBrowser.removeAttribute(this.parentSplitIndicator);
|
||||
throw new Error('TODO: Remove split view');
|
||||
return;
|
||||
}
|
||||
this.tabBrowser.setAttribute(this.parentSplitIndicator, "true");
|
||||
this.applyGridLayout(view.tabs, view.type, view.tabs[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MockedExports.BrowserTab[]} tabs
|
||||
* @param {SplitType} type
|
||||
* @protected
|
||||
*/
|
||||
createOrChangeSplitView(tabs, type) {
|
||||
let activeTab = tabs.find(tab => this.isTabSplit(tab));
|
||||
this.log(`createOrChangeSplitView: ${type}`);
|
||||
let viewId = -1;
|
||||
if (activeTab) {
|
||||
viewId = this.changeSplitViewBase(activeTab, type, tabs);
|
||||
} else {
|
||||
viewId = this.createSplitViewBase(tabs, type);
|
||||
}
|
||||
this.updateSplitView(viewId);
|
||||
}
|
||||
}
|
||||
|
||||
// Public API exposed by the module
|
||||
export class SplitViews extends SplitViewsBase {
|
||||
/**
|
||||
* @param {SplitViewConfig} config
|
||||
*/
|
||||
constructor(config) {
|
||||
super(config);
|
||||
this.addEventListeners();
|
||||
}
|
||||
|
||||
addEventListeners() {
|
||||
window.addEventListener('TabClose', this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Event} event
|
||||
*/
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case 'TabClose':
|
||||
this.onTabClose(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Event} event
|
||||
*/
|
||||
// @ts-ignore
|
||||
// @ts-ignore
|
||||
onTabClose(event) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MockedExports.Browser} browser
|
||||
*/
|
||||
// @ts-ignore
|
||||
// @ts-ignore
|
||||
onLocationChange(browser) {
|
||||
this.log('onLocationChange');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SplitType} type
|
||||
*/
|
||||
// @ts-ignore
|
||||
// @ts-ignore
|
||||
tileCurrentView(type) {
|
||||
this.log('tileCurrentView');
|
||||
}
|
||||
|
||||
closeCurrentView() {
|
||||
this.log('closeCurrentView');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MockedExports.BrowserTab} tab
|
||||
*/
|
||||
// @ts-ignore
|
||||
// @ts-ignore
|
||||
tabIsInActiveView(tab) {
|
||||
this.log('tabIsInActiveView');
|
||||
return false;
|
||||
}
|
||||
|
||||
getActiveViewTabs() {
|
||||
this.log('getActiveViewTabs');
|
||||
return [];
|
||||
}
|
||||
|
||||
getActiveViewType() {
|
||||
if (!this.isActivated) {
|
||||
return undefined;
|
||||
}
|
||||
return this.activeView.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MockedExports.BrowserTab[]} tabs
|
||||
* @param {SplitType} type
|
||||
* @public
|
||||
*/
|
||||
createSplitView(tabs, type = this.config.defaultSplitView) {
|
||||
if (tabs.length < 2) {
|
||||
return;
|
||||
}
|
||||
this.createOrChangeSplitView(tabs, type);
|
||||
}
|
||||
};
|
106
tsconfig.json
106
tsconfig.json
|
@ -1,106 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
|
||||
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// Make the type checking as strict as possible.
|
||||
// TypeScript will check JS files only if they have a @ts-check comment in them.
|
||||
"allowJs": true,
|
||||
// Allow esnext syntax. Otherwise the default is ES5 only.
|
||||
"target": "esnext",
|
||||
"lib": ["esnext", "dom"],
|
||||
|
||||
/* Modules */
|
||||
"module": "CommonJS", /* Specify what module code is generated. */
|
||||
"rootDir": "./src", /* Specify the root folder within your source files. */
|
||||
// "moduleResolution": "NodeNext", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
|
||||
"typeRoots": ["./@types"], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files */
|
||||
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
|
||||
"checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
|
||||
|
||||
/* Emit */
|
||||
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
"emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "outFile": "./dist/components.js", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
|
||||
"outDir": "./dist", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
|
||||
// "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue