VivaldiHooks Revival with Plain JS [Work in Progress]
-
With what looks like the end of VivaldiHooks, it might be a good time to try and recreate some of the hooks as regular independent mods. I haven't looked too deeply into which ones are viable to recreate, but there are at least a few that can be replicated and brought new life.
@Stardepp asked me awhile ago if I could make the bookmark dropdown button and my tab toggle button hooks as regular mods, and with the help of a convenient Vivaldi function and a little trial and error I was able to get at least partial functionality.
Hopefully this thread can be used to show which hooks people want recreated and collect any working alternatives to VivaldiHooks in one place.
Working Hooks:
- bookmarks-button.js - partial functionality restored
- tab-toggle-button.js - fully working [Deprecated], see post for details
- qc-tab-close.js - mostly full functionality
- source: https://forum.vivaldi.net/post/672258
- author: @ppgm
New Hooks Being Worked On:
- disable-VB-25302.js - working in PowerShell, pending Python base script rewrite
-
Bookmarks-button.js
Edit: This mod was changed to rely on a command chain button. See the configuration section for more details.
Bit of a tricky hook to recreate. I thought it would be easy to do once I saw the Vivaldi function
vivaldi.bookmarkContextMenu.show
, but it ended up being more difficult in the end.This mod doesn't have complete functionality returned from the original hook. As of now, it is only the button to the left of the URL bar with the navigation buttons and there are a few context menu options for the bookmarks in the dropdown that aren't working.
Thanks to @stardepp for helping with testing
Known Issues
-
New Bookmark
,New Folder
,Edit
,Cut
,Copy
, andPaste
in the right click context menu on bookmark items are nonfunctional. No clear path to a fix at this time. It is also not possible to edit the context menu to remove these options using the menu editor in settings. -
Right clicking on the bookmark icon on the right of the URL bar does not produce the bookmark dropdown menu. It is probably possible, just not explored at this time.
Configuration
- [Recent Change] - You need to create a custom button for a command chain for this mod to work. See @luetage's post here for more details. This is to allow easily setting your own custom icon in themes and improve freedom of placement for the location of the button.
- There is one configuration variable near the top of the file called
COMMAND_CHAIN_BUTTON_NAME
. This should match the name of the command chain you created. It is set to"Bookmarks"
by default, so you would need to create a command chain named "Bookmarks
".
Code
(function bookmarkButton() { // ============================================================================================================ // Bookmarks button in front of the address field // URL: https://forum.vivaldi.net/post/555475 // Description: Adds a bookmark button in front of the address field that opens a dropdown menu of all bookamrks // Author(s): @nomadic // CopyRight: No Copyright Reserved // ============================================================================================================ // Config ------------ const COMMAND_CHAIN_BUTTON_NAME = "Bookmarks"; // ------------------- async function handleBookmarkMenuEvents(windowId, event) { const bookmarkListFromID = await chrome.bookmarks.get(event.id); const bookmark = bookmarkListFromID[0]; switch (event.command) { case "addactivetab": const isItemAFolder = bookmark.url === undefined; const parentId = isItemAFolder ? bookmark.id : bookmark.parentId; const index = isItemAFolder ? null : bookmark.index + 1; chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { const currentTab = tabs[0]; chrome.bookmarks.create({ parentId: parentId, index: index, title: currentTab.title, url: currentTab.url }); }); break; case "addbookmark": // TODO: No current solution, can't open bookmark dialog break; case "addfolder": // TODO: No current solution, can't open bookmark dialog break; case "addseparator": chrome.bookmarks.create({ index: bookmark.index + 1, parentId: bookmark.parentId, title: "---", url: "http://bookmark.placeholder.url/", description: "separator" }); break; case "edit": // TODO: No current solution, can't open bookmark dialog break; case "cut": // TODO: No current solution, can't make work with built in function in bookmark panel and page break; case "copy": // TODO: No current solution, can't make work with built in function in bookmark panel and page break; case "paste": // TODO: No current solution, can't make work with built in function in bookmark panel and page break; case "delete": // function works without special handling break; default: break; } removeBookmarkListeners(); } async function handleBoormarkOpenEvents(windowId, event) { let isOpenedInBackgroundTab = event.background; const bookmarkListFromID = await chrome.bookmarks.get(event.id); const bookmarkClicked = bookmarkListFromID[0]; // determine the bookmarks to open, an individual or a folder of bookmarks const bookmarkChildren = await chrome.bookmarks.getChildren(bookmarkClicked.id); let bookmarksToOpen; if (bookmarkChildren.length === 0) { bookmarksToOpen = [bookmarkClicked]; } else { bookmarksToOpen = bookmarkChildren; } for (bookmark of bookmarksToOpen) { switch (event.disposition) { // Depends on the setting for whether the bookmark is opened in a new tab or not case "setting": let shouldOpenInNewTab = await vivaldi.prefs.get("vivaldi.bookmarks.open_in_new_tab"); const crtlKeyState = event.state.ctrl; const middleMouseState = event.state.center; // adjust whether opened in the background depending on ctrl key or middle mouse press isOpenedInBackgroundTab = isOpenedInBackgroundTab || crtlKeyState || middleMouseState; // adjust whether opened in new tab depending on ctrl key or middle mouse press shouldOpenInNewTab = shouldOpenInNewTab || crtlKeyState || middleMouseState; if (shouldOpenInNewTab) { chrome.tabs.create({ active: !isOpenedInBackgroundTab, url: bookmark.url }); } else { chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { const currentTab = tabs[0]; chrome.tabs.update(currentTab.id, { url: bookmark.url }); }); } break; case "new-tab": chrome.tabs.create({ active: !isOpenedInBackgroundTab, url: bookmark.url }); break; case "current": chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { const currentTab = tabs[0]; chrome.tabs.update(currentTab.id, { url: bookmark.url }); }); break; case "new-window": chrome.windows.getCurrent((window) => { chrome.windows.create({ url: bookmark.url, height: window.height, width: window.width, top: window.top, left: window.left }); }); break; case "new-private-window": chrome.windows.getCurrent((window) => { chrome.windows.create({ incognito: true, url: bookmark.url, height: window.height, width: window.width, top: window.top, left: window.left }); }); break; default: break; } } removeBookmarkListeners(); } function removeBookmarkListeners() { vivaldi.menubarMenu.onBookmarkAction.removeListener(handleBookmarkMenuEvents); vivaldi.menubarMenu.onOpenBookmark.removeListener(handleBoormarkOpenEvents); } async function openBookmarkDropdown(event) { // clear any existing listeners removeBookmarkListeners(); vivaldi.menubarMenu.onBookmarkAction.addListener(handleBookmarkMenuEvents); vivaldi.menubarMenu.onOpenBookmark.addListener(handleBoormarkOpenEvents); const windowID = await vivaldi.windowPrivate.getCurrentId(); const rect = event.target.getBoundingClientRect(); vivaldi.bookmarkContextMenu.show({ windowId: windowID, id: "1", siblings: [ { folderGroup: true, id: "1", offset: 0, rect: { x: parseInt(rect.left), y: parseInt(rect.top), width: parseInt(rect.width), height: parseInt(rect.height), }, }, ], sortField: undefined, sortOrder: undefined, edge: "off", icons: [ "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", ], }); } // Adapted from @luetage's mod: https://forum.vivaldi.net/topic/83388/update-feeds // Bind bookmark dropdown to command chain button let appendChild = Element.prototype.appendChild; Element.prototype.appendChild = function () { if (this.tagName === "BUTTON") { setTimeout( function () { if (this.classList.contains("ToolbarButton-Button")) { if (this.title === COMMAND_CHAIN_BUTTON_NAME && this.name !== "PanelBookmarks") { this.addEventListener("click", openBookmarkDropdown); } } }.bind(this, arguments[0]) ); } return appendChild.apply(this, arguments); }; })();
Edits:
- (April 6, 2022) - Some Vivaldi listeners changed to return the window ID before returning the event data.
- (June 4, 2022) - Customizable toolbars in 5.3 changed some aspects of the address bar.
- (June 7, 2022) - The mailbar observer was also affected by the 5.3 update, so the selector needed to be adjusted as well. Thanks to @stardepp for pointing out the issue.
- (February 16, 2023) - Changed so listener is added to a command chain button to allow easily customizable icons. Thanks to @luetage for code to accomplish this.
- (October 21, 2023) - Added check to prevent menu from appearing on the Bookmarks Panel button when clicked. Thanks to @stardepp for pointing it out.
-
-
Tab-toggle-button.js
Deprecated. The mod can now be accomplished with a command chain button set to the
Tab Bar
command.Expand for old contents of post.
Nothing too special. Just adds a button for toggling the visibility of the tab bar. It was never an official VivaldiHook, but it was shared in the thread.
Thanks to @stardepp for helping with testing
Code
(function () { // ============================================================================================================ // Toggle tabs button for the navigation area of the address bar // URL: https://forum.vivaldi.net/post/555476 // Description: Adds a navigation bar button that will toggle the visibility of the tab bar // Author(s): @nomadic // CopyRight: No Copyright Reserved // ============================================================================================================ function toggleTabsButton() { function toggleTabs() { // get initial value vivaldi.prefs.get("vivaldi.tabs.visible", (isVisible) => { vivaldi.prefs.set({ path: "vivaldi.tabs.visible", value: !isVisible }); }); } function addTabToggleButton() { const addressBar = document.querySelector(".mainbar > .toolbar-mainbar.toolbar-droptarget"); const urlField = document.querySelector(".UrlBar-AddressField"); const oldButton = document.getElementById("toggleTabsBtn"); // check if already exists and elements are valid if (oldButton || !addressBar || !urlField) return; const toggleTabsBtn = document.createElement("div"); toggleTabsBtn.id = "toggleTabsBtn"; toggleTabsBtn.classList.add("button-toolbar"); toggleTabsBtn.innerHTML = ` <button draggable="false" tabindex="-1" title="Toggle tab bar" type="button" class="ToolbarButton-Button" name="Bookmarks"> <span> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"> <path fill-rule="evenodd" d="M19 8H7a1 1 0 0 0-1 1v2h14V9a1 1 0 0 0-1-1zM6 17v-5h14v5a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1zM7 7a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2zm4 2H7v1h4z"/> </svg> <span> </button> `; addressBar.insertBefore(toggleTabsBtn, urlField); toggleTabsBtn.addEventListener("click", toggleTabs); } // ============================================================================== // Mutation Observer for Address Bar Changes // ============================================================================== let main = document.getElementsByClassName("mainbar")[0]; // get the initial state of the addressbar as either urlbar or mailbar let oldIsMailBarActive = main.firstChild.classList.contains("toolbar-mailbar"); let addressBarObserver = new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { // only re-add on new nodes added. The list addedNodes will only have a // length attribute when it contains added nodes if (mutation.addedNodes.length) { // get the new state of the addressbar let isMailBarActive = mutation.addedNodes[0].classList.contains("toolbar-mailbar"); // if it is different from the previous state, we need to act on it if (oldIsMailBarActive !== isMailBarActive) { // update the old value for comparisons on future mutations oldIsMailBarActive = isMailBarActive; // if the addressbar isn't the mailbar, we can re-add the button if (!isMailBarActive) { // Run all changes that are only in the url bar and not the mail bar addTabToggleButton(); } } } }); }); addressBarObserver.observe(main, { childList: true }); addTabToggleButton(); } let intervalID = setInterval(() => { const browser = document.getElementById("browser"); if (browser) { clearInterval(intervalID); toggleTabsButton(); } }, 300); })();
Edits:
- (June 4, 2022) - Customizable toolbars in 5.3 changed some aspects of the address bar.
- (June 7, 2022) - The mailbar observer was also affected by the 5.3 update, so the selector needed to be adjusted as well. Thanks to @stardepp for pointing out the issue.
- (April 21, 2023) - Deprecated.
-
Wonderful initiative!
The only ones I used were:
- disable-VB-25302.js - I find the default behaviour annoying
- qc-close-tab.js - Really useful to be able to close tabs from QC. Unfortunately it kept breaking.
- speeddial-shortcuts.js - Fast access to the first 10 dials using keyboard hotkeys
- speeddial-spacing.js - So good, it should be built-in as default, there's just too much wasted space now.
The SD spacing one is IMO the most useful, but I suspect also most complex to recreate as it hooks directly into the settings UI.
-
Great initiative, yay - ta.
Of the many hooks i used, the ones i am missing most of all, & whose loss actively dissuades me from using V atm, are:
- small-vert-pinned-tabs.js
- tab-size.js
- treeview-spacing.js
- speeddial-spacing.js
-
tab-size.js
treeview-spacing.jsi use a few more but the loss of those 2 have kept me on 4.2 - i look at new releases and move on - to get those two back would be great - treeview spacing is the top of my list - i cannot see why the spacing is as it is by default - its far too wide
-
@nomadic Good initiative. Iβm sure thereβs many previous users who are desperate by now, lol
-
@luetage said in VivaldiHooks Revival with Plain JS [Work in Progress]:
initiative
We have three of a kind. That's a reasonable start. Now we must beware the existential risk of straights & full houses.
-
I still tweak browser.html after every update to Vivaldi, and I still add the line "<script src="jdhooks.js"></script>" each time, in the vain hope that ONE day, it might work again, or someone releases an update that fixes it for the modern Vivaldi generation. Ironically, it's now been so long since it last worked, I no longer actually remember the full list of hooks I previously used. Nevertheless, I would be soooo happy if someone can recreate as much of the original functionality as possible
-
@crimsonshade said in VivaldiHooks Revival with Plain JS [Work in Progress]:
I still tweak browser.html after every update to Vivaldi, and I still add the line "<script src="jdhooks.js"></script>" each time
Ditto, via running my bash script to do this after each update. Each time i ask myself "hey, self, why are you still doing this, you ninny?", the same answer always comes back. It's not only to load
jdhooks.js
, but also to load:advancedPanels.js
custom.js
... with the latter in turn providing:
- https://forum.vivaldi.net/topic/26623/zoom-find-in-page-other-actions-in-web-panels?page=1
- https://forum.vivaldi.net/topic/33047/autosave-sessions-mod?page=1
- https://forum.vivaldi.net/post/483429 [open-panels-on-mouse-over]
I suppose it'd be nice not to have to bother with the script any more, but otoh losing these nice functions would only compound the existing bitter disappointment of losing all the nice Hooks' functions.
So we beat on, boats against the current, borne back ceaselessly into the past.
-
@guigirl said in VivaldiHooks Revival with Plain JS [Work in Progress]:
Each time i ask myself "hey, self, why are you still doing this, you ninny?", the same answer always comes back. It's not only to load
jdhooks.js
, but also to load:custom.js
... with the latter in turn providing ...
Same here. I wouldn't be comfortable without my custom mods; and it's just a sad part that jdhooks is no longer part of these - yet, because of the value it once had, and the value I still get from the rest, I still continue the same old routine, however futile.
-
There's an awful lot of really bad crap going on all over the rock. I realise this is likely severely distracting, if not much worse, for many Vivaldifarians. That might be a significant factor in this thread, after the initial flurry of excitement, having become quiet.
Alternatively, notwithstanding the widespread shyteness of the world lately & in prospect, maybe the primary cause of the quietness is simply a tacit understanding that whatever broke VH remains an impregnable barrier to ongoing UI mods of similar scope.
Either alternative is bad.
-
@guigirl I am quite busy at the moment with finishing up a portfolio website, looking for a job, and helping my family with some projects, so I haven't had much of an opportunity to look into remaking any other Hooks as regular mods.
But I haven't abandoned the effort. I just wanted to finish up mods I had promised to work on for
@stardepp
and@legobuilder26
before I took a break from modding to allow me to get that other important stuff done.I was hoping that some others would gain hope by seeing a Hook revived and do some work to help bring others back as well. It of course takes a bit of experience to figure out how to get a few Vivaldi internal functions working, so it might have been a bit optimistic of me.
I never fully grasped how the inner workings of Vivaldi Hooks functioned, so getting a true replacement that injects changes before Vivaldi adds everything to the interface is definitely a daunting challenge. Maybe, at some point, with a little more knowledge about React and my now better understanding of JS from the last time I reviewed the Vivaldi Hooks code, it will be possible to get something working, but who knows
-
@nomadic I'd just like to clarify, to make it beyond any potential misunderstanding from my clumsy words, that my intention in my previous post was melancholy & sadness, but absolutely not any implied criticism of you or anyone else in the forum. Nothing in your reply implies that you took that interpretation, but it's just that there's so much sadness & ruination about atm that i wanted to make it utterly beyond question now.
Good luck!
-
With the new Vivaldi Update the Bookmark Button is working but i cant open any Bookmark with it. It does nothing.
-
@loomes Thanks for the heads up. Should be fixed now.
Looks like the update brought some changes to Vivaldi's internal API. The 2 event listeners I used now return the
windowID
where the event was triggered before returning theBookmarkAction
object.
@LonM looks like the Vivaldi Modders API Internal Documentation website needs another update for changes made in
5.2
.Thanks again for making it convenient to view
-
@nomadic Thanks again for your work, your bookmark mod works very well again.
-
@nomadic I updated it
-
@nomadic
Thank you for the fast Bugfix. Its working fine again -
Can you add for Tab Size ?
I'm using Vivaldi hooks only to change tab width, since Vivaldi doesn't have an option for it.