diff --git a/src/ZenEssentialsToolbar.mjs b/src/ZenEssentialsToolbar.mjs new file mode 100644 index 0000000..14446a0 --- /dev/null +++ b/src/ZenEssentialsToolbar.mjs @@ -0,0 +1,806 @@ + +class ZenEssentialsToolbar extends PlacesViewBase { + constructor(placesUrl, rootElt, viewElt) { + // We'll initialize the places URL after ensuring the folder exists + super(null, rootElt, viewElt); + // Do initialization of properties that don't depend on Places + this._init(); + this._initPlacesFolder(); + } + + get _accordionHeader() { + return document.getElementById("essentials-accordion-header"); + } + + async _initPlacesFolder() { + try { + const ESSENTIALS_GUID = "pfgqteRgY-Wr"; // Fixed GUID for the folder + + // First try to fetch by GUID since it's more efficient + let folder = await PlacesUtils.bookmarks.fetch(ESSENTIALS_GUID ); + + if (!folder) { + // If not found by GUID, try creating with our specific GUID + // try { + folder = await PlacesUtils.bookmarks.insert({ + type: PlacesUtils.bookmarks.TYPE_FOLDER, + guid: ESSENTIALS_GUID, + title: "Zen Essentials", + parentGuid: PlacesUtils.bookmarks.menuGuid + }); + } + + // Ensure the folder is in the right place with the right title + if (folder.parentGuid !== PlacesUtils.bookmarks.menuGuid || + folder.title !== "Zen Essentials") { + await PlacesUtils.bookmarks.update({ + guid: folder.guid, + title: "Zen Essentials", + parentGuid: PlacesUtils.bookmarks.menuGuid, + index: folder.index + }); + } + + if(!folder) { + console.error("Failed to initialize ZenEssentials folder"); + return; + } + + // Now that we have the folder, set up the places URL + this.place = `place:parent=${folder.guid}`; + + // Initialize view event listeners and setup + this._setupEventListeners(); + + } catch (ex) { + console.error("Failed to initialize ZenEssentials folder:", ex); + } + } + + _updateEssentialsVisibility() { + // Get current preference value + this.isEssentialsEnabled = Services.prefs.getBoolPref("zen.essentials.enabled", false); + + // Update visibility + if (this._rootElt) { + this._rootElt.hidden = !this.isEssentialsEnabled; + } + + if (this._accordionHeader) { + this._accordionHeader.style.display = this.isEssentialsEnabled ? "flex" : "none"; + } + } + + // Called by PlacesViewBase during initialization + _init() { + this._prefObserver = (() => { + this._updateEssentialsVisibility(); + }).bind(this); + + // Get initial preference value and set up observer + Services.prefs.addObserver("zen.essentials.enabled", this._prefObserver); + + // Initialize visibility based on current pref value + this.isEssentialsEnabled = Services.prefs.getBoolPref("zen.essentials.enabled", false); + this._updateEssentialsVisibility(); + this._overFolder = { + elt: null, + openTimer: null, + hoverTime: 350, + closeTimer: null, + }; + + let thisView = this; + [ + ["_dropIndicator", "EssentialsToolbarDropIndicator"], + ].forEach(function (elementGlobal) { + let [name, id] = elementGlobal; + thisView.__defineGetter__(name, function () { + let element = document.getElementById(id); + if (!element) { + return null; + } + delete thisView[name]; + return (thisView[name] = element); + }); + }); + + // Initialize base properties + this._viewElt._placesView = this; + this._dragRoot = this._viewElt; + } + + _setupEventListeners() { + // Add standard event listeners + this._addEventListeners(this._dragRoot, this._cbEvents, false); + this._addEventListeners(this._rootElt, ["popupshowing", "popuphidden"], true); + this._addEventListeners(window, ["unload"], false); + } + + _cbEvents = [ + "dragstart", + "dragover", + "dragleave", + "dragend", + "drop", + "mousemove", + "mouseover", + "mouseout", + "mousedown", + ]; + + QueryInterface = ChromeUtils.generateQI([ + "nsINamed", + "nsITimerCallback", + ...PlacesViewBase.interfaces, + ]); + + uninit() { + if (this._prefObserver) { + Services.prefs.removeObserver("zen.essentials.enabled", this._prefObserver); + this._prefObserver = null; + } + if (this._dragRoot) { + this._removeEventListeners(this._dragRoot, this._cbEvents, false); + } + this._removeEventListeners( + this._rootElt, + ["popupshowing", "popuphidden"], + true + ); + this._removeEventListeners(window, ["unload"], false); + + super.uninit(); + } + + _allowPopupShowing = true; + + + + get _isAlive() { + return this._resultNode && this._rootElt; + } + + async _rebuild() { + if (this._overFolder.elt) { + this._clearOverFolder(); + } + + while (this._rootElt.hasChildNodes()) { + this._rootElt.firstChild.remove(); + } + + let cc = this._resultNode.childCount; + if (cc > 0) { + for (let i = 0; i < cc; i++) { + this._insertNewItem(this._resultNode.getChild(i), this._rootElt); + } + } + } + + _insertNewItem(aChild, aInsertionNode, aBefore = null) { + this._domNodes.delete(aChild); + + let type = aChild.type; + let button; + if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) { + button = document.createXULElement("toolbarseparator"); + } else { + button = document.createXULElement("toolbarbutton"); + button.className = "bookmark-item"; + button.setAttribute("label", aChild.title || ""); + + if (PlacesUtils.containerTypes.includes(type)) { + button.setAttribute("type", "menu"); + button.setAttribute("container", "true"); + + if (PlacesUtils.nodeIsQuery(aChild)) { + button.setAttribute("query", "true"); + if (PlacesUtils.nodeIsTagQuery(aChild)) { + button.setAttribute("tagContainer", "true"); + } + } + + let popup = document.createXULElement("menupopup", { + is: "places-popup", + }); + popup.setAttribute("placespopup", "true"); + popup.classList.add("toolbar-menupopup"); + button.appendChild(popup); + popup._placesNode = PlacesUtils.asContainer(aChild); + popup.setAttribute("context", "placesContext"); + + this._domNodes.set(aChild, popup); + } else if (PlacesUtils.nodeIsURI(aChild)) { + button.setAttribute( + "scheme", + PlacesUIUtils.guessUrlSchemeForUI(aChild.uri) + ); + button.hidden = ZenWorkspaces.isBookmarkInAnotherWorkspace(aChild); + button.addEventListener("command", gZenGlanceManager.openGlanceForBookmark.bind(gZenGlanceManager)); + } + } + + button._placesNode = aChild; + let { icon } = button._placesNode; + if (icon) { + button.setAttribute("image", icon); + } + if (!this._domNodes.has(aChild)) { + this._domNodes.set(aChild, button); + } + + if (aBefore) { + aInsertionNode.insertBefore(button, aBefore); + } else { + aInsertionNode.appendChild(button); + } + return button; + } + + handleEvent(aEvent) { + switch (aEvent.type) { + case "unload": + this.uninit(); + break; + case "dragstart": + this._onDragStart(aEvent); + break; + case "dragover": + this._onDragOver(aEvent); + break; + case "dragleave": + this._onDragLeave(aEvent); + break; + case "dragend": + this._onDragEnd(aEvent); + break; + case "drop": + this._onDrop(aEvent); + break; + case "mouseover": + this._onMouseOver(aEvent); + break; + case "mousemove": + this._onMouseMove(aEvent); + break; + case "mouseout": + this._onMouseOut(aEvent); + break; + case "mousedown": + this._onMouseDown(aEvent); + break; + case "popupshowing": + this._onPopupShowing(aEvent); + break; + case "popuphidden": + this._onPopupHidden(aEvent); + break; + default: + throw new Error("Trying to handle unexpected event."); + } + } + + + + nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) { + let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode); + if (parentElt == this._rootElt) { + let children = this._rootElt.children; + if (aIndex > children.length) { + return; + } + + this._insertNewItem( + aPlacesNode, + this._rootElt, + children[aIndex] || null + ); + return; + } + + super.nodeInserted(aParentPlacesNode, aPlacesNode, aIndex); + } + + nodeRemoved(aParentPlacesNode, aPlacesNode, aIndex) { + let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode); + if (parentElt == this._rootElt) { + let elt = this._getDOMNodeForPlacesNode(aPlacesNode, true); + if (!elt) { + return; + } + + if (elt.localName == "menupopup") { + elt = elt.parentNode; + } + + this._removeChild(elt); + if (this._resultNode.childCount > this._rootElt.children.length) { + this._insertNewItem( + this._resultNode.getChild(this._rootElt.children.length), + this._rootElt + ); + } + return; + } + + super.nodeRemoved(aParentPlacesNode, aPlacesNode, aIndex); + } + + nodeMoved(aPlacesNode, aOldParentPlacesNode, aOldIndex, aNewParentPlacesNode, aNewIndex) { + let parentElt = this._getDOMNodeForPlacesNode(aNewParentPlacesNode); + if (parentElt == this._rootElt) { + let elt = this._getDOMNodeForPlacesNode(aPlacesNode, true); + if (elt) { + if (elt.localName == "menupopup") { + elt = elt.parentNode; + } + this._removeChild(elt); + } + + this._insertNewItem( + aPlacesNode, + this._rootElt, + this._rootElt.children[aNewIndex] + ); + return; + } + + super.nodeMoved( + aPlacesNode, + aOldParentPlacesNode, + aOldIndex, + aNewParentPlacesNode, + aNewIndex + ); + } + + nodeTitleChanged(aPlacesNode, aNewTitle) { + let elt = this._getDOMNodeForPlacesNode(aPlacesNode, true); + if (!elt || elt == this._rootElt) { + return; + } + + super.nodeTitleChanged(aPlacesNode, aNewTitle); + + if (elt.localName == "menupopup") { + elt = elt.parentNode; + } + } + + invalidateContainer(aPlacesNode) { + let elt = this._getDOMNodeForPlacesNode(aPlacesNode, true); + // Nothing to do if it's a never-visible node. + if (!elt) { + return; + } + + if (elt == this._rootElt) { + // Container is the toolbar itself. + let instance = (this._rebuildingInstance = {}); + if (!this._rebuilding) { + this._rebuilding = Promise.withResolvers(); + } + this._rebuild() + .catch(console.error) + .finally(() => { + if (instance == this._rebuildingInstance) { + this._rebuilding.resolve(); + this._rebuilding = null; + } + }); + return; + } + + super.invalidateContainer(aPlacesNode); + } + + _clearOverFolder() { + // The mouse is no longer dragging over the stored menubutton. + // Close the menubutton, clear out drag styles, and clear all + // timers for opening/closing it. + if (this._overFolder.elt && this._overFolder.elt.menupopup) { + if (!this._overFolder.elt.menupopup.hasAttribute("dragover")) { + this._overFolder.elt.menupopup.hidePopup(); + } + this._overFolder.elt.removeAttribute("dragover"); + this._overFolder.elt = null; + } + if (this._overFolder.openTimer) { + this._overFolder.openTimer.cancel(); + this._overFolder.openTimer = null; + } + if (this._overFolder.closeTimer) { + this._overFolder.closeTimer.cancel(); + this._overFolder.closeTimer = null; + } + } + + /** + * Determines the drop target while dragging over the vertical toolbar. + * + * @param {object} aEvent + * The drag event. + * @returns {object} + * - ip: The insertion point for the bookmarks service. + * - beforeIndex: Child index to drop before, for the drop indicator. + * - folderElt: The folder to drop into, if applicable. + */ + _getDropPoint(aEvent) { + if (!PlacesUtils.nodeIsFolderOrShortcut(this._resultNode)) { + return null; + } + + let dropPoint = { ip: null, beforeIndex: null, folderElt: null }; + let elt = aEvent.target; + + // If we're not dragging over a child element, handle dropping at the end + if (!elt._placesNode || elt == this._rootElt || elt.localName == "menupopup") { + dropPoint.ip = new PlacesInsertionPoint({ + parentGuid: PlacesUtils.getConcreteItemGuid(this._resultNode), + orientation: Ci.nsITreeView.DROP_BEFORE + }); + dropPoint.beforeIndex = -1; + + // Find the closest child based on vertical position + for (let i = 0; i < this._rootElt.children.length; i++) { + let childRect = this._rootElt.children[i].getBoundingClientRect(); + if (aEvent.clientY <= childRect.top) { + dropPoint.beforeIndex = i; + dropPoint.ip.index = i; + break; + } + } + return dropPoint; + } + + // Get target element's position info + let eltRect = elt.getBoundingClientRect(); + let eltIndex = Array.prototype.indexOf.call(this._rootElt.children, elt); + + // Handle dropping on folders + if (PlacesUtils.nodeIsFolderOrShortcut(elt._placesNode) && + !PlacesUIUtils.isFolderReadOnly(elt._placesNode)) { + + // Define drop zones: top 25%, middle 50%, bottom 25% + let topThreshold = eltRect.top + (eltRect.height * 0.25); + let bottomThreshold = eltRect.bottom - (eltRect.height * 0.25); + + if (aEvent.clientY < topThreshold) { + // Drop before folder + dropPoint.ip = new PlacesInsertionPoint({ + parentGuid: PlacesUtils.getConcreteItemGuid(this._resultNode), + index: eltIndex, + orientation: Ci.nsITreeView.DROP_BEFORE + }); + dropPoint.beforeIndex = eltIndex; + } else if (aEvent.clientY > bottomThreshold) { + // Drop after folder + let beforeIndex = eltIndex == this._rootElt.children.length - 1 ? -1 : eltIndex + 1; + dropPoint.ip = new PlacesInsertionPoint({ + parentGuid: PlacesUtils.getConcreteItemGuid(this._resultNode), + index: beforeIndex, + orientation: Ci.nsITreeView.DROP_BEFORE + }); + dropPoint.beforeIndex = beforeIndex; + } else { + // Drop inside folder + let tagName = PlacesUtils.nodeIsTagQuery(elt._placesNode) + ? elt._placesNode.title + : null; + dropPoint.ip = new PlacesInsertionPoint({ + parentGuid: PlacesUtils.getConcreteItemGuid(elt._placesNode), + tagName + }); + dropPoint.beforeIndex = eltIndex; + dropPoint.folderElt = elt; + } + } else { + // Handle dropping around non-folder items + let midPoint = eltRect.top + (eltRect.height / 2); + + if (aEvent.clientY < midPoint) { + // Drop before item + dropPoint.ip = new PlacesInsertionPoint({ + parentGuid: PlacesUtils.getConcreteItemGuid(this._resultNode), + index: eltIndex, + orientation: Ci.nsITreeView.DROP_BEFORE + }); + dropPoint.beforeIndex = eltIndex; + } else { + // Drop after item + let beforeIndex = eltIndex == this._rootElt.children.length - 1 ? -1 : eltIndex + 1; + dropPoint.ip = new PlacesInsertionPoint({ + parentGuid: PlacesUtils.getConcreteItemGuid(this._resultNode), + index: beforeIndex, + orientation: Ci.nsITreeView.DROP_BEFORE + }); + dropPoint.beforeIndex = beforeIndex; + } + } + + return dropPoint; + } + + _setTimer(aTime) { + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + timer.initWithCallback(this, aTime, timer.TYPE_ONE_SHOT); + return timer; + } + + get name() { + return "ZenEssentialsToolbar"; + } + + notify(aTimer) { + if (aTimer == this._overFolder.openTimer) { + this._overFolder.elt.menupopup.setAttribute("autoopened", "true"); + this._overFolder.elt.open = true; + this._overFolder.openTimer = null; + } else if (aTimer == this._overFolder.closeTimer) { + let currentPlacesNode = PlacesControllerDragHelper.currentDropTarget; + let inHierarchy = false; + while (currentPlacesNode) { + if (currentPlacesNode == this._rootElt) { + inHierarchy = true; + break; + } + currentPlacesNode = currentPlacesNode.parentNode; + } + if (inHierarchy) { + this._overFolder.elt = null; + } + this._clearOverFolder(); + } + } + + _onMouseOver(aEvent) { + let button = aEvent.target; + if ( + button.parentNode == this._rootElt && + button._placesNode && + PlacesUtils.nodeIsURI(button._placesNode) + ) { + window.XULBrowserWindow.setOverLink(aEvent.target._placesNode.uri); + } + } + + _onMouseOut() { + window.XULBrowserWindow.setOverLink(""); + } + + _onMouseDown(aEvent) { + let target = aEvent.target; + if ( + aEvent.button == 0 && + target.localName == "toolbarbutton" && + target.getAttribute("type") == "menu" + ) { + + + let modifKey = aEvent.shiftKey || aEvent.getModifierState("Accel"); + if (modifKey) { + // Do not open the popup since BEH_onClick is about to + // open all child uri nodes in tabs. + this._allowPopupShowing = false; + } + } + PlacesUIUtils.maybeSpeculativeConnectOnMouseDown(aEvent); + } + + _cleanupDragDetails() { + // Called on dragend and drop. + PlacesControllerDragHelper.currentDropTarget = null; + this._draggedElt = null; + this._dropIndicator.collapsed = true; + } + + _onDragStart(aEvent) { + // Sub menus have their own d&d handlers. + let draggedElt = aEvent.target; + if (draggedElt.parentNode != this._rootElt || !draggedElt._placesNode) { + return; + } + + if ( + draggedElt.localName == "toolbarbutton" && + draggedElt.getAttribute("type") == "menu" + ) { + // If the drag gesture on a container is toward down we open instead + // of dragging. + let translateY = this._cachedMouseMoveEvent.clientY - aEvent.clientY; + let translateX = this._cachedMouseMoveEvent.clientX - aEvent.clientX; + if (translateY >= Math.abs(translateX / 2)) { + // Don't start the drag. + aEvent.preventDefault(); + // Open the menu. + draggedElt.open = true; + return; + } + + // If the menu is open, close it. + if (draggedElt.open) { + draggedElt.menupopup.hidePopup(); + draggedElt.open = false; + } + } + + // Activate the view and cache the dragged element. + this._draggedElt = draggedElt._placesNode; + this._rootElt.focus(); + + this._controller.setDataTransfer(aEvent); + aEvent.stopPropagation(); + } + + _onDragOver(aEvent) { + // Cache the dataTransfer + PlacesControllerDragHelper.currentDropTarget = aEvent.target; + let dt = aEvent.dataTransfer; + + let dropPoint = this._getDropPoint(aEvent); + if ( + !dropPoint || + !dropPoint.ip || + !PlacesControllerDragHelper.canDrop(dropPoint.ip, dt) + ) { + this._dropIndicator.collapsed = true; + aEvent.stopPropagation(); + return; + } + + if (dropPoint.folderElt) { + let overElt = dropPoint.folderElt; + if (this._overFolder.elt != overElt) { + this._clearOverFolder(); + this._overFolder.elt = overElt; + this._overFolder.openTimer = this._setTimer(this._overFolder.hoverTime); + } + if (!this._overFolder.elt.hasAttribute("dragover")) { + this._overFolder.elt.setAttribute("dragover", "true"); + } + + this._dropIndicator.collapsed = true; + } else { + // Dragging over a normal toolbarbutton, + // show indicator bar and move it to the appropriate drop point. + let ind = this._dropIndicator; + ind.parentNode.collapsed = false; + let halfInd = ind.clientWidth / 2; + let translateX; + if (this.isRTL) { + halfInd = Math.ceil(halfInd); + translateX = 0 - this._rootElt.getBoundingClientRect().right - halfInd; + if (this._rootElt.firstElementChild) { + if (dropPoint.beforeIndex == -1) { + translateX += + this._rootElt.lastElementChild.getBoundingClientRect().left; + } else { + translateX += + this._rootElt.children[ + dropPoint.beforeIndex + ].getBoundingClientRect().right; + } + } + } else { + halfInd = Math.floor(halfInd); + translateX = 0 - this._rootElt.getBoundingClientRect().left + halfInd; + if (this._rootElt.firstElementChild) { + if (dropPoint.beforeIndex == -1) { + translateX += + this._rootElt.lastElementChild.getBoundingClientRect().right; + } else { + translateX += + this._rootElt.children[ + dropPoint.beforeIndex + ].getBoundingClientRect().left; + } + } + } + + ind.style.transform = "translate(" + Math.round(translateX) + "px)"; + ind.style.marginInlineStart = -ind.clientWidth + "px"; + ind.collapsed = false; + + // Clear out old folder information. + this._clearOverFolder(); + } + + aEvent.preventDefault(); + aEvent.stopPropagation(); + } + + _onDrop(aEvent) { + PlacesControllerDragHelper.currentDropTarget = aEvent.target; + + let dropPoint = this._getDropPoint(aEvent); + if (dropPoint && dropPoint.ip) { + PlacesControllerDragHelper.onDrop( + dropPoint.ip, + aEvent.dataTransfer + ).catch(console.error); + aEvent.preventDefault(); + } + + this._cleanupDragDetails(); + aEvent.stopPropagation(); + } + + _onDragLeave() { + PlacesControllerDragHelper.currentDropTarget = null; + + this._dropIndicator.collapsed = true; + + // If we hovered over a folder, close it now. + if (this._overFolder.elt) { + this._overFolder.closeTimer = this._setTimer(this._overFolder.hoverTime); + } + } + + _onDragEnd() { + this._cleanupDragDetails(); + } + + _onPopupShowing(aEvent) { + if (!this._allowPopupShowing) { + this._allowPopupShowing = true; + aEvent.preventDefault(); + return; + } + + let parent = aEvent.target.parentNode; + if (parent.localName == "toolbarbutton") { + this._openedMenuButton = parent; + } + + super._onPopupShowing(aEvent); + } + + _onPopupHidden(aEvent) { + let popup = aEvent.target; + let placesNode = popup._placesNode; + // Avoid handling popuphidden of inner views + if ( + placesNode && + PlacesUIUtils.getViewForNode(popup) == this && + // UI performance: folder queries are cheap, keep the resultnode open + // so we don't rebuild its contents whenever the popup is reopened. + !PlacesUtils.nodeIsFolderOrShortcut(placesNode) + ) { + placesNode.containerOpen = false; + } + + let parent = popup.parentNode; + if (parent.localName == "toolbarbutton") { + this._openedMenuButton = null; + // Clear the dragover attribute if present, if we are dragging into a + // folder in the hierachy of current opened popup we don't clear + // this attribute on clearOverFolder. See Notify for closeTimer. + if (parent.hasAttribute("dragover")) { + parent.removeAttribute("dragover"); + } + } + } + + _onMouseMove(aEvent) { + // Used in dragStart to prevent dragging folders when dragging down. + this._cachedMouseMoveEvent = aEvent; + + if ( + this._openedMenuButton == null || + PlacesControllerDragHelper.getSession() + ) { + return; + } + + let target = aEvent.originalTarget; + if ( + this._openedMenuButton != target && + target.localName == "toolbarbutton" && + target.type == "menu" + ) { + this._openedMenuButton.open = false; + target.open = true; + } + } +} \ No newline at end of file diff --git a/src/ZenGlanceManager.mjs b/src/ZenGlanceManager.mjs index 9c401bb..4987d3c 100644 --- a/src/ZenGlanceManager.mjs +++ b/src/ZenGlanceManager.mjs @@ -294,6 +294,38 @@ }); }, 300); } + + openGlanceForBookmark(event) { + const activationMethod = Services.prefs.getStringPref('zen.glance.activation-method', 'ctrl'); + + if (activationMethod === 'ctrl' && !event.ctrlKey) { + return; + } else if (activationMethod === 'alt' && !event.altKey) { + return; + } else if (activationMethod === 'shift' && !event.shiftKey) { + return; + } else if (activationMethod === 'meta' && !event.metaKey) { + return; + }else if (activationMethod === 'mantain' || typeof activationMethod === 'undefined') { + return; + } + + event.preventDefault(); + event.stopPropagation(); + + const rect = event.target.getBoundingClientRect(); + const data = { + url: event.target._placesNode.uri, + x: rect.left, + y: rect.top, + width: rect.width, + height: rect.height, + }; + + this.openGlance(data); + + return false; + } } window.gZenGlanceManager = new ZenGlanceManager(); diff --git a/src/ZenSidebarManager.mjs b/src/ZenSidebarManager.mjs index 1103b1b..367bacc 100644 --- a/src/ZenSidebarManager.mjs +++ b/src/ZenSidebarManager.mjs @@ -783,6 +783,27 @@ class ZenBrowserManagerSidebar extends ZenDOMOperatedFeature { const url = gContextMenu.linkURL || gContextMenu.target.ownerDocument.location.href; this._createNewPanel(url); } + + toggleEssentialsAccordion(header) { + const content = document.getElementById('EssentialsToolbarItems'); + const isExpanded = header.hasAttribute('expanded'); + + if (isExpanded) { + // Collapse + header.removeAttribute('expanded'); + content.style.maxHeight = null; + content.style.opacity = '0'; + setTimeout(() => { + content.removeAttribute('expanded'); + }, 300); + } else { + // Expand + header.setAttribute('expanded', 'true'); + content.setAttribute('expanded', 'true'); + content.style.maxHeight = content.scrollHeight + "px"; + content.style.opacity = '1'; + } + } } window.gZenBrowserManagerSidebar = new ZenBrowserManagerSidebar(); diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index adbbad9..d4fa786 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -941,6 +941,17 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature { for (let listener of this._changeListeners ?? []) { listener(window); } + // reset bookmark toolbars + const placesToolbar = document.getElementById("PlacesToolbar"); + if(placesToolbar?._placesView) { + placesToolbar._placesView.invalidateContainer(placesToolbar._placesView._resultNode); + } + + const essentialsToolbar = document.getElementById("EssentialsToolbar"); + if(essentialsToolbar?._placesView) { + essentialsToolbar._placesView.invalidateContainer(essentialsToolbar._placesView._resultNode); + } + this._inChangingWorkspace = false; } @@ -1194,4 +1205,14 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature { const workspaceToSwitch = workspaces.workspaces[index]; await this.changeWorkspace(workspaceToSwitch); } + + isBookmarkInAnotherWorkspace(bookmark) { + let tags = bookmark.tags; + // if any tag starts with "_workspace_id" and the workspace id doesnt match the active workspace id, return null + if (tags) { + for (let tag of tags.split(",")) { + return !!(tag.startsWith("zen_workspace_") && this.getActiveWorkspaceFromCache()?.uuid !== tag.split("_")[2]); + } + } + } })();