Update files
This commit is contained in:
@@ -33,12 +33,10 @@ nginx=start /d d:\bin\Nginx /b d:\bin\Nginx\nginx.exe $*
|
||||
nmap=D:\bin\nettools\Nmap\nmap.exe $*
|
||||
nping=D:\bin\nettools\Nmap\nping.exe $*
|
||||
npp=d:\bin\Notepad++\notepad++.exe $*
|
||||
pb=sh d:\bin\cygwin\usr\local\bin\pb $*
|
||||
putty=d:\bin\putty\putty.exe $*
|
||||
pwd=cd
|
||||
python=d:\bin\python\python.exe $*
|
||||
rm=rm -i $*
|
||||
shn=curl -F "format=simple" -F "url=$1" "https://is.gd/create.php"
|
||||
sublime=d:\bin\Sublime\sublime_text.exe $*
|
||||
sudo=gsudo $*
|
||||
tc=d:\bin\totalcmd\totalcmd.exe $*
|
||||
@@ -48,3 +46,6 @@ tshark=d:\bin\Wireshark\tshark.exe $*
|
||||
unalias=alias /d $1
|
||||
vi=vim $*
|
||||
winscp=d:\bin\WinSCP\winscp.exe $*
|
||||
pb=curl -sF "files[]=@$*" "https://qu.ax/upload.php" $B jq -r '.files[].url'
|
||||
shn=curl -F "format=simple" -F "url=$1" "https://is.gd/create.php"
|
||||
cb=curl -s -F "reqtype=fileupload" -F "fileToUpload=@$*" "https://catbox.moe/user/api.php"
|
||||
|
||||
BIN
Default.rdp
BIN
Default.rdp
Binary file not shown.
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0"?>
|
||||
<Settings component="StartMenu" version="4.4.169">
|
||||
<Settings component="StartMenu" version="4.4.197">
|
||||
<MenuStyle value="Classic1"/>
|
||||
<ShiftRight value="1"/>
|
||||
<Computer value="Hide"/>
|
||||
<Favorites value="Hide"/>
|
||||
<Documents value="Hide"/>
|
||||
@@ -15,7 +16,9 @@
|
||||
<RecentSortName value="1"/>
|
||||
<StartScreenShortcut value="0"/>
|
||||
<HighlightNew value="0"/>
|
||||
<SameSizeColumns value="1"/>
|
||||
<SearchInternet value="0"/>
|
||||
<InvertMetroIcons value="1"/>
|
||||
<MenuCaption value="Crapstation"/>
|
||||
<MenuShadow value="1"/>
|
||||
<EnableGlass value="1"/>
|
||||
@@ -31,8 +34,7 @@
|
||||
<Line>CENTER_NAME=0</Line>
|
||||
<Line>SMALL_ICONS=0</Line>
|
||||
<Line>LARGE_FONT=0</Line>
|
||||
<Line>ICON_FRAMES=1</Line>
|
||||
<Line>OPAQUE=0</Line>
|
||||
<Line>DISABLE_MASK=1</Line>
|
||||
</SkinOptionsC1>
|
||||
<SkinW7 value="Midnight"/>
|
||||
<SkinVariationW7 value=""/>
|
||||
@@ -44,9 +46,9 @@
|
||||
<Line>OPAQUE=0</Line>
|
||||
</SkinOptionsW7>
|
||||
<EnableStartButton value="1"/>
|
||||
<AllTaskbars value="1"/>
|
||||
<StartButtonType value="CustomButton"/>
|
||||
<StartButtonPath value="%USERPROFILE%\Documents\Settings\StartOrb\Win7.png"/>
|
||||
<StartButtonSize value="80"/>
|
||||
<StartButtonPath value="%USERPROFILE%\Documents\Settings\Diverse\Classic Start Menu\EMad777.bmp"/>
|
||||
<StartButtonAlign value="0"/>
|
||||
<CustomTaskbar value="1"/>
|
||||
<TaskbarLook value="Glass"/>
|
||||
@@ -13,6 +13,11 @@
|
||||
grid-template-columns: unset;
|
||||
}
|
||||
|
||||
/* Width of empty spacer before tabs */
|
||||
#browser #tabs-tabbar-container .toolbar-tabbar-before {
|
||||
min-width: 0px;
|
||||
}
|
||||
|
||||
/* Skinnier mainbar */
|
||||
.mainbar .toolbar-mainbar.toolbar-addressbar,
|
||||
.mainbar .toolbar-mainbar .button-toolbar button {
|
||||
@@ -63,18 +68,13 @@
|
||||
}
|
||||
|
||||
/* Hide AddressField stuff */
|
||||
.UrlBar-AddressField .permission-popup,
|
||||
.UrlBar-AddressField .permission-popup.is-blocking,
|
||||
.UrlBar-AddressField .UrlBar-UrlObfuscationWarning,
|
||||
.UrlBar-AddressField .ContentBlocker-Control,
|
||||
.UrlBar-AddressField .ToolbarButton-Button[title^="Translate"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* AddressField show allowed permission buttons */
|
||||
.UrlBar-AddressField .permission-popup.is-allowing {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* AddressField URL scheme part */
|
||||
.UrlFragment--Lowlight:first-of-type {
|
||||
color: var(--colorFgFadedMost);
|
||||
@@ -96,7 +96,7 @@
|
||||
}
|
||||
|
||||
/* Extensions toggle button */
|
||||
.toolbar-extensions .ToolbarButton-Button[name="Extensions"] .button-icon {
|
||||
.toolbar-extensions .ExtensionToggleIcon .button-icon {
|
||||
opacity: 0.5 !important;
|
||||
}
|
||||
|
||||
@@ -181,8 +181,3 @@
|
||||
.overlayinfobubble {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Width of empty spacer before tabs */
|
||||
.tabs-top #tabs-tabbar-container .toolbar-tabbar-before:empty {
|
||||
min-width: 0px;
|
||||
}
|
||||
|
||||
469
Vivaldi/mods/JS/tab-cycler-close.js
Executable file
469
Vivaldi/mods/JS/tab-cycler-close.js
Executable file
@@ -0,0 +1,469 @@
|
||||
// ==UserScript==
|
||||
// @name Mod: Use Tab cycler to close multiple tabs at once
|
||||
// @description Mark multiple tabs with checkboxes as you navigate the Tab Cycler, then close them all at once.
|
||||
// @version 1.0
|
||||
// @author barbudo2005
|
||||
// @match *://*/*
|
||||
// ==/UserScript==
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
let cyclerActive = false;
|
||||
let markedTabIds = new Set();
|
||||
let processedTabs = new Set();
|
||||
let focusObserver = null;
|
||||
|
||||
console.log('🚀 Tab Cycler Batch Close v1.0 loaded');
|
||||
|
||||
// M key - toggle checkbox on highlighted tab
|
||||
window.addEventListener('keydown', function(e) {
|
||||
if (!cyclerActive) return;
|
||||
|
||||
if (e.code === 'Space' && e.ctrlKey) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const activeLi = document.querySelector('.tabswitcher.list li.selected');
|
||||
if (!activeLi) return;
|
||||
|
||||
const checkbox = activeLi.querySelector('.tab-checkbox');
|
||||
if (!checkbox) return;
|
||||
|
||||
// Toggle checkbox
|
||||
if (checkbox.checked) {
|
||||
checkbox.checked = false;
|
||||
checkbox.removeAttribute('checked');
|
||||
} else {
|
||||
checkbox.checked = true;
|
||||
checkbox.setAttribute('checked', '');
|
||||
}
|
||||
|
||||
const tabId = parseInt(checkbox.dataset.tabId);
|
||||
|
||||
if (checkbox.checked) {
|
||||
markedTabIds.add(tabId);
|
||||
} else {
|
||||
markedTabIds.delete(tabId);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}, true);
|
||||
|
||||
// CTRL or ALT keyup - batch close
|
||||
document.addEventListener('keyup', function(e) {
|
||||
if (e.key === 'Control' || e.key === 'Alt') {
|
||||
if (markedTabIds.size === 0) return;
|
||||
executeBatchClose();
|
||||
}
|
||||
}, true);
|
||||
|
||||
function executeBatchClose() {
|
||||
const idsToClose = Array.from(markedTabIds);
|
||||
|
||||
chrome.tabs.remove(idsToClose, () => {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.error('❌ Batch close error:', chrome.runtime.lastError);
|
||||
}
|
||||
markedTabIds.clear();
|
||||
});
|
||||
}
|
||||
|
||||
// Polling
|
||||
setInterval(function() {
|
||||
const cycler = document.querySelector('.tabswitcher.list');
|
||||
|
||||
if (cycler) {
|
||||
if (!cyclerActive) {
|
||||
cyclerActive = true;
|
||||
processedTabs.clear();
|
||||
markedTabIds.clear();
|
||||
applyCustomStyles();
|
||||
setupFocusObserver(cycler);
|
||||
}
|
||||
injectCheckboxes(cycler);
|
||||
} else if (cyclerActive) {
|
||||
cyclerActive = false;
|
||||
processedTabs.clear();
|
||||
|
||||
// Batch close when cycler closes (any method)
|
||||
if (markedTabIds.size > 0) {
|
||||
executeBatchClose();
|
||||
}
|
||||
|
||||
// Cleanup observer
|
||||
if (focusObserver) {
|
||||
focusObserver.disconnect();
|
||||
focusObserver = null;
|
||||
}
|
||||
}
|
||||
}, 500);
|
||||
|
||||
function setupFocusObserver(cycler) {
|
||||
if (focusObserver) return;
|
||||
|
||||
focusObserver = new MutationObserver(() => {
|
||||
const selectedLi = cycler.querySelector('li.selected');
|
||||
if (selectedLi) {
|
||||
const checkbox = selectedLi.querySelector('.tab-checkbox');
|
||||
if (checkbox && document.activeElement !== checkbox) {
|
||||
checkbox.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
focusObserver.observe(cycler, {
|
||||
attributes: true,
|
||||
attributeFilter: ['class'],
|
||||
subtree: true
|
||||
});
|
||||
}
|
||||
|
||||
function applyCustomStyles() {
|
||||
if (document.getElementById('tab-cycler-custom-styles')) return;
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.id = 'tab-cycler-custom-styles';
|
||||
style.textContent = `
|
||||
.tabswitcher.list li.active-page {
|
||||
filter: brightness(1.4) !important;
|
||||
}
|
||||
|
||||
.tab-checkbox {
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
width: 13px !important;
|
||||
height: 13px !important;
|
||||
min-width: 13px !important;
|
||||
min-height: 13px !important;
|
||||
border: 1px solid var(--colorFgFadedMost) !important;
|
||||
border-radius: 2px !important;
|
||||
background: transparent !important;
|
||||
cursor: pointer !important;
|
||||
position: absolute !important;
|
||||
right: 6px !important;
|
||||
top: 50% !important;
|
||||
transform: translateY(-50%) !important;
|
||||
z-index: 99999 !important;
|
||||
transition: all 0.15s ease !important;
|
||||
pointer-events: auto !important;
|
||||
}
|
||||
|
||||
.tab-checkbox:hover {
|
||||
border-color: var(--colorFgFaded) !important;
|
||||
}
|
||||
|
||||
/* Override Vivaldi's checkbox ::before and ::after */
|
||||
input[type=checkbox].tab-checkbox::before,
|
||||
input[type=checkbox].tab-checkbox::after {
|
||||
display: none !important;
|
||||
content: none !important;
|
||||
}
|
||||
|
||||
.tab-checkbox:checked {
|
||||
border-color: var(--colorFg) !important;
|
||||
background-color: var(--colorHighlightBg) !important;
|
||||
}
|
||||
|
||||
/* Our custom checkmark - more specific selector */
|
||||
|
||||
input[type=checkbox].tab-checkbox:checked::after {
|
||||
content: '✓' !important;
|
||||
display: block !important;
|
||||
color: var(--colorFg) !important;
|
||||
font-size: 10px !important;
|
||||
font-weight: bold !important;
|
||||
position: absolute !important;
|
||||
top: -1px !important;
|
||||
left: 1px !important;
|
||||
transform: none !important;
|
||||
background: none !important;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
function injectCheckboxes(cycler) {
|
||||
const tabItems = cycler.querySelectorAll('ul.listed-tabs > li.visual-list');
|
||||
|
||||
chrome.tabs.query({currentWindow: true}, (allTabs) => {
|
||||
tabItems.forEach((li) => {
|
||||
if (processedTabs.has(li)) return;
|
||||
if (li.querySelector('.tab-checkbox')) {
|
||||
processedTabs.add(li);
|
||||
return;
|
||||
}
|
||||
|
||||
let matchingTab = null;
|
||||
|
||||
// Get URL from favicon
|
||||
const favicon = li.querySelector('.visual-tab-list-favicon');
|
||||
|
||||
if (favicon && favicon.srcset) {
|
||||
const match = favicon.srcset.match(/chrome:\/\/favicon\/size\/\d+\/(.+?)\s/);
|
||||
if (match) {
|
||||
const tabUrl = match[1];
|
||||
matchingTab = allTabs.find(t => t.url === tabUrl);
|
||||
}
|
||||
}
|
||||
|
||||
if (!matchingTab) {
|
||||
processedTabs.add(li);
|
||||
return;
|
||||
}
|
||||
|
||||
li.style.position = 'relative';
|
||||
li.style.display = 'flex';
|
||||
li.style.alignItems = 'center';
|
||||
li.style.gap = '8px';
|
||||
li.style.paddingRight = '25px';
|
||||
li.style.minWidth = '0';
|
||||
|
||||
const textNodes = Array.from(li.childNodes).filter(node => node.nodeType === Node.TEXT_NODE);
|
||||
textNodes.forEach(textNode => {
|
||||
if (textNode.textContent.trim()) {
|
||||
const span = document.createElement('span');
|
||||
span.textContent = textNode.textContent;
|
||||
span.style.overflow = 'hidden';
|
||||
span.style.textOverflow = 'ellipsis';
|
||||
span.style.whiteSpace = 'nowrap';
|
||||
span.style.flex = '1';
|
||||
span.style.minWidth = '0';
|
||||
span.style.cursor = 'pointer';
|
||||
|
||||
span.addEventListener('mousedown', function(e) {
|
||||
chrome.tabs.update(matchingTab.id, {active: true});
|
||||
|
||||
if (markedTabIds.size > 0) {
|
||||
executeBatchClose();
|
||||
}
|
||||
}, true);
|
||||
|
||||
li.replaceChild(span, textNode);
|
||||
}
|
||||
});
|
||||
|
||||
if (favicon) {
|
||||
favicon.style.flexShrink = '0';
|
||||
}
|
||||
|
||||
const checkbox = document.createElement('input');
|
||||
checkbox.type = 'checkbox';
|
||||
checkbox.className = 'tab-checkbox';
|
||||
checkbox.dataset.tabId = matchingTab.id;
|
||||
|
||||
checkbox.addEventListener('change', function() {
|
||||
const tabId = parseInt(checkbox.dataset.tabId);
|
||||
|
||||
if (checkbox.checked) {
|
||||
markedTabIds.add(tabId);
|
||||
} else {
|
||||
markedTabIds.delete(tabId);
|
||||
}
|
||||
});
|
||||
|
||||
// Block propagation to prevent tab activation
|
||||
checkbox.addEventListener('mousedown', function(e) {
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
}, true);
|
||||
|
||||
checkbox.addEventListener('mouseup', function(e) {
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
}, true);
|
||||
|
||||
checkbox.addEventListener('click', function(e) {
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
}, true);
|
||||
|
||||
li.appendChild(checkbox);
|
||||
processedTabs.add(li);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
console.log('✅ Active');
|
||||
})();
|
||||
|
||||
|
||||
/*****************************************************************************************************************/
|
||||
|
||||
(async () => {
|
||||
'use strict';
|
||||
|
||||
const config = {
|
||||
// タブスタックにベースドメインを使用する (true: 有効, false: 無効)
|
||||
// Use the base domain for tab stacks (true: enabled, false: disabled)
|
||||
base_domain: true,
|
||||
|
||||
// タブスタックの名前を自動的に変更する (0: 無効, 1: ホスト名を使用, 2: ベースドメインから生成)
|
||||
// Automatically change the name of the tab stack (0: disabled, 1: use hostname, 2: generate from base domain)
|
||||
rename_stack: 0,
|
||||
|
||||
// 自動タブスタックを許可するワークスペース (完全一致もしくは <default_workspace>)
|
||||
// * 未設定の場合はすべてのワークスペースで自動タブスタックを許可する
|
||||
// Workspaces that allow automatic tab stacking (exact match or <default_workspace>)
|
||||
// * If not set, automatic tab stacking is allowed in all workspaces
|
||||
allow_workspaces: [
|
||||
// "<default_workspace>",
|
||||
// "Shopping",
|
||||
],
|
||||
|
||||
// 自動タブスタックを許可するドメイン (完全一致もしくは正規表現)
|
||||
// * 未設定の場合はすべてのドメインで自動タブスタックを許可する
|
||||
// Domains that allow automatic tab stacking (exact match or regular expression)
|
||||
// * If not set, automatic tab stacking is allowed for all domains
|
||||
allow_domains: [
|
||||
// "www.example.com",
|
||||
// /^(.+\.)?example\.net$/,
|
||||
],
|
||||
|
||||
// 自動タブスタックから除外するドメイン (完全一致もしくは正規表現)
|
||||
// Domains to exclude from automatic tab stacking (exact match or regular expression)
|
||||
block_domains: [
|
||||
// "www.example.com",
|
||||
// /^(.+\.)?example\.net$/,
|
||||
],
|
||||
};
|
||||
|
||||
const mergeArrays = (...arrays) => [...new Set(arrays.flat())];
|
||||
|
||||
const getUrlFragments = (url) => vivaldi.utilities.getUrlFragments(url);
|
||||
|
||||
const getBaseDomain = (url) => {
|
||||
const {hostForSecurityDisplay, tld} = getUrlFragments(url);
|
||||
return hostForSecurityDisplay.match(`([^.]+\\.${ tld })$`)?.[1] || hostForSecurityDisplay;
|
||||
};
|
||||
|
||||
const getHostname = (url) => {
|
||||
const {hostForSecurityDisplay} = getUrlFragments(url);
|
||||
return config.base_domain ? getBaseDomain(url) : hostForSecurityDisplay;
|
||||
};
|
||||
|
||||
const matchHostRule = (url, rule) => {
|
||||
const {hostForSecurityDisplay} = getUrlFragments(url);
|
||||
return rule instanceof RegExp ? rule.test(hostForSecurityDisplay) : hostForSecurityDisplay === rule;
|
||||
};
|
||||
|
||||
const getTab = async (tabId) => {
|
||||
const tab = await chrome.tabs.get(tabId);
|
||||
|
||||
if (tab.vivExtData) {
|
||||
tab.vivExtData = JSON.parse(tab.vivExtData);
|
||||
return tab;
|
||||
}
|
||||
};
|
||||
|
||||
const getTabIndex = async (tabId) => (await getTab(tabId)).index;
|
||||
|
||||
const getWorkspaceName = async (workspaceId) => {
|
||||
if (!workspaceId) {
|
||||
return '<default_workspace>';
|
||||
}
|
||||
const workspaceList = await vivaldi.prefs.get('vivaldi.workspaces.list');
|
||||
return workspaceList.find(item => item.id === workspaceId).name;
|
||||
};
|
||||
|
||||
const getTabsByWorkspace = async () => {
|
||||
const tabs = (await chrome.tabs.query({ currentWindow: true }))
|
||||
.filter(tab => tab.id !== -1 && tab.vivExtData)
|
||||
.map(tab => Object.assign(tab, { vivExtData: JSON.parse(tab.vivExtData) }))
|
||||
.filter(tab => !tab.pinned && !tab.vivExtData.panelId)
|
||||
.filter(tab => !config.allow_domains.length || config.allow_domains.find(rule => matchHostRule(tab.url, rule)))
|
||||
.filter(tab => !config.block_domains.length || !config.block_domains.find(rule => matchHostRule(tab.url, rule)));
|
||||
|
||||
return Object.groupBy(tabs, tab => tab.vivExtData.workspaceId);
|
||||
};
|
||||
|
||||
const getTabsByStack = (tabs) => Object.groupBy(tabs, tab => tab.vivExtData.group);
|
||||
|
||||
const getTabsByHost = (tabs) => Object.groupBy(tabs, tab => getHostname(tab.url));
|
||||
|
||||
const getMaxTabsStackId = (tabsByStack, targetHost) => {
|
||||
const counts = {};
|
||||
|
||||
for (const [stackId, tabs] of Object.entries(tabsByStack)) {
|
||||
if (stackId !== 'undefined') {
|
||||
const tabsByHost = getTabsByHost(tabs);
|
||||
const count = tabsByHost[targetHost]?.length || 0;
|
||||
|
||||
delete tabsByHost[targetHost];
|
||||
counts[stackId] = Object.values(tabsByHost)
|
||||
.reduce((acc, tabs) => {
|
||||
return acc > tabs.length ? acc : 0;
|
||||
}, count);
|
||||
}
|
||||
}
|
||||
|
||||
return Object.entries(counts)
|
||||
.reduce((acc, [stackId, count]) => {
|
||||
return acc[1] < count ? [stackId, count] : acc;
|
||||
}, [, 0])[0];
|
||||
};
|
||||
|
||||
const getTabStackName = (url) => {
|
||||
let stackName;
|
||||
|
||||
switch (config.rename_stack) {
|
||||
case 1:
|
||||
stackName = getHostname(url);
|
||||
break;
|
||||
case 2:
|
||||
stackName = getBaseDomain(url).split('.')[0];
|
||||
stackName = stackName.charAt(0).toUpperCase() + stackName.slice(1);
|
||||
break;
|
||||
}
|
||||
return stackName;
|
||||
};
|
||||
|
||||
const addTabStack = async (tabId, stackId, stackName) => {
|
||||
const {vivExtData} = await getTab(tabId);
|
||||
|
||||
if (stackName) {
|
||||
vivExtData.fixedGroupTitle = stackName;
|
||||
}
|
||||
vivExtData.group = stackId;
|
||||
chrome.tabs.update(tabId, { vivExtData: JSON.stringify(vivExtData) });
|
||||
};
|
||||
|
||||
const stackingTabs = async (workspaceId) => {
|
||||
const workspaceName = await getWorkspaceName(workspaceId);
|
||||
|
||||
if (!config.allow_workspaces.length || config.allow_workspaces.includes(workspaceName)) {
|
||||
const tabsByWorkspace = await getTabsByWorkspace();
|
||||
const tabsByStack = getTabsByStack(tabsByWorkspace[workspaceId]);
|
||||
const tabsByHost = getTabsByHost(tabsByWorkspace[workspaceId]);
|
||||
|
||||
for (const [host, tabs] of Object.entries(tabsByHost)) {
|
||||
const targetStackId = getMaxTabsStackId(tabsByStack, host) || crypto.randomUUID();
|
||||
const targetStackTabs = tabsByStack[targetStackId] ? getTabsByHost(tabsByStack[targetStackId])[host] : [];
|
||||
const targetTabs = mergeArrays(targetStackTabs, tabs);
|
||||
const targetStackName = getTabStackName(tabs[0].pendingUrl || tabs[0].url);
|
||||
|
||||
let tabIndex = await getTabIndex(targetTabs[0].id);
|
||||
|
||||
for (const tab of targetTabs) {
|
||||
addTabStack(tab.id, targetStackId, targetStackName);
|
||||
chrome.tabs.move(tab.id, { index: tabIndex });
|
||||
tabIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
chrome.webNavigation.onCommitted.addListener(async details => {
|
||||
if (details.tabId !== -1) {
|
||||
const tab = await getTab(details.tabId);
|
||||
|
||||
if (tab && !tab.pinned && !tab.vivExtData.panelId && details.frameType === 'outermost_frame') {
|
||||
const workspaceId = tab.vivExtData.workspaceId;
|
||||
stackingTabs(workspaceId);
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
grid-template-columns: unset;
|
||||
}
|
||||
|
||||
/* Width of empty spacer before tabs */
|
||||
#browser #tabs-tabbar-container .toolbar-tabbar-before {
|
||||
min-width: 0px;
|
||||
}
|
||||
|
||||
/* Skinnier mainbar */
|
||||
.mainbar .toolbar-mainbar.toolbar-addressbar,
|
||||
.mainbar .toolbar-mainbar .button-toolbar button {
|
||||
@@ -42,34 +47,6 @@
|
||||
border-top: unset !important;
|
||||
}
|
||||
|
||||
/* SiteInfoButton */
|
||||
.SiteInfoButton {
|
||||
opacity: unset !important;
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
}
|
||||
|
||||
/* SiteInfo V logo */
|
||||
.SiteInfoButton.internal svg path, .SiteInfoButton.warning svg path {
|
||||
d: path("M10.4 5c-.4-.8 0-1.8 1-2 .7 0 1.5.4 1.6 1.2a1.4 1.4 0 0 1-.2 1l-4 7c-.3.5-.7.8-1.2.8-.6 0-1-.2-1.3-.7L3.8 7.8 2.2 5c-.5-.8 0-2 1-2 .7 0 1 .2 1.4.7l1 2 1 1.4a2 2 0 0 0 1.7 1.5 2.2 2.2 0 0 0 2.3-2V6c0-.3 0-.6-.2-1z");
|
||||
}
|
||||
|
||||
/* SiteInfo old style lock */
|
||||
.SiteInfoButton.secure svg path, .SiteInfoButton.certified svg path {
|
||||
d: path("M12.2 6.7C12.2 4 10.5 2 8 2 5.8 2 3.8 4 3.8 6.7V7H3v7h10V7h-.8v-.3zM10.7 8H5.3V6.7c0-1.7 1.4-3 2.8-3 1.7 0 2.8 1.3 2.8 3V8z");
|
||||
fill: #00a100;
|
||||
}
|
||||
|
||||
/* SiteInfo secure background colour */
|
||||
.SiteInfoButton.secure, .SiteInfoButton.certified {
|
||||
background-color: #004400 !important;
|
||||
}
|
||||
|
||||
/* SiteInfo hide EV/Not Secure text */
|
||||
.SiteInfoButton .siteinfo-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Remove panels switch top padding */
|
||||
#switch {
|
||||
padding-top: unset;
|
||||
@@ -124,7 +101,7 @@
|
||||
}
|
||||
|
||||
/* Extensions toggle button */
|
||||
.toolbar-extensions .ToolbarButton-Button[name="Extensions"] .button-icon {
|
||||
.toolbar-extensions .ExtensionToggleIcon .button-icon {
|
||||
opacity: 0.5 !important;
|
||||
}
|
||||
|
||||
@@ -210,12 +187,4 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Width of empty spacer before tabs */
|
||||
.tabs-top #tabs-tabbar-container .toolbar-tabbar-before:empty {
|
||||
min-width: 0px;
|
||||
}
|
||||
|
||||
/* TEMP FIX FOR TAB BAR */
|
||||
#tabs-tabbar-container.top {
|
||||
padding-top: unset;
|
||||
}
|
||||
|
||||
Binary file not shown.
52
Vivaldi/userscript/Override Client Hints API - Basic.user.js
Executable file
52
Vivaldi/userscript/Override Client Hints API - Basic.user.js
Executable file
@@ -0,0 +1,52 @@
|
||||
// ==UserScript==
|
||||
// @name Override Client Hints API - Basic
|
||||
// @namespace
|
||||
// @version 1.0
|
||||
// @description A script to override the Client Hints JavaScript API
|
||||
// @author
|
||||
// @match https://*/*
|
||||
// @run-at document-start
|
||||
// @grant none
|
||||
// @license MIT
|
||||
// ==/UserScript==
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
const ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36";
|
||||
|
||||
function override(obj, prop, value) {
|
||||
Object.defineProperty(obj, prop, {
|
||||
get: () => value,
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
|
||||
override(navigator, "userAgent", ua);
|
||||
override(navigator, "appVersion", ua.slice("Mozilla/".length));
|
||||
override(navigator, "vendor", "Google Inc.");
|
||||
override(navigator, "platform", "Win32");
|
||||
override(navigator, "hardwareConcurrency", 8);
|
||||
override(navigator, "deviceMemory", 8);
|
||||
|
||||
override(navigator, "userAgentData", {
|
||||
brands: [
|
||||
{ brand: "Vivaldi", version: "7.10" },
|
||||
{ brand: "Chromium", version: "146" },
|
||||
{ brand: "Not=A?Brand", version: "24" }
|
||||
],
|
||||
fullVersionList: [
|
||||
{ brand: "Vivaldi", version: "7.10" },
|
||||
{ brand: "Chromium", version: "146.0.0.0" },
|
||||
{ brand: "Not=A?Brand", version: "24.0.0.0" }
|
||||
],
|
||||
uaFullVersion: "146.0.0.0",
|
||||
platform: "Windows",
|
||||
platformVersion: "15.0.0",
|
||||
architecture: "x86",
|
||||
bitness: "64",
|
||||
mobile: false,
|
||||
wow64: false
|
||||
});
|
||||
|
||||
})();
|
||||
92
Vivaldi/userscript/Override Client Hints API.user.js
Executable file
92
Vivaldi/userscript/Override Client Hints API.user.js
Executable file
@@ -0,0 +1,92 @@
|
||||
// ==UserScript==
|
||||
// @name Override Client Hints API
|
||||
// @namespace
|
||||
// @version 1.0
|
||||
// @description A script to override the Client Hints JavaScript API
|
||||
// @author
|
||||
// @match https://*/*
|
||||
// @run-at document-start
|
||||
// @grant none
|
||||
// @license MIT
|
||||
// ==/UserScript==
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// Use full version in UA string (e.g. Chrome/133.0.6943.126) to match real Chrome
|
||||
const VIVALDI_VERSION = "7.10";
|
||||
const VIVALDI_FULL_VERSION = "7.10";
|
||||
const CHROME_VERSION = "146";
|
||||
const CHROME_FULL_VERSION = "146.0.7680.171";
|
||||
const chromeUA = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36`;
|
||||
|
||||
function override(obj, prop, value) {
|
||||
Object.defineProperty(obj, prop, {
|
||||
get: () => value,
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Basic navigator properties
|
||||
override(Navigator.prototype, "userAgent", chromeUA);
|
||||
override(Navigator.prototype, "appVersion", chromeUA.slice("Mozilla/".length));
|
||||
override(Navigator.prototype, "platform", "Win32");
|
||||
override(Navigator.prototype, "vendor", "Google Inc.");
|
||||
override(Navigator.prototype, "oscpu", undefined); // Chrome doesn't expose this; Firefox does
|
||||
override(Navigator.prototype, "productSub", "20030107"); // Chrome: "20030107", Firefox: "20100101"
|
||||
|
||||
// User-Agent Client Hints
|
||||
// GREASE brand string and version rotate based on Chrome major version to prevent fingerprinting
|
||||
function getGreasedBrand(major) {
|
||||
const n = parseInt(major, 10) % 3;
|
||||
const brandStrings = ["Not A(Brand", "Not)A;Brand", "Not:A-Brand"];
|
||||
const brandVersions = ["8", "99", "24"];
|
||||
return { brand: brandStrings[n], version: brandVersions[n] };
|
||||
}
|
||||
|
||||
const greaseBrand = getGreasedBrand(CHROME_VERSION);
|
||||
const greaseBrandFull = { brand: greaseBrand.brand, version: `${greaseBrand.version}.0.0.0` };
|
||||
|
||||
const brands = [
|
||||
greaseBrand,
|
||||
{ brand: "Vivaldi", version: VIVALDI_VERSION },
|
||||
{ brand: "Chromium", version: CHROME_VERSION }
|
||||
];
|
||||
const fullVersionList = [
|
||||
greaseBrandFull,
|
||||
{ brand: "Vivaldi", version: VIVALDI_FULL_VERSION },
|
||||
{ brand: "Chromium", version: CHROME_FULL_VERSION }
|
||||
];
|
||||
|
||||
// Built once, reused on every getHighEntropyValues() call
|
||||
const highEntropyMap = {
|
||||
architecture: "x86",
|
||||
bitness: "64",
|
||||
brands,
|
||||
fullVersionList,
|
||||
mobile: false,
|
||||
model: "",
|
||||
platform: "Windows",
|
||||
platformVersion: "15.0.0",
|
||||
uaFullVersion: CHROME_FULL_VERSION
|
||||
};
|
||||
|
||||
const uaData = {
|
||||
brands,
|
||||
mobile: false,
|
||||
platform: "Windows",
|
||||
getHighEntropyValues: async function(hints) {
|
||||
const result = {};
|
||||
for (const hint of hints) {
|
||||
if (hint in highEntropyMap) result[hint] = highEntropyMap[hint];
|
||||
}
|
||||
return result;
|
||||
},
|
||||
toJSON: function() {
|
||||
return { brands, mobile: false, platform: "Windows" };
|
||||
}
|
||||
};
|
||||
override(Navigator.prototype, "userAgentData", uaData);
|
||||
})();
|
||||
Reference in New Issue
Block a user