VivaldiHooks Revival with Plain JS [Work in Progress]
-
First of all, thank you very much for your help
The problem was not with the *.js file or the browser.html, it was with the file "Preferences" which is in my user directory under .config/vivaldi/Default/.
If I delete it and recreate it on browser startup, the bookmarks button is there.
Where it was exactly I do not know, only that I could reproduce the behavior on two systems when upgrading from 5* to 6*, another system worked as described my laptop.
-
Hi everyone!
I made mod like a "qc-close-tab.js".
(Although, I don't know original qc-close-tab.js it's maybe different thing.)If you want it, I think it's very usefull
qc-tab-close.js
It set close-button on qucick-command.
It can alse close tab in tab stack, workspace.Known bug
- It's not function when openning setting page.
- Current tab is forced to switch to another tab.
- Localize only 'en-US', 'en-UK', 'ja', 'ja-KS'
Please tell me or fix it if you see bug
I'll fix these bugs.
And I would like to develop more mods for QuickCommand in the future. Let me know if there's a QuickCommand feature you'd like:)Update(2023/06/08)
- Implement shortcut(Alt + Delete) to close tab in QuickCommand.
- Apply this CSS from @Pathduck when scrolling qc too.
- It will be applied every time qc-modal is updated and you can apply your favorite CSS.
Update(2023/06/08)
- Remove width-height-CSS
- Because these don't relate qc-close-tab functions.
(function SetCloseTabButtonQC() { "use strict"; var lang; const l10n = { 'en-US': { tab: 'Tabs' }, 'en-UK': { tab: 'Tabs' }, 'ja': { tab: 'γΏγ' }, 'ja-KS': { tab: 'γΏγ' }, }; vivaldi.utilities.getLanguage().then(l => { lang = l10n[`${l}`]; }); // Set shortcut close tab // it can't register specific key what had default function for some reason( ex) Ctrl|Shift|Alt+Backspace). // It can't register Shift+Delete for some reason(Ctrl|Alt+Delete has default function). const DELETE_TAB_KEY_COMBO = "Alt+Delete"; function handleKeyboardShortcut(_, combo) { console.log(combo); if(DELETE_TAB_KEY_COMBO !== combo) return; // Delete tab const selected = document.querySelector('.quick-command[data-selected]'); const selectedTitle = selected.querySelector('.quick-command-title'); var tabTitle = selectedTitle.textContent.trim(); const isRemoved = removeTabFromTabTitle(tabTitle); // Delete tab in qc if(isRemoved) return; selected.classList.add('strikethrough-text') } vivaldi.tabsPrivate.onKeyboardShortcut.addListener(handleKeyboardShortcut); // Set close button function addCloseButtonsToQuickCommands() { // Parent of quick-command-sectionheader and quick-command class const qcContainer = document.querySelector('.quick-commands'); // Extract qc-commands from React auto generate container tag const qcCommands = qcContainer.firstChild.firstChild; for (const command of qcCommands.childNodes) { // Set button when'quick-command-sectionheader' include 'Tab' if (command.classList.contains('quick-command-sectionheader')) { if (command.textContent.includes(lang.tab)) { continue; } else { // Return when next section-header return; } } // Set close button in quick-commnad class if (command.querySelector('.close-button')) continue; var closeButton = document.createElement('span'); closeButton.className = 'close-button'; closeButton.textContent = 'Γ'; closeButton.style.padding = '4px 8px'; // Invert color at mouse hover closeButton.addEventListener('mouseenter', function () { this.style.backgroundColor = '#000'; this.style.color = '#fff'; this.style.cursor = 'pointer'; }); // Revevrt color at mouse leave closeButton.addEventListener('mouseleave', function () { this.style.backgroundColor = ''; this.style.color = ''; this.style.cursor = 'default'; }); closeButton.addEventListener('click', handleButtonClick, true); command.appendChild(closeButton); } } function handleButtonClick() { var titleElement = this.parentNode.getElementsByClassName('quick-command-title')[0]; var tabTitle = titleElement.textContent.trim(); removeTabFromTabTitle(tabTitle); } function removeTabFromTabTitle(tabTitle) { chrome.tabs.query({}, function (tabs) { for (var i = 0; i < tabs.length; i++) { if (tabs[i].title !== tabTitle) continue; chrome.tabs.remove(tabs[i].id, function () { }); return true; } console.log('Not found tab'); return false; }); } // Observe entire html to appeare qc-modal function startObservingModalBg() { var targetNode = document.documentElement; var observer = new MutationObserver(handleModalBgMutation); const observerOptions = { childList: true, subtree: true }; observer.observe(targetNode, observerOptions); } // observe qc-modal for refleshed commands function handleModalBgMutation(mutationsList, observer) { for (var mutation of mutationsList) { if (mutation.type !== 'childList') continue; var addedNodes = mutation.addedNodes; for (var i = 0; i < addedNodes.length; i++) { if (addedNodes[i].id !== 'modal-bg') continue; addCloseButtonsToQuickCommands(); const observer = new MutationObserver(() => { addCloseButtonsToQuickCommands() }); const gridListElement = document.getElementById('modal-bg'); const observerOptions = { childList: true, subtree: true }; observer.observe(gridListElement, observerOptions); } } } startObservingModalBg(); })();
-
qc-close-tab.js fix several bugs
- [FIX] It's not function when openning setting page.
- [FIX] Current tab is forced to switch to another tab.
Another bug is that deleting a tab in another workspace will take you to that workspace.
(function SetCloseTabButtonQC() { "use strict"; var lang; const l10n = { 'en-US': { tab: 'Tabs' }, 'en-UK': { tab: 'Tabs' }, 'ja': { tab: 'γΏγ' }, 'ja-KS': { tab: 'γΏγ' }, }; vivaldi.utilities.getLanguage().then(l => { lang = l10n[`${l}`]; }); // Create close button function addCloseButtonsToQuickCommands() { // Parent of quick-command-sectionheader and quick-command class const qcContainer = document.querySelector('.quick-commands'); // Extract qc-commands from React auto generate container tag const qcCommands = qcContainer.firstChild.firstChild; for (const command of qcCommands.childNodes) { // Set button when'quick-command-sectionheader' include 'Tab' if (command.classList.contains('quick-command-sectionheader')) { if (command.textContent.includes(lang.tab)) { continue; } else { // Return when next section-header return; } } // Set close button in quick-commnad class if (command.querySelector('.close-button')) continue; var closeButton = document.createElement('span'); closeButton.className = 'close-button'; closeButton.textContent = 'Γ'; closeButton.style.padding = '4px 8px'; // Invert color at mouse hover closeButton.addEventListener('mouseenter', function () { this.style.backgroundColor = '#000'; this.style.color = '#fff'; this.style.cursor = 'pointer'; }); // Revevrt color at mouse leave closeButton.addEventListener('mouseleave', function () { this.style.backgroundColor = ''; this.style.color = ''; this.style.cursor = 'default'; }); closeButton.addEventListener('click', handleButtonClick, true); command.appendChild(closeButton); } } function handleButtonClick() { var titleElement = this.parentNode.getElementsByClassName('quick-command-title')[0]; var tabTitle = titleElement.textContent.trim(); removeTabFromTabTitle(tabTitle); } // Remove tab from tab title function removeTabFromTabTitle(tabTitle) { chrome.tabs.query({}, function (tabs) { for (var i = 0; i < tabs.length; i++) { if (tabs[i].title !== tabTitle) continue; chrome.tabs.remove(tabs[i].id, function () { }); return; } console.log('Not found tab'); }); } // Observe entire html to appeare qc-modal function startObservingModalBg() { var targetNode = document.documentElement; var observer = new MutationObserver(handleModalBgMutation); const observerOptions = { childList: true, subtree: true }; observer.observe(targetNode, observerOptions); } // observe qc-modal for commands function handleModalBgMutation(mutationsList, observer) { for (var mutation of mutationsList) { if (mutation.type !== 'childList') continue; var addedNodes = mutation.addedNodes; for (var i = 0; i < addedNodes.length; i++) { if (addedNodes[i].id !== 'modal-bg') continue; addCloseButtonsToQuickCommands(); const observer = new MutationObserver(() => addCloseButtonsToQuickCommands()); const gridListElement = document.getElementById('modal-bg'); const observerOptions = { childList: true, subtree: true }; observer.observe(gridListElement, observerOptions); } } } startObservingModalBg(); })();
-
@ppgm Nice! Glad to have someone else working on these as well
If you edit your original post with the updates to the mod, it will allow me to link to your mod in the top post of this thread.
-
@ppgm Well done!
One of the things I pushed for in the original qc-close-tab hook was to have it close tabs with a keyboard hotkey. Shift+Delete was implemented as an alternative to clicking the tiny
x
.Having options to make the QC popup taller/wider would also be great, IMO it's too small at the moment to be all that useful. This is possible with custom CSS but with only CSS you get some strange issues when scrolling for instance.
Here's my attempt at making the QC more useful, it kinda works but it's been a while since I looked at it properly:
https://pathduck.github.io/vivaldi/mods/quick-command-tweaks.css -
@nomadic
Thanks!
Going forward, I'll update code in original post! -
@Pathduck
Thanks and nice very usefull CSS! I like it!I applied this CSS every time qc update in
qc-close-tab.js
so check it -
@ppgm Hey, that's cool - and thanks, I like it too, even if it bugs out when scrolling...
To be honest though - I am a firm believer in not mixing up functionality with style. So if your JS mod is about adding tab close functionality to the QC, it does not fit in adding style to the QC dialog. It would mean other people who might have their own CSS style for the QC might have conflicts.
So to be honest, I think it would best to keep the CSS out of the JS. Of course, I would not stop you using it, just think it's a bad idea. But of course, feel free to still recommend adding it for some cool QC style
Also, if you'd like me to fork these QC-Tab-Close mod posts off to its own topic I can do it for you if you prefer. Might be an idea to keep it as a separate topic for discussion, even if it's slightly related to @nomadic's WIP/on-hold/abandoned attempt to revive the VivialdiHooks
Oh and if someone can figure out what might be wrong with the CSS when scrolling up it would be much appreciated - I suspect it might only be solvable by JS though (i.e. the entries just aren't there)...
-
@Pathduck
hmm...That's right.
I guessed js and css should be mixed up because both mod subscribe html code. It's reudandant if both mod is applied by each js code base.
But these divede each code base as you say because width-height-style don't relate qc-close-tab functionsI update original post! Thank you advice
-
Is it possible that the mod no longer works under version 6.2.3105.45 or something must be adjusted?
-
@utiltiy JS Mods since 6.2 have to be hooked in window.html now due new portal feature.
-
@Hadden89
Thank you! Now works as before -
hi guys, do you think it's possible to have "Create tab with middle click on a free tabbar space" working again?
-
@giaaaacomo it seems to be just a css mod. So it should already work.
-
@Hadden89 didn't expect such a fast reply, nice! looks like i'm missing something about the installation or the activation. i tried to install the hooks through installhooks.bat, but nothing is changed, nor i see any new section in the settings
-
@giaaaacomo The hooks doesn't work anymore. The point of the thread is to recreate js hooks as js mods but in this case seems to be only css and you just need to add the string to the custom.css
https://forum.vivaldi.net/topic/10549/modding-vivaldi?page=1 -
uh ok, thanks for now!
-
Hello! first time posting on a forum like this but I like many others have gotten fed up with Franz/Ferdi/Ferdium and similar apps for their pricing plans or "meltdowns" while the free/open source solutions do not allow extensions so I took it upon myself to recreate most of what I've been missing in Vivaldi.
I am not exactly available to maintain this code
-- I just wanted to share it so that others may contribute/improve upon it for everyone else rather than keep it to myself... it kinda already does everything I need/want it to lol so I am not very motivated to polish it any further.You can find/download the code here (or just copy it from the post):
https://github.com/oms9/VivaldiModCustom.js:
const tabX = 0; const spacing = 3; const ptabHeight = 60; const ptabWidth = 60; let lastY = 0; let normExist = false; const observer = new MutationObserver(setHeights); function enlargePinnedTabs() { "use strict"; let pinnedTabs = document.getElementsByClassName("tab-position is-pinned"); let idx = 0; Array.from(pinnedTabs).forEach(function (tab) { //Default cssText str: "--PositionX: 0px; --PositionY: 0px; --Height: 30px; --Width: 72px; --ZIndex: 2;" let tabY = (idx * 60) + spacing; tab.style['cssText'] = `--PositionX: ${tabX}px; --PositionY: ${tabY+spacing}px; --Height: ${ptabHeight}px; --Width: ${ptabWidth}px; --ZIndex: 2;`; idx ++; lastY = tabY; }); }; function setHeights(){ //<div id="tabs-tabbar-container" class="left dotted" style="width: 72px; height: 100%;"> observer.disconnect() let CONT = document.getElementById("tabs-container"); if (CONT.className == 'overflow left'){ let CONT = document.getElementsByClassName("left dotted"); CONT[0].style['cssText'] = `width: 82px; height: 100%;` } else{ let CONT = document.getElementsByClassName("left dotted"); CONT[0].style['cssText'] = `width: 72px; height: 100%;` } enlargePinnedTabs(); let tabBar = document.getElementsByClassName("tab-strip"); Array.from(tabBar[0].children).forEach(function (elem) { target = elem.firstChild; // Get the inside in a separate var if ((target.className).includes('is-pinned')){ // PINNED TABS HERE ; //skip } else if (elem.className == "separator"){ // SEPARATOR HERE elem.style['cssText'] = `--PositionX: 0px; --PositionY: ${lastY+ptabHeight+spacing}px; --Height: 20px; --Width: 72px; --ZIndex: 0;` lastY += spacing + 56; // 20 + 30 + 2x spacing } else if (target.className == "tab-position"){ // NORMAL TABS HERE target.style['cssText'] = `--PositionX: 0px; --PositionY: ${lastY+spacing+20}px; --Height: 30px; --Width: 72px; --ZIndex: 2;` lastY += 33; } else if (target.className == "button-toolbar newtab"){ // NEW TAB BUTTON HERE sepExist = document.getElementsByTagName("hr"); if (sepExist.length == 1){ // SEPARATOR target.style['cssText'] = `--PositionX: 21px; --PositionY: ${lastY+spacing+20}px; --Height: 30px; --Width: 30px; --ZIndex: 0; left: 21px; top: ${lastY+spacing+20}px;` } else{ // NO SEPARATOR target.style['cssText'] = `--PositionX: 21px; --PositionY: ${lastY+ptabHeight+spacing+10}px; --Height: 30px; --Width: 30px; --ZIndex: 0; left: 21px; top: ${lastY+ptabHeight+spacing+10}px;` } } else { // everything else here (nothing as of yet) //console.log('Found ELSE: ' + target.className); } }); observer.observe(tabBar[0], {attributes: true, childlist: true, subtree: true}); } // Loop waiting for the browser to load the UI let intervalID = setInterval(function () { const browser = document.getElementById("browser"); if (browser) { clearInterval(intervalID); // set init size document.getElementsByClassName("left dotted")[0].style['cssText'] = `width: 72px; height: 100%;`; // call function once setHeights(); } }, 300); // Don't forget to thank @luetage for the standard 300 ms
CSS:
#browser.color-behind-tabs-on .tab-position .tab.active.active { background-color: #7b7b7b; color: #FFFFFF; } :not(.ui-transparent-tabs).transparent-tabbar:not(.acc-dark.bg-dark.color-accent-from-page.color-behind-tabs-on) .tab-position .tab { background-color: #1c1c1c; }
Preview:
-
Hello @nomadic
With the latest Vivaldi Version 6.9.3447.37 the Bookmark Button is not working anymore ):
Can you take a look when you have time? -
@loomes But it still works with Vivaldi 6.9.3447.37. Did you copy the .js modifications back into the correct Vivaldi folder after this update?