Initial commit

This commit is contained in:
He4eT 2023-08-15 14:57:00 +03:00
commit 9306068d2a
7 changed files with 478 additions and 0 deletions

7
.editorconfig Normal file
View 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
View 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
View 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
View 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);
}

View 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;
}

View 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>

View 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()