mirror of
https://github.com/He4eT/tabswiper.git
synced 2026-05-05 00:57:23 +00:00
Initial commit
This commit is contained in:
parent
b47ce482b8
commit
9306068d2a
7 changed files with 478 additions and 0 deletions
7
.editorconfig
Normal file
7
.editorconfig
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
max_line_length = 80
|
||||
197
api/tabs/tabs.js
Normal file
197
api/tabs/tabs.js
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
export const getWindowTabs = () => {
|
||||
return browser.tabs.query({currentWindow: true})
|
||||
}
|
||||
|
||||
export const removeTab = (tab) => {
|
||||
browser.tabs.remove(tab.id)
|
||||
}
|
||||
|
||||
/**
|
||||
* listTabs to switch to
|
||||
*/
|
||||
function listTabs() {
|
||||
getCurrentWindowTabs().then((tabs) => {
|
||||
let tabsList = document.getElementById('tabs-list');
|
||||
let currentTabs = document.createDocumentFragment();
|
||||
let limit = 5;
|
||||
let counter = 0;
|
||||
|
||||
tabsList.textContent = '';
|
||||
|
||||
for (let tab of tabs) {
|
||||
if (!tab.active && counter <= limit) {
|
||||
let tabLink = document.createElement('a');
|
||||
|
||||
tabLink.textContent = tab.title || tab.id;
|
||||
tabLink.setAttribute('href', tab.id);
|
||||
tabLink.classList.add('switch-tabs');
|
||||
currentTabs.appendChild(tabLink);
|
||||
}
|
||||
|
||||
counter += 1;
|
||||
}
|
||||
|
||||
tabsList.appendChild(currentTabs);
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", listTabs);
|
||||
|
||||
function getCurrentWindowTabs() {
|
||||
return browser.tabs.query({currentWindow: true});
|
||||
}
|
||||
|
||||
document.addEventListener("click", (e) => {
|
||||
function callOnActiveTab(callback) {
|
||||
getCurrentWindowTabs().then((tabs) => {
|
||||
for (let tab of tabs) {
|
||||
if (tab.active) {
|
||||
callback(tab, tabs);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (e.target.id === "tabs-move-beginning") {
|
||||
callOnActiveTab((tab, tabs) => {
|
||||
let index = 0;
|
||||
if (!tab.pinned) {
|
||||
index = firstUnpinnedTab(tabs);
|
||||
}
|
||||
console.log(`moving ${tab.id} to ${index}`)
|
||||
browser.tabs.move([tab.id], {index});
|
||||
});
|
||||
}
|
||||
|
||||
if (e.target.id === "tabs-move-end") {
|
||||
callOnActiveTab((tab, tabs) => {
|
||||
let index = -1;
|
||||
if (tab.pinned) {
|
||||
let lastPinnedTab = Math.max(0, firstUnpinnedTab(tabs) - 1);
|
||||
index = lastPinnedTab;
|
||||
}
|
||||
browser.tabs.move([tab.id], {index});
|
||||
});
|
||||
}
|
||||
|
||||
else if (e.target.id === "tabs-duplicate") {
|
||||
callOnActiveTab((tab) => {
|
||||
browser.tabs.duplicate(tab.id);
|
||||
});
|
||||
}
|
||||
|
||||
else if (e.target.id === "tabs-reload") {
|
||||
callOnActiveTab((tab) => {
|
||||
browser.tabs.reload(tab.id);
|
||||
});
|
||||
}
|
||||
|
||||
else if (e.target.id === "tabs-remove") {
|
||||
callOnActiveTab((tab) => {
|
||||
browser.tabs.remove(tab.id);
|
||||
});
|
||||
}
|
||||
|
||||
else if (e.target.id === "tabs-create") {
|
||||
browser.tabs.create({url: "https://developer.mozilla.org/en-US/Add-ons/WebExtensions"});
|
||||
}
|
||||
|
||||
else if (e.target.id === "tabs-create-reader") {
|
||||
browser.tabs.create({url: "https://developer.mozilla.org/en-US/Add-ons/WebExtensions", openInReaderMode: true});
|
||||
}
|
||||
|
||||
else if (e.target.id === "tabs-alertinfo") {
|
||||
callOnActiveTab((tab) => {
|
||||
let props = "";
|
||||
for (let item in tab) {
|
||||
props += `${ item } = ${ tab[item] } \n`;
|
||||
}
|
||||
alert(props);
|
||||
});
|
||||
}
|
||||
|
||||
else if (e.target.id === "tabs-add-zoom") {
|
||||
callOnActiveTab((tab) => {
|
||||
let gettingZoom = browser.tabs.getZoom(tab.id);
|
||||
gettingZoom.then((zoomFactor) => {
|
||||
//the maximum zoomFactor is 5, it can't go higher
|
||||
if (zoomFactor >= MAX_ZOOM) {
|
||||
alert("Tab zoom factor is already at max!");
|
||||
} else {
|
||||
let newZoomFactor = zoomFactor + ZOOM_INCREMENT;
|
||||
//if the newZoomFactor is set to higher than the max accepted
|
||||
//it won't change, and will never alert that it's at maximum
|
||||
newZoomFactor = newZoomFactor > MAX_ZOOM ? MAX_ZOOM : newZoomFactor;
|
||||
browser.tabs.setZoom(tab.id, newZoomFactor);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
else if (e.target.id === "tabs-decrease-zoom") {
|
||||
callOnActiveTab((tab) => {
|
||||
let gettingZoom = browser.tabs.getZoom(tab.id);
|
||||
gettingZoom.then((zoomFactor) => {
|
||||
//the minimum zoomFactor is 0.3, it can't go lower
|
||||
if (zoomFactor <= MIN_ZOOM) {
|
||||
alert("Tab zoom factor is already at minimum!");
|
||||
} else {
|
||||
let newZoomFactor = zoomFactor - ZOOM_INCREMENT;
|
||||
//if the newZoomFactor is set to lower than the min accepted
|
||||
//it won't change, and will never alert that it's at minimum
|
||||
newZoomFactor = newZoomFactor < MIN_ZOOM ? MIN_ZOOM : newZoomFactor;
|
||||
browser.tabs.setZoom(tab.id, newZoomFactor);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
else if (e.target.id === "tabs-default-zoom") {
|
||||
callOnActiveTab((tab) => {
|
||||
let gettingZoom = browser.tabs.getZoom(tab.id);
|
||||
gettingZoom.then((zoomFactor) => {
|
||||
if (zoomFactor == DEFAULT_ZOOM) {
|
||||
alert("Tab zoom is already at the default zoom factor");
|
||||
} else {
|
||||
browser.tabs.setZoom(tab.id, DEFAULT_ZOOM);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
else if (e.target.classList.contains('switch-tabs')) {
|
||||
let tabId = +e.target.getAttribute('href');
|
||||
|
||||
browser.tabs.query({
|
||||
currentWindow: true
|
||||
}).then((tabs) => {
|
||||
for (let tab of tabs) {
|
||||
if (tab.id === tabId) {
|
||||
browser.tabs.update(tabId, {
|
||||
active: true
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
//onRemoved listener. fired when tab is removed
|
||||
browser.tabs.onRemoved.addListener((tabId, removeInfo) => {
|
||||
console.log(`The tab with id: ${tabId}, is closing`);
|
||||
|
||||
if(removeInfo.isWindowClosing) {
|
||||
console.log(`Its window is also closing.`);
|
||||
} else {
|
||||
console.log(`Its window is not closing`);
|
||||
}
|
||||
});
|
||||
|
||||
//onMoved listener. fired when tab is moved into the same window
|
||||
browser.tabs.onMoved.addListener((tabId, moveInfo) => {
|
||||
let startIndex = moveInfo.fromIndex;
|
||||
let endIndex = moveInfo.toIndex;
|
||||
console.log(`Tab with id: ${tabId} moved from index: ${startIndex} to index: ${endIndex}`);
|
||||
});
|
||||
21
manifest.json
Normal file
21
manifest.json
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"action": {
|
||||
"default_title": "Tabswiper",
|
||||
"default_popup": "popup/tabswiper/tabswiper.html"
|
||||
},
|
||||
"commands": {
|
||||
"_execute_action": {
|
||||
"suggested_key": {
|
||||
"default": "Shift+F2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Clear the list of your tabs from outdated and unnecessary ones",
|
||||
"homepage_url": "https://github.com/He4eT/tabswiper",
|
||||
"manifest_version": 3,
|
||||
"name": "Tabswiper",
|
||||
"permissions": [
|
||||
"tabs"
|
||||
],
|
||||
"version": "1.0"
|
||||
}
|
||||
57
popup/css/popup.css
Normal file
57
popup/css/popup.css
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
html, body {
|
||||
font-family: sans;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.popup {
|
||||
--color-bg: #eeeeee;
|
||||
--color-text: #333333;
|
||||
--color-accent: #666666;
|
||||
|
||||
--step: 8px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.popup {
|
||||
--color-bg: darkgray;
|
||||
--color-text: white;
|
||||
--color-accent: blue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Controls */
|
||||
|
||||
*:focus-visible {
|
||||
outline-color: var(--color-accent);
|
||||
outline-offset: 2px;
|
||||
outline-style: solid;
|
||||
outline-width: 1px;
|
||||
}
|
||||
|
||||
/* Button */
|
||||
|
||||
button {
|
||||
background: var(--color-bg);
|
||||
|
||||
border-radius: 0;
|
||||
border: 1px solid var(--color-text);
|
||||
|
||||
color: var(--color-text);
|
||||
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
|
||||
padding: var(--step);
|
||||
}
|
||||
|
||||
button:active {
|
||||
border-color: var(--color-accent);
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
/* Link */
|
||||
|
||||
a {
|
||||
color: var(--color-accent);
|
||||
}
|
||||
53
popup/tabswiper/tabswiper.css
Normal file
53
popup/tabswiper/tabswiper.css
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
.tabswiper {
|
||||
background-color: var(--color-bg);
|
||||
border: 1px solid var(--color-accent);
|
||||
color: var(--color-text);
|
||||
width: 640px;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
|
||||
.tabswiper header {
|
||||
padding: var(--step);
|
||||
border-block-end: 1px solid var(--color-accent);
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
aligh-items: center;
|
||||
}
|
||||
|
||||
/* Current Tab */
|
||||
|
||||
.tabswiper .currentTab {
|
||||
padding: calc(1 * var(--step)) var(--step);
|
||||
cursor: pointer;
|
||||
word-wrap: break-word;
|
||||
/* line-height: 1.5; */
|
||||
}
|
||||
|
||||
.tabswiper .currentTab .title {
|
||||
/* font-size: 1.2em; */
|
||||
margin-block-end: calc(1 * var(--step));
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tabswiper .currentTab .url {
|
||||
/* font-size: 0.8em; */
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
|
||||
.tabswiper footer {
|
||||
padding: var(--step);
|
||||
border-block-start: 1px solid var(--color-accent);
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--step);
|
||||
}
|
||||
|
||||
.tabswiper footer .actionButton {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
58
popup/tabswiper/tabswiper.html
Normal file
58
popup/tabswiper/tabswiper.html
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<!doctype html>
|
||||
<html lang='en'>
|
||||
<head>
|
||||
<meta charset='UTF-8'>
|
||||
<title>Tabswiper</title>
|
||||
<link rel='stylesheet' href='../css/popup.css' />
|
||||
<link rel='stylesheet' href='./tabswiper.css' />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main class='popup'>
|
||||
<article class='tabswiper'>
|
||||
<header>
|
||||
<section class='tabCounter'>
|
||||
Tab 1 of 20
|
||||
</section>
|
||||
<a
|
||||
class='infoLink'
|
||||
target='_blank'
|
||||
href='#'
|
||||
>
|
||||
Info
|
||||
</a>
|
||||
</header>
|
||||
<section class='currentTab'>
|
||||
<section id='titleTab' class='title'>
|
||||
Some title here
|
||||
</section>
|
||||
<a
|
||||
id='linkTab'
|
||||
class='url'
|
||||
href='https://some.url/here'
|
||||
>
|
||||
https://some.url/here
|
||||
</a>
|
||||
</section>
|
||||
<footer>
|
||||
<button
|
||||
id='buttonClose'
|
||||
class='actionButton close'
|
||||
title='Close this tab. Shortcuts: [J], [Left Arrow]'
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
<button
|
||||
id='buttonKeep'
|
||||
class='actionButton keep'
|
||||
title='Keep this tab. Shortcuts: [K], [Right Arrow]'
|
||||
>
|
||||
Keep
|
||||
</button>
|
||||
</footer>
|
||||
</article>
|
||||
</main>
|
||||
|
||||
<script type='module' src='./tabswiper.js'></script>
|
||||
</body>
|
||||
</html>
|
||||
85
popup/tabswiper/tabswiper.js
Normal file
85
popup/tabswiper/tabswiper.js
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/* */
|
||||
|
||||
const state = {
|
||||
tabs: [],
|
||||
skipped: [],
|
||||
currentTab: null,
|
||||
}
|
||||
|
||||
const updateState = () =>
|
||||
updateTabs()
|
||||
.then(updateCurrent)
|
||||
.then(updateInterface)
|
||||
|
||||
const updateTabs = () =>
|
||||
browser.tabs.query({currentWindow: true})
|
||||
.then((tabs) => tabs.reverse())
|
||||
.then((tabs) => void (state.tabs = tabs))
|
||||
|
||||
const updateCurrent = () => {
|
||||
const filteredTabs = state.tabs
|
||||
.filter(({ id }) => state.skipped.includes(id) === false)
|
||||
state.currentTab = filteredTabs[0] ?? null
|
||||
}
|
||||
|
||||
const keepTab = (tab) => {
|
||||
state.skipped.push(tab.id)
|
||||
updateState()
|
||||
}
|
||||
|
||||
const goToTab = (tab) => {
|
||||
browser.tabs.update(tab.id, { active: true })
|
||||
updateState()
|
||||
}
|
||||
|
||||
const closeTab = (tab) => {
|
||||
browser.tabs.remove(tab.id)
|
||||
updateState()
|
||||
}
|
||||
|
||||
/* */
|
||||
|
||||
const setDOMListeners = () => {
|
||||
const pairs = [
|
||||
['buttonClose', closeTab],
|
||||
['buttonKeep', keepTab],
|
||||
['linkTab', goToTab],
|
||||
]
|
||||
|
||||
pairs.forEach(([elementId, handler]) => {
|
||||
document.getElementById(elementId).addEventListener('click', (e) => {
|
||||
console.log('Element:', elementId, 'Handler:', handler)
|
||||
e.preventDefault()
|
||||
handler(state.currentTab)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const updateInterface = () => {
|
||||
console.log(state)
|
||||
|
||||
if (state.currentTab === null) {
|
||||
location.reload()
|
||||
return
|
||||
}
|
||||
|
||||
const items = [
|
||||
['titleTab', 'textContent', state.currentTab.title],
|
||||
['linkTab', 'textContent', state.currentTab.url],
|
||||
['linkTab', 'href', state.currentTab.url],
|
||||
]
|
||||
|
||||
items.forEach(([elementId, property, value]) => {
|
||||
document.getElementById(elementId)[property] = value
|
||||
})
|
||||
}
|
||||
|
||||
/* */
|
||||
|
||||
const init = () =>
|
||||
updateState()
|
||||
.then(setDOMListeners)
|
||||
// .then(setKeyboardListeners)
|
||||
// .then(setBrowserListeners)
|
||||
|
||||
init()
|
||||
Loading…
Add table
Add a link
Reference in a new issue