1
0
mirror of https://github.com/XFox111/TabsAsideExtension.git synced 2026-07-02 19:52:47 +03:00

Compare commits

...

11 Commits

Author SHA1 Message Date
Michael Gordeev d8c31e3da5 Minor 1.9 (#43)
- Now you can close the panel by clicking outside of it (#40)
- Added ability to move only selected tabs aside (#19)
- Context menu entries removed from pages' context menu (#41)
- After update extension now will open GitHub Releases page with the latest version
- Minor performance improvements

Co-authored-by: Amine A. <15179425+AmineI@users.noreply.github.com>
Co-Authored-By: Michael Gordeev <michael@xfox111.net>
2020-09-12 22:45:39 +03:00
Michael Gordeev 8d2c83eea6 Patch 1.8.1 (#38)
- Added spanish translation #37 

Co-authored-by: @Wafflects
2020-09-04 01:31:17 +03:00
Michael Gordeev 3e60583427 Minor 1.8 (#34)
- Ability to rename Tab Groups #33
- Added collections view switch #35
2020-08-29 18:39:51 +03:00
Michael Gordeev 10edf4f975 Repo documentation link typos fixed 2020-08-14 14:43:49 +03:00
Michael Gordeev cc854fbd25 Updated README.md 2020-08-11 19:04:20 +03:00
Michael Gordeev ad9acb6208 Create ci.yaml 2020-08-11 18:56:30 +03:00
Michael Gordeev f74eeb5759 [v1.7.3] Firefox compability patch (#32)
- Added Firefox support (#28)
- Updated settings defaults
- Updated `about:` protocol behavior
- Fixed collections header becoming the old one on a tab removing
- Added VS Code debugging config
- Removed trailing whitespaces
2020-08-10 23:46:37 +03:00
Michael Gordeev 80c9dbe1a5 HOTFIX: Chinese language pack path typo fixed 2020-08-05 12:29:01 +03:00
Michael Gordeev c777f1a60a Minor 1.7 (#31)
- Complete UI/UX overhaul
- Updated default hotkeys (#27)
- Added option to disable deletion confirmation (#29)
- Added Chinese translation
- Fixed font artifacts on Windows

Co-authored-by: Dustin-Jiang <56217843+Dustin-Jiang@users.noreply.github.com>
Co-Authored-By: Michael Gordeev <michael@xfox111.net>
2020-08-05 12:07:11 +03:00
Michael Gordeev 1b7b419168 Patch 1.6.1
One tab remove button removes whole collection fixed (#25)
2020-07-18 21:39:15 +03:00
Michael Gordeev 39f7cd391a Minor 1.6 (#24)
- Added localization system (#23)
- Added Russian locale (#23)
- Feedback button link is now based on the extension download source (#20)
- Added badge counter to the extension icon
- Added keyboard shortcuts (#22)
- Added action items to the extension icon context menu (#22)
- Added ability to change primary extension icon action to save tabs instead of opening the pane (#22)
2020-07-15 16:54:50 +03:00
16 changed files with 1328 additions and 275 deletions
+73
View File
@@ -0,0 +1,73 @@
name: CI
on:
workflow_dispatch:
push:
branches: [ master ]
paths:
# Trigger deploy on manifest change
- 'manifest.json'
jobs:
Firefox:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build Extension for Firefox
id: web-ext-build
uses: kewisch/action-web-ext@v1
with:
cmd: build
- name: Sign build
id: web-ext-sign
uses: kewisch/action-web-ext@v1
with:
cmd: sign
source: ${{ steps.web-ext-build.outputs.target }}
apiKey: ${{ secrets.FIREFOX_API_KEY }}
apiSecret: ${{ secrets.FIREFOX_CLIENT_SECRET }}
- name: Publish to Firefox Webstore
uses: trmcnvn/firefox-addon@v1
with:
uuid: tabsaside@xfox111.net
xpi: ${{ steps.web-ext-sign.outputs.target }}
manifest: ./manifest.json
api-key: ${{ secrets.FIREFOX_API_KEY }}
api-secret: ${{ secrets.FIREFOX_CLIENT_SECRET }}
- name: Drop artifacts
uses: actions/upload-artifact@v2
with:
name: 'Firefox Artefacts'
path: ${{ steps.web-ext-sign.outputs.target }}
Chrome:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Pack extension
uses: TheDoctor0/zip-release@0.4.1
with:
filename: ./TabsAside.zip
exclusions: '.git/* .vscode/* .github/* *.md'
- name: Publish to Chrome Webstore
uses: SebastienGllmt/chrome-addon@v3
with:
extension: mgmjbodjgijnebfgohlnjkegdpbdjgin
zip: ./TabsAside.zip
client-id: ${{ secrets.CHROME_CLIENT_ID }}
client-secret: ${{ secrets.CHROME_CLIENT_SECRET }}
refresh-token: ${{ secrets.CHROME_REFRESH_TOKEN }}
- name: Drop artifacts
uses: actions/upload-artifact@v2
with:
name: 'Chrome Artifacts'
path: ./TabsAside.zip
+13
View File
@@ -0,0 +1,13 @@
{
"version": "0.2.0",
"configurations":
[
{
"name": "Debug",
"type": "firefox",
"request": "launch",
"reAttach": true,
"addonPath": "${workspaceFolder}"
}
]
}
+8 -8
View File
@@ -4,7 +4,7 @@ Welcome, and thank you for your interest in contributing to my project!
There are many ways in which you can contribute, beyond writing code. The goal of this document is to provide a high-level overview of how you can get involved.
## Table of Contents
- [Contribution Guidelines](#gutschedule-contribution-guidelines)
- [Contribution Guidelines](#contribution-guidelines)
- [Table of Contents](#table-of-contents)
- [Asking Questions](#asking-questions)
- [Providing Feedback](#providing-feedback)
@@ -35,15 +35,15 @@ Have a question? Rather than opening an issue, please ask me directly on opensou
## Providing Feedback
Your comments and feedback are welcome.
You can leave your feedbak on feedback@xfox111.net or do it on [Microsoft Edge Add-ons Webstore](https://microsoftedge.microsoft.com/addons/detail/tabs-aside/kmnblllmalkiapkfknnlpobmjjdnlhnd) or [Chrome Webstore](https://chrome.google.com/webstore/detail/tabs-aside/mgmjbodjgijnebfgohlnjkegdpbdjgin)
You can leave your feedbak on feedback@xfox111.net or do it on [Microsoft Edge Add-ons Webstore](https://microsoftedge.microsoft.com/addons/detail/tabs-aside/kmnblllmalkiapkfknnlpobmjjdnlhnd), [Chrome Extensions Webstore](https://chrome.google.com/webstore/detail/tabs-aside/mgmjbodjgijnebfgohlnjkegdpbdjgin) or [Mozilla Add-ons Webstore](https://addons.mozilla.org/firefox/addon/ms-edge-tabs-aside/)
## Reporting Issues
Have you identified a reproducible problem in the extension? Have a feature request? I'd like to hear it! Here's how you can make reporting your issue as effective as possible.
### Look For an Existing Issue
Before you create a new issue, please do a search in [open issues](https://github.com/xfox111/ChromiumTabsAside/issues) to see if the issue or feature request has already been filed.
Before you create a new issue, please do a search in [open issues](https://github.com/xfox111/TabsAsideExtension/issues) to see if the issue or feature request has already been filed.
Be sure to scan through the [feature requests](https://github.com/XFox111/ChromiumTabsAside/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement).
Be sure to scan through the [feature requests](https://github.com/XFox111/TabsAsideExtension/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement).
If you find your issue already exists, make relevant comments and add your [reaction](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments). Use a reaction in place of a "+1" comment:
@@ -83,7 +83,7 @@ If you are interested in writing code to fix issues or implement new awesome fea
### Deploy test version on your browser
1. Clone repository to local storage using [Git](https://guides.github.com/introduction/git-handbook/)
```
git clone https://github.com/xfox111/ChromiumTabsAside.git
git clone https://github.com/xfox111/TabsAsideExtension.git
```
2. Enable Developers mode on your browser extensions page
3. Click "Load unpacked" button and navigate to the extension root folder (contains `manifest.json`)
@@ -135,14 +135,14 @@ Use "double quotes" wherever it's possible
}
```
- Put spaces between operators and before braces in methods declarations, conditionals and loops
- Wrong:
- Wrong:
- `y=k*x+b`
- `function FunctionName()`
- Correct:
- `y = k * x + b`
- `function FunctionName ()`
- Use ternary conditionals wherever it's possible
- Wrong:
- Wrong:
```
var s;
if (condition)
@@ -175,7 +175,7 @@ Use "double quotes" wherever it's possible
```
### Finding an issue to work on
Check out the [full issues list](https://github.com/XFox111/ChromiumTabsAside/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue) for a list of all potential areas for contributions. **Note** that just because an issue exists in the repository does not mean we will accept a contribution. There are several reasons we may not accept a pull request like:
Check out the [full issues list](https://github.com/XFox111/TabsAsideExtension/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue) for a list of all potential areas for contributions. **Note** that just because an issue exists in the repository does not mean we will accept a contribution. There are several reasons we may not accept a pull request like:
- Performance - One of Tabs Aside core values is to deliver a lightweight extension, that means it should perform well in both real and test environments.
- User experience - Since we want to deliver a lightweight extension, the UX should feel lightweight as well and not be cluttered. Most changes to the UI should go through the issue owner and project owner (@XFox111).
+29 -21
View File
@@ -1,56 +1,64 @@
# Tabs aside for Google Chrome
# Tabs aside
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/xfox111/TabsAsideExtension)](https://github.com/xfox111/TabsAsideExtension/releases/latest)
![CI](https://github.com/XFox111/TabsAsideExtension/workflows/CI/badge.svg)
[![Chrome Web Store](https://img.shields.io/chrome-web-store/users/mgmjbodjgijnebfgohlnjkegdpbdjgin?label=Chrome%20Web%20Store%20downloads)](https://chrome.google.com/webstore/detail/tabs-aside/mgmjbodjgijnebfgohlnjkegdpbdjgin)
[![Chrome Web Store](https://img.shields.io/chrome-web-store/users/mgmjbodjgijnebfgohlnjkegdpbdjgin?label=Chrome%20Webstore%20downloads)](https://chrome.google.com/webstore/detail/tabs-aside/mgmjbodjgijnebfgohlnjkegdpbdjgin)
[![Chrome Web Store](https://img.shields.io/chrome-web-store/rating/mgmjbodjgijnebfgohlnjkegdpbdjgin)](https://chrome.google.com/webstore/detail/tabs-aside/mgmjbodjgijnebfgohlnjkegdpbdjgin)
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/xfox111/chromiumtabsaside)](https://github.com/xfox111/chromiumtabsaside/releases/latest)
[![GitHub issues](https://img.shields.io/github/issues/xfox111/chromiumtabsaside)](https://github.com/xfox111/ChromiumTabsAside/issues)
[![GitHub last commit](https://img.shields.io/github/last-commit/xfox111/chromiumtabsaside)](https://github.com/xfox111/ChromiumTabsAside/commits/master)
[![GitHub repo size](https://img.shields.io/github/repo-size/xfox111/chromiumtabsaside?label=repo%20size)](https://github.com/xfox111/ChromiumTabsAside)
[![MIT License](https://img.shields.io/github/license/xfox111/chromiumtabsaside)](https://opensource.org/licenses/MIT)
[![Mozilla Add-on](https://img.shields.io/amo/users/ms-edge-tabs-aside?label=Firefox%20Webstore%20downloads)](https://addons.mozilla.org/firefox/addon/ms-edge-tabs-aside/)
[![Mozilla Add-on](https://img.shields.io/amo/rating/ms-edge-tabs-aside)](https://addons.mozilla.org/firefox/addon/ms-edge-tabs-aside/)
[![Mozilla Add-on](https://img.shields.io/amo/dw/ms-edge-tabs-aside)](https://addons.mozilla.org/firefox/addon/ms-edge-tabs-aside/)
[![GitHub issues](https://img.shields.io/github/issues/xfox111/TabsAsideExtension)](https://github.com/xfox111/TabsAsideExtension/issues)
[![GitHub last commit](https://img.shields.io/github/last-commit/xfox111/TabsAsideExtension)](https://github.com/xfox111/TabsAsideExtension/commits/master)
[![GitHub repo size](https://img.shields.io/github/repo-size/xfox111/TabsAsideExtension?label=repo%20size)](https://github.com/xfox111/TabsAsideExtension)
[![MIT License](https://img.shields.io/github/license/xfox111/TabsAsideExtension)](https://opensource.org/licenses/MIT)
[![Twitter Follow](https://img.shields.io/twitter/follow/xfox111?style=social)](https://twitter.com/xfox111)
[![GitHub followers](https://img.shields.io/github/followers/xfox111?label=Follow%20@xfox111&style=social)](https://github.com/xfox111)
[![Buy Me a Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-%40xfox111-orange)](https://buymeacoffee.com/xfox111)
![Tabs aside](https://xfox111.net/images/TabsAside.png)
![Tabs aside](https://xfox111.net/y7xk3z)
If youre like me, you often find yourself with a bunch of open tabs. Youd like to get those tabs out of the way sometimes, but theyre maybe not worth saving as actual bookmarks.
In the Edge browser, Microsoft has introduced a new feature called "Tabs aside" (or Tab groups) which lets you set tabs aside in a sort of temporary workspace so that you can call them back up later.
Unfortunately, in new Chromium-based Microsoft Edge, the devs decided not to implement this must-have-feature. So I've decided to create a browser extension which replicates this awesome feature in Chromium-based browsers
Unfortunately, in new Chromium-based Microsoft Edge, the devs decided not to implement this must-have-feature. So I've decided to create a browser extension which replicates this awesome feature
## Features
- Familiar UI inherited from legacy Microsoft Edge with some improvements
- Auto Dark mode
- Now you can restore one tab from collection without removing
- Now you can choose if you want to load restored tabs only when you're navigating onto them
- Set tabs you've selected aside
- **Now available for Firefox!**
## Download
- [Google Chrome Webstore](https://chrome.google.com/webstore/detail/tabs-aside/mgmjbodjgijnebfgohlnjkegdpbdjgin)
- [Microsoft Edge Add-ons Webstore](https://microsoftedge.microsoft.com/addons/detail/kmnblllmalkiapkfknnlpobmjjdnlhnd)
- [GitHub Releases](https://github.com/xfox111/chromiumtabsaside/releases/latest)
- [Firefox Add-ons](https://addons.mozilla.org/firefox/addon/ms-edge-tabs-aside/)
- [GitHub Releases](https://github.com/xfox111/TabsAsideExtension/releases/latest)
## Project roadmap
You can go to the project's [roadmap kanban board](https://github.com/XFox111/ChromiumTabsAside/projects/1) and see what have we planned and watch our progress in realtime
You can go to the project's [roadmap kanban board](https://github.com/XFox111/TabsAsideExtension/projects/1) and see what have we planned and watch our progress in realtime
## Contributing
There are many ways in which you can participate in the project, for example:
- [Submit bugs and feature requests](https://github.com/xfox111/gutschedule/issues), and help us verify as they are checked in
- Review [source code changes](https://github.com/xfox111/gutschedule/pulls)
- [Submit bugs and feature requests](https://github.com/xfox111/TabsAsideExtension/issues), and help us verify as they are checked in
- Review [source code changes](https://github.com/xfox111/TabsAsideExtension/pulls)
- Review documentation and make pull requests for anything from typos to new content
If you are interested in fixing issues and contributing directly to the code base, please see the [Contribution Guidelines](https://github.com/XFox111/ChromiumTabsAside/blob/master/CONTRIBUTING.md), which covers the following:
- [How to deploy the extension on your browser](https://github.com/XFox111/ChromiumTabsAside/blob/master/CONTRIBUTING.md#deploy-test-version-on-your-browser)
- [The development workflow](https://github.com/XFox111/ChromiumTabsAside/blob/master/CONTRIBUTING.md#development-workflow), including debugging and running tests
- [Coding guidelines](https://github.com/XFox111/ChromiumTabsAside/blob/master/CONTRIBUTING.md#coding-guidelines)
- [Submitting pull requests](https://github.com/XFox111/ChromiumTabsAside/blob/master/CONTRIBUTING.md#submitting-pull-requests)
- [Finding an issue to work on](https://github.com/XFox111/ChromiumTabsAside/blob/master/CONTRIBUTING.md#finding-an-issue-to-work-on)
- [Contributing to translations](https://github.com/XFox111/ChromiumTabsAside/blob/master/CONTRIBUTING.md#contributing-to-translations)
If you are interested in fixing issues and contributing directly to the code base, please see the [Contribution Guidelines](https://github.com/XFox111/TabsAsideExtension/blob/master/CONTRIBUTING.md), which covers the following:
- [How to deploy the extension on your browser](https://github.com/XFox111/TabsAsideExtension/blob/master/CONTRIBUTING.md#deploy-test-version-on-your-browser)
- [The development workflow](https://github.com/XFox111/TabsAsideExtension/blob/master/CONTRIBUTING.md#development-workflow), including debugging and running tests
- [Coding guidelines](https://github.com/XFox111/TabsAsideExtension/blob/master/CONTRIBUTING.md#coding-guidelines)
- [Submitting pull requests](https://github.com/XFox111/TabsAsideExtension/blob/master/CONTRIBUTING.md#submitting-pull-requests)
- [Finding an issue to work on](https://github.com/XFox111/TabsAsideExtension/blob/master/CONTRIBUTING.md#finding-an-issue-to-work-on)
- [Contributing to translations](https://github.com/XFox111/TabsAsideExtension/blob/master/CONTRIBUTING.md#contributing-to-translations)
## Code of Conduct
This project has adopted the Contributor Covenant. For more information see the [Code of Conduct](https://github.com/XFox111/ChromiumTabsAside/blob/master/CODE_OF_CONDUCT.md)
This project has adopted the Contributor Covenant. For more information see the [Code of Conduct](https://github.com/XFox111/TabsAsideExtension/blob/master/CODE_OF_CONDUCT.md)
## Copyrights
> ©2020 Michael "XFox" Gordeev
+48 -24
View File
@@ -2,46 +2,70 @@
<html>
<head>
<title>Tabs aside</title>
<title loc="name">Tabs aside</title>
<link id="icon" rel="shortcut icon" type="image/png" href="icons/light/empty/16.png" />
<link rel="stylesheet" type="text/css" href="css/style.css" />
<link rel="stylesheet" type="text/css" href="css/style.generic.css" />
<link rel="stylesheet" type="text/css" href="css/style.dark.css" />
<link rel="stylesheet" type="text/css" href="css/style.listview.css" />
<meta charset="utf-8" />
</head>
<body>
<div class="tabsAside background">
<div class="tabsAside closeArea"></div>
<aside class="tabsAside pane">
<header>
<div>
<h1>Tabs aside</h1>
<button class="btn more" title="Options"></button>
<h1 loc="name">Tabs aside</h1>
<button loc_alt="options" class="btn more" title="Options"></button>
<nav>
<p>
<input type="checkbox" id="loadOnRestore"/>
<label for="loadOnRestore">Load tabs on restore</label>
</p>
<div>
<button value="https://github.com/xfox111/ChromiumTabsAside">Visit GitHub page</button>
<button value="https://github.com/XFox111/ChromiumTabsAside/graphs/contributors">Project contributors</button>
<button value="https://chrome.google.com/webstore/detail/tabs-aside/mgmjbodjgijnebfgohlnjkegdpbdjgin">Leave feedback</button>
<button value="https://buymeacoffee.com/xfox111">Buy me a coffee!</button>
</div>
<p>
<small>v1.0</small><br />
Developed by Michael Gordeev (<a href="https://twitter.com/xfox111"
target="_blank">@xfox111</a>)
</p>
</nav>
<nav>
<p>
<input type="checkbox" id="loadOnRestore" />
<label loc="loadOnRestore" for="loadOnRestore">Load tabs on restore</label>
</p>
<p>
<input type="checkbox" id="swapIconAction" />
<label loc="swapIconAction" for="swapIconAction">Set tabs aside on extension icon click (Alt+P or right-click to open the pane)</label>
</p>
<p>
<input type="checkbox" id="showDeleteDialog" />
<label loc="showDeleteDialog" for="showDeleteDialog">Show confirmation dialog before deleting an item</label>
</p>
<hr />
<div>
<button loc="github" value="https://github.com/xfox111/TabsAsideExtension">Visit GitHub page</button>
<button loc="contributors" value="https://github.com/XFox111/TabsAsideExtension/graphs/contributors">Project contributors</button>
<button loc="feedback" feedback-button>Leave feedback</button>
<button loc="buyMeACoffee" value="https://buymeacoffee.com/xfox111">Buy me a coffee!</button>
</div>
<hr />
<p>
<small>v1.0</small><br />
<span loc="credits">Developed by Michael Gordeev</span> (<a href="https://twitter.com/xfox111" target="_blank">@xfox111</a>)
</p>
</nav>
<button loc_alt="closePanel" class="btn remove" title="Close panel"></button>
<a class="saveTabs"><span class="iconArrowRight"></span> <span loc="setAside">Set current tabs aside</span></a>
<div class="listviewSwitch tile">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div class="listviewSwitch list">
<div></div>
<div></div>
<div></div>
</div>
<a class="saveTabs"><span class="iconArrowRight"></span> Set current tabs aside</a>
<hr />
</header>
<section>
<h2>You have no aside tabs</h2>
<h2 loc="nothingSaved">You have no aside tabs</h2>
</section>
</aside>
</div>
+127
View File
@@ -0,0 +1,127 @@
{
"name":
{
"message": "Tabs aside",
"description": "Extension name. Displayed in the manifest and pane header"
},
"description":
{
"message": "Classic Microsoft Edge \"Tabs Aside\" feature",
"description": "Extension description"
},
"author":
{
"message": "Michael \"XFox\" Gordeev",
"description": "Author name"
},
"options":
{
"message": "Options",
"description": "Alternative text for options button in the pane"
},
"closePanel":
{
"message": "Close",
"description": "Alternative text for close panel button"
},
"loadOnRestore":
{
"message": "Load tabs on restore",
"description": "Label for option"
},
"showDeleteDialog":
{
"message": "Show confirmation dialog before deleting an item",
"description": "Label for option"
},
"swapIconAction":
{
"message": "Set tabs aside on extension icon click (%TOGGLE_SHORTCUT% or right-click to open the pane)",
"description": "Label for option"
},
"github":
{
"message": "Visit GitHub page",
"description": "Link title"
},
"contributors":
{
"message": "Project contributors",
"description": "Link title"
},
"feedback":
{
"message": "Leave feedback",
"description": "Link title"
},
"buyMeACoffee":
{
"message": "Buy me a coffee!",
"description": "Link title"
},
"credits":
{
"message": "Developed by Michael 'XFox' Gordeev",
"description": "Options menu credits"
},
"setAside":
{
"message": "Set current tabs aside",
"description": "Save collection action name. Used in the pane, extension context menu and manifest shortcuts"
},
"nothingSaved":
{
"message": "You have no aside tabs",
"description": "Placeholder for empty pane"
},
"removeTab":
{
"message": "Remove tab from collection",
"description": "Button hint on a tab card"
},
"restoreTabs":
{
"message": "Restore tabs",
"description": "Collection restore action link name"
},
"more":
{
"message": "More...",
"description": "Collections' more button title"
},
"restoreNoRemove":
{
"message": "Restore without removing",
"description": "Context action item name"
},
"removeCollection":
{
"message": "Remove collection",
"description": "Collection remove action name"
},
"removeCollectionConfirm":
{
"message": "Are you sure you want to delete this collection?",
"description": "Prompt dialog content on collection deletion"
},
"removeTabConfirm":
{
"message": "Are you sure you want to delete this tab?",
"description": "Prompt dialog content on one tab deletion"
},
"togglePaneContext":
{
"message": "Toggle tabs aside pane",
"description": "Context action name. Used in extension context menu and manifest shortcuts"
},
"noTabsToSave":
{
"message": "No tabs available to save",
"description": "Alert dialog message when there's no tabs to save"
},
"tabs":
{
"message": "items",
"description": "Collection tabs counter label (e.g. 8 items)"
}
}
+127
View File
@@ -0,0 +1,127 @@
{
"name":
{
"message": "Pestañas que has reservado",
"description": "Extension name. Displayed in the manifest and pane header"
},
"description":
{
"message": "Función \"Reservar pestañas\" de Microsoft Edge clásico",
"description": "Extension description"
},
"author":
{
"message": "Michael \"XFox\" Gordeev",
"description": "Author name"
},
"options":
{
"message": "Opciones",
"description": "Alternative text for options button in the pane"
},
"closePanel":
{
"message": "Cerrar",
"description": "Alternative text for close panel button"
},
"loadOnRestore":
{
"message": "Cargar pestañas al restaurar",
"description": "Label for option"
},
"showDeleteDialog":
{
"message": "Mostrar diálogo de confirmacion antes de eliminar un item",
"description": "Label for option"
},
"swapIconAction":
{
"message": "Reservar pestañas al apretar el icono de la extensión (%TOGGLE_SHORTCUT% o click-derecho para abrir el panel)",
"description": "Label for option"
},
"github":
{
"message": "Visitar la pagina de GitHub",
"description": "Link title"
},
"contributors":
{
"message": "Contribuidores del proyecto",
"description": "Link title"
},
"feedback":
{
"message": "Deja un comentario",
"description": "Link title"
},
"buyMeACoffee":
{
"message": "¡Comprame un cafe!",
"description": "Link title"
},
"credits":
{
"message": "Desarrollado por Michael 'XFox' Gordeev",
"description": "Options menu credits"
},
"setAside":
{
"message": "Reservar las pestañas actuales",
"description": "Save collection action name. Used in the pane, extension context menu and manifest shortcuts"
},
"nothingSaved":
{
"message": "No tienes pestañas reservadas",
"description": "Placeholder for empty pane"
},
"removeTab":
{
"message": "Eliminar pestañas de la colección",
"description": "Button hint on a tab card"
},
"restoreTabs":
{
"message": "Restaurar pestañas",
"description": "Collection restore action link name"
},
"more":
{
"message": "Más...",
"description": "Collections' more button title"
},
"restoreNoRemove":
{
"message": "Restaurar sin eliminar",
"description": "Context action item name"
},
"removeCollection":
{
"message": "Eliminar la colección",
"description": "Collection remove action name"
},
"removeCollectionConfirm":
{
"message": "¿Está seguro que quiere eliminar esta colección?",
"description": "Prompt dialog content on collection deletion"
},
"removeTabConfirm":
{
"message": "¿Está seguro que quiere eliminar esta pestaña?",
"description": "Prompt dialog content on one tab deletion"
},
"togglePaneContext":
{
"message": "Abrir panel de pestañas",
"description": "Context action name. Used in extension context menu and manifest shortcuts"
},
"noTabsToSave":
{
"message": "No hay pestañas disponibles para reservar",
"description": "Alert dialog message when there's no tabs to save"
},
"tabs":
{
"message": "items",
"description": "Collection tabs counter label (e.g. 8 items)"
}
}
+127
View File
@@ -0,0 +1,127 @@
{
"name":
{
"message": "Отложенные вкладки",
"description": "Extension name. Displayed in the manifest and pane header"
},
"description":
{
"message": "Функиця отложенных вкладок классического Microsoft Edge",
"description": "Extension description"
},
"author":
{
"message": "Михаил \"XFox\" Гордеев",
"description": "Author name"
},
"options":
{
"message": "Настройки",
"description": "Alternative text for options button in the pane"
},
"closePanel":
{
"message": "Закрыть",
"description": "Alternative text for close panel button"
},
"loadOnRestore":
{
"message": "Загружать вкладки после открытия",
"description": "Label for option"
},
"showDeleteDialog":
{
"message": "Показывать окно подтверждения перед удалением элемента",
"description": "Label for option"
},
"swapIconAction":
{
"message": "Откладывать вкладки при нажатии на иконку расширения (%TOGGLE_SHORTCUT% или правая кнопка мыши для открытия панели)",
"description": "Label for option"
},
"github":
{
"message": "Страница GitHub",
"description": "Link title"
},
"contributors":
{
"message": "Свой вклад вложили",
"description": "Link title"
},
"feedback":
{
"message": "Оставить отзыв",
"description": "Link title"
},
"buyMeACoffee":
{
"message": "Купить мне кофе!",
"description": "Link title"
},
"credits":
{
"message": "Разработано: Михаил 'XFox' Гордеев",
"description": "Options menu credits"
},
"setAside":
{
"message": "Отложить открытые вкладки",
"description": "Save collection action name. Used in the pane, extension context menu and manifest shortcuts"
},
"nothingSaved":
{
"message": "У вас нет отложенных вкладок",
"description": "Placeholder for empty pane"
},
"removeTab":
{
"message": "Удалить вкладку из коллекции",
"description": "Button hint on a tab card"
},
"restoreTabs":
{
"message": "Восстановить вкладки",
"description": "Collection restore action link name"
},
"more":
{
"message": "Больше...",
"description": "Collections' more button title"
},
"restoreNoRemove":
{
"message": "Восстановить без удаления",
"description": "Context action item name"
},
"removeCollection":
{
"message": "Удалить коллекцию",
"description": "Collection remove action name"
},
"removeCollectionConfirm":
{
"message": "Вы уверены, что хотите удалить эту коллекцию?",
"description": "Prompt dialog content on collection deletion"
},
"removeTabConfirm":
{
"message": "Вы уверены, что хотите удалить эту вкладку из коллекции?",
"description": "Prompt dialog content on one tab deletion"
},
"togglePaneContext":
{
"message": "Открыть/закрыть панель",
"description": "Context action name. Used in extension context menu and manifest shortcuts"
},
"noTabsToSave":
{
"message": "Нет доступных для сохранения вкладок",
"description": "Alert dialog message when there's no tabs to save"
},
"tabs":
{
"message": "вкладок",
"description": "Collection tabs counter label (e.g. 8 items)"
}
}
+127
View File
@@ -0,0 +1,127 @@
{
"name":
{
"message": "搁置的标签页",
"description": "Extension name. Displayed in the manifest and pane header"
},
"description":
{
"message": "为Chromium浏览器提供旧版 Microsoft Edge 中的\"搁置标签页\" 功能",
"description": "Extension description"
},
"author":
{
"message": "Michael \"XFox\" Gordeev",
"description": "Author name"
},
"options":
{
"message": "设置",
"description": "Alternative text for options button in the pane"
},
"closePanel":
{
"message": "关闭",
"description": "Alternative text for close panel button"
},
"loadOnRestore":
{
"message": "在重新打开时加载页面",
"description": "Label for option"
},
"showDeleteDialog":
{
"message": "在删除项目之前显示确认对话框",
"description": "Label for option"
},
"swapIconAction":
{
"message": "点击拓展图标来搁置所有标签页 (按%TOGGLE_SHORTCUT%或右键来打开侧栏)",
"description": "Label for option"
},
"github":
{
"message": "查看GitHub页面",
"description": "Link title"
},
"contributors":
{
"message": "项目贡献者",
"description": "Link title"
},
"feedback":
{
"message": "给我们反馈",
"description": "Link title"
},
"buyMeACoffee":
{
"message": "给我买杯咖啡!",
"description": "Link title"
},
"credits":
{
"message": "由Michael 'XFox' Gordeev开发",
"description": "Options menu credits"
},
"setAside":
{
"message": "搁置当前的所有标签页",
"description": "Save collection action name. Used in the pane, extension context menu and manifest shortcuts"
},
"nothingSaved":
{
"message": "你目前没有搁置的标签页",
"description": "Placeholder for empty pane"
},
"removeTab":
{
"message": "从标签页集中移除标签页",
"description": "Button hint on a tab card"
},
"restoreTabs":
{
"message": "恢复标签页",
"description": "Collection restore action link name"
},
"more":
{
"message": "更多...",
"description": "Collections' more button title"
},
"restoreNoRemove":
{
"message": "恢复但不移除标签页",
"description": "Context action item name"
},
"removeCollection":
{
"message": "移除标签页集",
"description": "Collection remove action name"
},
"removeCollectionConfirm":
{
"message": "你确定要移除这个标签页集吗?",
"description": "Prompt dialog content on collection deletion"
},
"removeTabConfirm":
{
"message": "你确定要移除这个标签页吗?",
"description": "Prompt dialog content on one tab deletion"
},
"togglePaneContext":
{
"message": "打开或关闭侧栏",
"description": "Context action name. Used in extension context menu and manifest shortcuts"
},
"noTabsToSave":
{
"message": "没有可以搁置的标签页",
"description": "Alert dialog message when there's no tabs to save"
},
"tabs":
{
"message": "项",
"description": "Collection tabs counter label (e.g. 8 items)"
}
}
+104 -52
View File
@@ -10,6 +10,17 @@
color: black;
}
.tabsAside.closeArea
{
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
background-color: transparent;
}
.tabsAside.pane
{
user-select: none;
@@ -18,8 +29,10 @@
right: 0px;
top: 0px;
bottom: 0px;
overflow: auto;
overflow: hidden;
display: grid;
grid-template-rows: auto 1fr;
width: 100%;
min-width: 500px;
@@ -36,7 +49,7 @@
aside[embedded]
{
width: 40% !important;
width: 500px !important;
}
.tabsAside.pane[opened]
@@ -47,36 +60,28 @@
/* Pane header*/
.tabsAside.pane > header
{
margin: 20px 40px;
z-index: 1;
padding: 14px 20px 16px 20px;
box-shadow: 0px 0px 5px rgba(0,0,0,.5);
background-color: white;
display: grid;
grid-template-columns: 1fr auto auto;
grid-column-gap: 10px;
grid-row-gap: 30px;
}
.tabsAside.pane > header > div
{
display: grid;
grid-template-columns: 1fr auto;
}
.tabsAside.pane > header > div > h1
.tabsAside.pane > header > h1
{
margin: 10px 0px;
font-weight: normal;
font-size: 21pt;
margin: 0px 5px;
font-weight: 500;
font-size: 15pt;
}
.tabsAside.pane > header > div > button
.tabsAside.pane > header > nav
{
margin: auto;
top: 45px;
right: 55px;
}
.tabsAside.pane > header > div > nav
{
top: 70px;
right: 40px;
}
.tabsAside.pane > header nav > div
{
box-shadow: 0px 4px 5px -2px rgba(100, 100, 100, .5);
}
.tabsAside.pane > header nav > p
{
@@ -88,25 +93,37 @@
text-decoration: none;
}
.iconArrowRight
.saveTabs
{
width: 13px;
height: 13px;
display: inline-block;
background-repeat: no-repeat;
background-size: 13px;
background-position: center;
background-image: url("chrome-extension://__MSG_@@extension_id__/icons/arrowRight.svg");
display: inline-grid;
grid-template-columns: 16px auto;
grid-column-gap: 15px;
font-weight: 600;
margin-right: auto;
}
.tabsAside.pane > header > hr
.iconArrowRight
{
border: 1px solid #8a8a8a;
width: 16px;
height: 16px;
display: inline-block;
background-repeat: no-repeat;
background-size: 16px;
background-position: center;
background-image: url("chrome-extension://__MSG_@@extension_id__/icons/arrowRight.svg"),
url("moz-extension://__MSG_@@extension_id__/icons/arrowRight.svg");
margin: 2px;
}
.tabsAside.pane section
{
overflow: auto;
}
.tabsAside.pane > section > h2
{
margin: 0px 40px;
margin: 20px;
font-weight: normal;
}
@@ -116,11 +133,34 @@
transition: .2s;
}
.collectionSet
{
background-color: white;
margin: 10px;
border-radius: 5px;
border: 1px solid #eee;
}
.collectionSet:hover
{
box-shadow: 0px 0px 5px rgba(0, 0, 0, .25);
}
.collectionSet .header > *
{
visibility: hidden;
}
.collectionSet:hover .header > *
{
visibility: visible;
}
.collectionSet > .header
{
margin: 0px 20px;
margin: 10px 10px 0px 20px;
display: grid;
grid-template-columns: auto 1fr auto auto auto;
grid-template-columns: 1fr auto auto auto;
grid-column-gap: 10px;
align-items: center;
}
@@ -128,13 +168,23 @@
.collectionSet > .header > small
{
color: gray;
visibility: visible !important;
}
.collectionSet > .header > span
.collectionSet > .header > input
{
font-weight: 600;
margin: 0px;
visibility: visible !important;
font-weight: 500;
border: none;
background: transparent;
}
.collectionSet > .header > input:hover
{
border: 1px solid black;
}
.collectionSet > .header > a
{
font-size: 11pt;
@@ -149,8 +199,7 @@
/* Tabs collection */
.collectionSet > .set
{
margin: 0px 0px 0px 20px;
padding: 10px 40px 10px 20px;
padding: 5px 10px;
white-space: nowrap;
overflow: auto;
}
@@ -163,7 +212,7 @@
.collectionSet > .set:hover::-webkit-scrollbar-thumb
{
visibility: visible;
}
}
.collectionSet > .set > div
{
@@ -172,21 +221,23 @@
margin: 5px;
background-color: #c2c2c2;
background-image: url("chrome-extension://__MSG_@@extension_id__/images/tab_thumbnail.png");
background-image: url("chrome-extension://__MSG_@@extension_id__/images/tab_thumbnail.png"),
url("chrome-extension://__MSG_@@extension_id__/images/tab_thumbnail.png");
background-size: cover;
background-position-x: center;
display: inline-grid;
grid-template-rows: 1fr auto;
box-shadow: 0px 0px 5px rgba(100, 100, 100, .5);
transition: .25s;
cursor: pointer;
border: 1px solid #eee;
border-radius: 5px;
}
.collectionSet > .set > div:hover
{
filter: brightness(120%);
box-shadow: 0px 0px 15px rgba(100, 100, 100, .5);
box-shadow: 0px 0px 5px rgba(100, 100, 100, .5);
}
.collectionSet > .set > div > div
@@ -213,9 +264,10 @@
{
width: 20px;
height: 20px;
margin: 10px;
margin: 8px;
background-image: url("chrome-extension://__MSG_@@extension_id__/images/tab_icon.png");
background-image: url("chrome-extension://__MSG_@@extension_id__/images/tab_icon.png"),
url("moz-extension://__MSG_@@extension_id__/images/tab_icon.png");
background-size: 20px;
}
+38 -3
View File
@@ -4,11 +4,21 @@
}
.tabsAside[darkmode] .pane
{
{
background-color: #333333;
color: white;
}
.tabsAside[darkmode] .pane header .iconArrowRight
{
filter: invert();
}
.tabsAside[darkmode] .pane header
{
background-color: #3b3b3b;
}
.tabsAside[darkmode] .saveTabs > div
{
filter: invert();
@@ -28,6 +38,22 @@
background-color: dimgray;
}
.tabsAside[darkmode] .collectionSet > .header > input
{
color: white;
}
.tabsAside[darkmode] .collectionSet > .header > input:hover
{
border: 1px solid dimgray;
}
.tabsAside[darkmode] .collectionSet > .header > input:focus
{
background: white;
color: black;
}
.tabsAside[darkmode] a
{
color: #48adff;
@@ -50,11 +76,19 @@
background: dimgray;
}
.tabsAside[darkmode] .pane .collectionSet
{
background-color: #3b3b3b;
border-color: #444;
}
/* Tab style */
.tabsAside[darkmode] .pane .collectionSet > .set > div
{
background-color: #0c0c0c;
background-image: url("chrome-extension://__MSG_@@extension_id__/images/tab_thumbnail_dark.png");
background-image: url("chrome-extension://__MSG_@@extension_id__/images/tab_thumbnail_dark.png"),
url("moz-extension://__MSG_@@extension_id__/images/tab_thumbnail_dark.png");
border-color: #444;
}
.tabsAside[darkmode] .pane .collectionSet > .set > div > div
@@ -64,7 +98,8 @@
.tabsAside[darkmode] .pane .collectionSet > .set > div > div > div
{
background-image: url("chrome-extension://__MSG_@@extension_id__/images/tab_icon_dark.png");
background-image: url("chrome-extension://__MSG_@@extension_id__/images/tab_icon_dark.png"),
url("moz-extension://__MSG_@@extension_id__/images/tab_icon_dark.png");
}
/* Context menu style */
+36 -14
View File
@@ -17,7 +17,7 @@
.tabsAside
{
font-family: 'DefaultFont';
font-family: 'Segoe UI' ,'DefaultFont';
font-size: 14px;
user-select: none;
}
@@ -27,7 +27,7 @@
{
color: #0078d7;
}
.tabsAside a:hover
.tabsAside a:hover
{
text-decoration: underline;
cursor: pointer;
@@ -41,15 +41,15 @@
/* Buttons style */
.tabsAside button
{
width: 32px;
height: 32px;
width: 28px;
height: 28px;
background-color: transparent;
border: none;
cursor: pointer;
}
.tabsAside button:hover
{
background-color: #c6c6c6;
background-color: #f2f2f2;
}
.tabsAside button:active
{
@@ -62,27 +62,43 @@
user-select: none;
position: absolute;
width: 250px;
width: 290px;
box-shadow: 0px 0px 10px black;
box-shadow: 0px 0px 10px rgba(0,0,0,.5);
background-color: white;
border-radius: 5px;
z-index: 10;
visibility: hidden;
padding: 4px 0px;
}
.tabsAside nav button
{
.tabsAside nav hr
{
border: none;
height: 1px;
background-color: lightgray;
}
.tabsAside nav button
{
text-align: start;
padding: 0px 10px;
width: 100%;
height: 32px;
font-family: 'Segoe UI' ,'DefaultFont';
}
.tabsAside nav button:hover
{
background-color: #eeee;
}
.tabsAside button + nav:active,
.tabsAside button:focus + nav
.tabsAside button:focus + nav
{
visibility: visible;
}
@@ -91,18 +107,20 @@
.btn
{
background-repeat: no-repeat;
background-size: 15px;
background-size: 12px;
background-position: center;
}
.btn.more
{
background-image: url("chrome-extension://__MSG_@@extension_id__/icons/more.svg");
background-image: url("chrome-extension://__MSG_@@extension_id__/icons/more.svg"),
url("moz-extension://__MSG_@@extension_id__/icons/more.svg");
}
.btn.remove
{
background-image: url("chrome-extension://__MSG_@@extension_id__/icons/cancel.svg");
background-image: url("chrome-extension://__MSG_@@extension_id__/icons/cancel.svg"),
url("moz-extension://__MSG_@@extension_id__/icons/cancel.svg");
}
@font-face
@@ -111,5 +129,9 @@
src: local("Segoe UI"),
url("chrome-extension://__MSG_@@extension_id__/fonts/WeblySleekUI/weblysleekuisemilight.ttf") format("truetype"),
url("chrome-extension://__MSG_@@extension_id__/fonts/WeblySleekUI/weblysleekuisemilight.woff") format("woff"),
url("chrome-extension://__MSG_@@extension_id__/fonts/WeblySleekUI/weblysleekuisemilight.woff2") format("woff2");
url("chrome-extension://__MSG_@@extension_id__/fonts/WeblySleekUI/weblysleekuisemilight.woff2") format("woff2"),
url("moz-extension://__MSG_@@extension_id__/fonts/WeblySleekUI/weblysleekuisemilight.ttf") format("truetype"),
url("moz-extension://__MSG_@@extension_id__/fonts/WeblySleekUI/weblysleekuisemilight.woff") format("woff"),
url("moz-extension://__MSG_@@extension_id__/fonts/WeblySleekUI/weblysleekuisemilight.woff2") format("woff2");
}
+58
View File
@@ -0,0 +1,58 @@
.tabsAside[listview] .collectionSet > .header
{
margin-bottom: 5px;
}
.tabsAside[listview] .collectionSet > .set
{
max-height: 250px;
}
.tabsAside[listview] .collectionSet > .set > div
{
width: initial;
height: initial;
background-image: none !important;
display: block;
}
.listviewSwitch
{
width: 20px;
height: 20px;
display: grid;
grid-row-gap: 2px;
grid-column-gap: 2px;
cursor: pointer;
}
.listviewSwitch.tile
{
grid-template-columns: 1fr 1fr;
}
.listviewSwitch > div
{
border-radius: 1px;
background-color: #c2c2c2;
}
.listviewSwitch:hover > div
{
background-color: #a0a0aa;
}
.tabsAside .listviewSwitch.tile > div
{
background-color: gray;
}
.tabsAside[listview] .listviewSwitch.tile > div
{
background-color: #c2c2c2;
}
.tabsAside[listview] .listviewSwitch.list > div
{
background-color: gray;
}
+194 -100
View File
@@ -1,4 +1,4 @@
if (window.location === window.parent.location && window.location.protocol != "chrome-extension:") // For open/close call
if (window.location === window.parent.location && !window.location.protocol.includes("-extension:")) // For open/close call
{
var iframe = document.querySelector("iframe.tabsAsideIframe");
if (!iframe)
@@ -52,16 +52,18 @@ function Initialize()
if (window.location !== window.parent.location)
{
pane.setAttribute("embedded", "");
window.addEventListener('message', event =>
window.addEventListener('message', event =>
{
if (event.data.target == "TabsAside")
{
pane.parentElement.style.opacity = 0;
pane.removeAttribute("opened");
}
});
});
}
UpdateLocale();
if (window.matchMedia("(prefers-color-scheme: dark)").matches)
{
pane.parentElement.setAttribute("darkmode", "");
@@ -69,13 +71,20 @@ function Initialize()
}
document.querySelector(".tabsAside .saveTabs").onclick = SetTabsAside;
document.querySelector(".tabsAside header .btn.remove").addEventListener("click", () =>
chrome.runtime.sendMessage({ command: "togglePane" })
);
document.querySelector(".tabsAside.closeArea").addEventListener("click", () =>
chrome.runtime.sendMessage({ command: "togglePane" })
);
document.querySelector("nav > p > small").textContent = chrome.runtime.getManifest()["version"];
var loadOnRestoreCheckbox = document.querySelector("nav > p > input[type=checkbox]");
// Tabs dismiss option
var loadOnRestoreCheckbox = document.querySelector("#loadOnRestore");
chrome.storage.sync.get(
{ "loadOnRestore": false },
values => loadOnRestoreCheckbox.checked = values.loadOnRestore
{ "loadOnRestore": true },
values => loadOnRestoreCheckbox.checked = values?.loadOnRestore ?? true
);
chrome.storage.onChanged.addListener((changes, namespace) =>
{
@@ -91,19 +100,117 @@ function Initialize()
})
);
document.querySelectorAll(".tabsAside.pane > header nav button").forEach(i =>
i.onclick = () => window.open(i.value, '_blank'));
// Exntension browser icon action
var swapIconAction = document.querySelector("#swapIconAction");
chrome.storage.sync.get(
{ "setAsideOnClick": false },
values => swapIconAction.checked = values?.setAsideOnClick ?? false
);
chrome.storage.onChanged.addListener((changes, namespace) =>
{
if (namespace == 'sync')
for (key in changes)
if (key === 'setAsideOnClick')
swapIconAction.checked = changes[key].newValue
});
swapIconAction.addEventListener("click", () =>
chrome.storage.sync.set(
{
"setAsideOnClick": swapIconAction.checked
})
);
// Deletion confirmation dialog
var showDeleteDialog = document.querySelector("#showDeleteDialog");
chrome.storage.sync.get(
{ "showDeleteDialog": true },
values => showDeleteDialog.checked = values.showDeleteDialog
);
chrome.storage.onChanged.addListener((changes, namespace) =>
{
if (namespace == 'sync')
for (key in changes)
if (key === 'showDeleteDialog')
showDeleteDialog.checked = changes[key].newValue
});
showDeleteDialog.addEventListener("click", () =>
chrome.storage.sync.set(
{
"showDeleteDialog": showDeleteDialog.checked
})
);
// Collections view switch
chrome.storage.sync.get(
{ "listview": false },
values =>
{
if (values?.listview)
pane.setAttribute("listview", "");
}
);
document.querySelectorAll(".listviewSwitch").forEach(i =>
{
i.onclick = (args) =>
{
if (args.currentTarget.classList[1] == "list")
{
pane.setAttribute("listview", "");
chrome.storage.sync.set({ "listview": true });
}
else
{
pane.removeAttribute("listview");
chrome.storage.sync.set({ "listview": false });
}
}
});
chrome.storage.onChanged.addListener((changes, namespace) =>
{
if (namespace == 'sync')
for (key in changes)
if (key === 'listview')
if (changes[key].newValue)
pane.setAttribute("listview", "");
else
pane.removeAttribute("listview");
});
document.querySelectorAll(".tabsAside.pane > header nav button").forEach(i =>
i.onclick = () =>
{
if (i.hasAttribute("feedback-button"))
{
if (chrome.runtime.getManifest()["update_url"] && chrome.runtime.getManifest()["update_url"].includes("edge.microsoft.com"))
window.open("https://microsoftedge.microsoft.com/addons/detail/tabs-aside/kmnblllmalkiapkfknnlpobmjjdnlhnd", "_blank")
else
window.open("https://chrome.google.com/webstore/detail/tabs-aside/mgmjbodjgijnebfgohlnjkegdpbdjgin", "_blank")
}
else
window.open(i.value, "_blank");
});
chrome.runtime.sendMessage({ command: "loadData" }, (collections) =>
{
if (document.querySelector(".tabsAside.pane section div") == null)
collections.forEach(i =>
collections.forEach(i =>
AddCollection(i));
});
setTimeout(() => pane.setAttribute("opened", ""), 100);
}
function UpdateLocale()
{
document.querySelectorAll("*[loc]").forEach(i => i.textContent = chrome.i18n.getMessage(i.getAttribute("loc")));
document.querySelectorAll("*[loc_alt]").forEach(i => i.title = chrome.i18n.getMessage(i.getAttribute("loc_alt")));
var swapActionsLabel = document.querySelector("label[loc=swapIconAction]");
chrome.runtime.sendMessage({ command: "getShortcuts" }, (shortcuts) =>
swapActionsLabel.textContent = swapActionsLabel.textContent.replace("%TOGGLE_SHORTCUT%", shortcuts.filter(i => i.name == "toggle-pane")[0].shortcut)
);
}
function AddCollection(collection)
{
var list = document.querySelector(".tabsAside section");
@@ -114,12 +221,12 @@ function AddCollection(collection)
for (var i = 0; i < collection.links.length; i++)
{
rawTabs +=
"<div title='" + collection.titles[i] + "'" + ((collection.thumbnails && collection.thumbnails[i]) ? " style='background-image: url(" + collection.thumbnails[i] + ")'" : "") + ">" +
"<span class='openTab' value='" + collection.links[i] + "'></span>" +
"<div title='" + collection.titles[i] + "'" + ((collection.thumbnails && collection.thumbnails[i]) ? " style='background-image: url(" + collection.thumbnails[i] + ")'" : "") + " value='" + collection.links[i] + "'>" +
//"<span class='openTab' value='" + collection.links[i] + "'></span>" +
"<div>" +
"<div" + ((collection.icons[i] == 0 || collection.icons[i] == null) ? "" : " style='background-image: url(\"" + collection.icons[i] + "\")'") + "></div>" +
"<span>" + collection.titles[i] + "</span>" +
"<button class='btn remove' title='Remove tab from collection'></button>" +
"<button loc_alt='removeTab' class='btn remove' title='Remove tab from collection'></button>" +
"</div>" +
"</div>";
}
@@ -127,41 +234,50 @@ function AddCollection(collection)
list.innerHTML +=
"<div class='collectionSet'>" +
"<div class='header'>" +
"<span>Tabs: " + collection.links.length + "</span>" +
"<small>" + GetAgo(collection.timestamp) + "</small>" +
"<a class='restoreCollection'>Restore tabs</a>" +
"<input type='text' value='" + (collection.name ?? new Date(collection.timestamp).toDateString()) + "'/>" +
"<a loc='restoreTabs' class='restoreCollection'>Restore tabs</a>" +
"<div>" +
"<button class='btn more' title='More...'></button>" +
"<button loc_alt='more' class='btn more' title='More...'></button>" +
"<nav>" +
"<button class='restoreCollection noDelete'>Restore without removing</button>" +
"<button loc='restoreNoRemove' class='restoreCollection noDelete'>Restore without removing</button>" +
"</nav>" +
"</div>" +
"<button class='btn remove' title='Remove collection'></button>" +
"<button loc_alt='removeCollection' class='btn remove' title='Remove collection'></button>" +
"<small>" + collection.links.length + " " + chrome.i18n.getMessage("tabs") +"</small>" +
"</div>" +
"<div class='set' class='tabsList'>" + rawTabs + "</div>" +
"</div>"
"</div>";
list.querySelectorAll(".restoreCollection").forEach(i =>
UpdateLocale();
list.querySelectorAll("input").forEach(i =>
i.oninput = (event) => RenameCollection(i.parentElement.parentElement, event.target.value));
list.querySelectorAll(".restoreCollection").forEach(i =>
i.onclick = () => RestoreTabs(i.parentElement.parentElement));
list.querySelectorAll(".restoreCollection.noDelete").forEach(i =>
list.querySelectorAll(".restoreCollection.noDelete").forEach(i =>
i.onclick = () => RestoreTabs(i.parentElement.parentElement.parentElement.parentElement, false));
list.querySelectorAll(".openTab").forEach(i =>
i.onclick = () =>
chrome.runtime.sendMessage(
{
command: "openTab",
url: i.getAttribute("value")
}
));
list.querySelectorAll(".set > div").forEach(i =>
i.onclick = (args) =>
{
if (args.target.localName != "button")
chrome.runtime.sendMessage(
{
command: "openTab",
url: i.getAttribute("value")
}
);
});
document.querySelectorAll(".btn.remove").forEach(i =>
document.querySelectorAll(".header .btn.remove").forEach(i =>
i.onclick = () => RemoveTabs(i.parentElement.parentElement));
document.querySelectorAll(".tabsList .btn.remove").forEach(i =>
i.onclick = () => RemoveOneTab(i.parentElement.parentElement));
document.querySelectorAll(".set .btn.remove").forEach(i =>
i.onclick = (args) =>
RemoveOneTab(i.parentElement.parentElement));
}
function SetTabsAside()
@@ -169,6 +285,16 @@ function SetTabsAside()
chrome.runtime.sendMessage({ command: "saveTabs" });
}
function RenameCollection(collectionData, name)
{
chrome.runtime.sendMessage(
{
command: "renameCollection",
newName: name,
collectionIndex: Array.prototype.slice.call(collectionData.parentElement.children).indexOf(collectionData) - 1
});
}
function RestoreTabs(collectionData, removeCollection = true)
{
chrome.runtime.sendMessage(
@@ -187,79 +313,47 @@ function RestoreTabs(collectionData, removeCollection = true)
function RemoveTabs(collectionData)
{
if (!confirm("Are you sure you want to delete this collection?"))
return;
chrome.storage.sync.get({ "showDeleteDialog": true }, values =>
{
if (values.showDeleteDialog && !confirm(chrome.i18n.getMessage("removeCollectionConfirm")))
return;
chrome.runtime.sendMessage(
{
command: "deleteTabs",
collectionIndex: Array.prototype.slice.call(collectionData.parentElement.children).indexOf(collectionData) - 1
},
() => RemoveCollectionElement(collectionData)
);
chrome.runtime.sendMessage(
{
command: "deleteTabs",
collectionIndex: Array.prototype.slice.call(collectionData.parentElement.children).indexOf(collectionData) - 1
},
() => RemoveCollectionElement(collectionData)
);
});
}
function RemoveOneTab(tabData)
{
if (!confirm("Are you sure you want to delete this tab?"))
return;
chrome.storage.sync.get({ "showDeleteDialog": true }, values =>
{
if (values.showDeleteDialog && !confirm(chrome.i18n.getMessage("removeTabConfirm")))
return;
chrome.runtime.sendMessage(
{
command: "removeTab",
collectionIndex: Array.prototype.slice.call(tabData.parentElement.parentElement.parentElement.children).indexOf(tabData.parentElement.parentElement) - 1,
tabIndex: Array.prototype.slice.call(tabData.parentElement.children).indexOf(tabData)
},
() =>
{
tabData.parentElement.previousElementSibling.children[0].textContent = "Tabs: " + (tabData.parentElement.children.length - 1);
if (tabData.parentElement.children.length < 2)
chrome.runtime.sendMessage(
{
RemoveElement(tabData.parentElement.parentElement);
if (document.querySelector("tabsAside.pane > section").children.length < 2)
setTimeout(() => document.querySelector(".tabsAside.pane > section > h2").removeAttribute("hidden"), 250);
}
else
RemoveElement(tabData);
});
}
function GetAgo(timestamp)
{
var minutes = (Date.now() - timestamp) / 60000;
if (minutes < 1)
return "Just now";
else if (Math.floor(minutes) == 1)
return "1 minute ago";
else if (minutes < 60)
return Math.floor(minutes) + " minutes ago";
else if (Math.floor(minutes / 60) == 1)
return "1 hour ago";
else if (minutes < 24 * 60)
return Math.floor(minutes / 60) + " hours ago";
else if (Math.floor(minutes / 24 / 60) == 1)
return "1 day ago";
else if (minutes < 7 * 24 * 60)
return Math.floor(minutes / 24 / 60) + " days ago";
else if (Math.floor(minutes / 7 / 24 / 60) == 1)
return "1 week ago";
else if (minutes < 30 * 24 * 60)
return Math.floor(minutes / 7 / 24 / 60) + " weeks ago";
else if (Math.floor(minutes / 30 / 24 / 60) == 1)
return "1 month ago";
else if (minutes < 365 * 24 * 60)
return Math.floor(minutes / 30 / 24 / 60) + " months ago";
else if (Math.floor(minutes / 24 / 60) == 365)
return "1 year ago";
else
return Math.floor(minutes / 365 / 24 / 60) + "years ago";
command: "removeTab",
collectionIndex: Array.prototype.slice.call(tabData.parentElement.parentElement.parentElement.children).indexOf(tabData.parentElement.parentElement) - 1,
tabIndex: Array.prototype.slice.call(tabData.parentElement.children).indexOf(tabData)
},
() =>
{
tabData.parentElement.previousElementSibling.querySelector("small").textContent = (tabData.parentElement.children.length - 1) + " " + chrome.i18n.getMessage("tabs");
if (tabData.parentElement.children.length < 2)
{
RemoveElement(tabData.parentElement.parentElement);
if (document.querySelector("tabsAside.pane > section").children.length < 2)
setTimeout(() => document.querySelector(".tabsAside.pane > section > h2").removeAttribute("hidden"), 250);
}
else
RemoveElement(tabData);
});
});
}
function RemoveElement(el)
@@ -270,7 +364,7 @@ function RemoveElement(el)
function RemoveCollectionElement(el)
{
if (el.parentElement.children.length < 3)
setTimeout(() => document.querySelector(".tabsAside.pane > section > h2").removeAttribute("hidden"), 250);
RemoveElement(el);
if (el.parentElement.children.length < 2)
setTimeout(() => document.querySelector(".tabsAside.pane > section > h2").removeAttribute("hidden"), 250);
}
+150 -43
View File
@@ -1,7 +1,26 @@
chrome.browserAction.onClicked.addListener((tab) =>
//This variable is populated when the browser action icon is clicked, or a command is called (with a shortcut for example).
//We can't populate it later, as selected tabs get deselected on a click inside a tab.
var tabsToSave = [];
//Get the tabs to save, either all the window or the selected tabs only, and pass them through a callback.
function GetTabsToSave(callback)
{
chrome.tabs.query({ currentWindow: true }, (windowTabs) =>
{
var highlightedTabs = windowTabs.filter(item => item.highlighted);
//If there are more than one selected tab in the window, we set only those aside.
// Otherwise, all the window's tabs get saved.
return callback((highlightedTabs.length > 1 ? highlightedTabs : windowTabs));
});
}
function TogglePane(tab)
{
if (tab.url.startsWith("http")
&& !tab.url.includes("chrome.google.com")
&& !tab.url.includes("addons.mozilla.org")
&& !tab.url.includes("microsoftedge.microsoft.com"))
{
chrome.tabs.executeScript(tab.id,
@@ -21,14 +40,14 @@ chrome.browserAction.onClicked.addListener((tab) =>
active: true
},
(activeTab) =>
chrome.tabs.onActivated.addListener(function TabsAsideCloser(activeInfo)
chrome.tabs.onActivated.addListener(function TabsAsideCloser(activeInfo)
{
chrome.tabs.query({ url: chrome.extension.getURL("TabsAside.html") }, (result) =>
{
if (result.length)
setTimeout(() =>
{
result.forEach(i =>
result.forEach(i =>
{
if (activeInfo.tabId != i.id)
chrome.tabs.remove(i.id);
@@ -38,10 +57,75 @@ chrome.browserAction.onClicked.addListener((tab) =>
});
}));
}
}
function ProcessCommand(command)
{
GetTabsToSave((returnedTabs) =>
{
tabsToSave = returnedTabs;
switch(command)
{
case "set-aside":
SaveCollection();
break;
case "toggle-pane":
chrome.tabs.query(
{
active: true,
currentWindow: true
},
(tabs) => TogglePane(tabs[0])
)
break;
}
});
}
chrome.browserAction.onClicked.addListener((tab) =>
{
GetTabsToSave((returnedTabs) =>
{
tabsToSave = returnedTabs;
chrome.storage.sync.get({ "setAsideOnClick": false }, values =>
{
if (values?.setAsideOnClick)
SaveCollection();
else
TogglePane(tab);
});
});
});
var collections = JSON.parse(localStorage.getItem("sets")) || [];
var shortcuts;
chrome.commands.getAll((commands) => shortcuts = commands);
chrome.commands.onCommand.addListener(ProcessCommand);
chrome.contextMenus.onClicked.addListener((info) => ProcessCommand(info.menuItemId));
chrome.runtime.onInstalled.addListener((reason) =>
{
chrome.tabs.create({ url: "https://github.com/XFox111/TabsAsideExtension/releases/latest" });
// Adding context menu options
chrome.contextMenus.create(
{
id: "toggle-pane",
contexts: ["browser_action"],
title: chrome.i18n.getMessage("togglePaneContext")
}
);
chrome.contextMenus.create(
{
id: "set-aside",
contexts: ["browser_action"],
title: chrome.i18n.getMessage("setAside")
}
);
});
//We receive a message from the pane aside-script, which means the tabsToSave are already assigned on message reception.
chrome.runtime.onMessage.addListener((message, sender, sendResponse) =>
{
switch (message.command)
@@ -67,12 +151,34 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) =>
RemoveTab(message.collectionIndex, message.tabIndex);
sendResponse();
break;
case "renameCollection":
collections[message.collectionIndex].name = message.newName;
localStorage.setItem("sets", JSON.stringify(collections));
break;
case "togglePane":
chrome.tabs.query(
{
active: true,
currentWindow: true
},
(tabs) => TogglePane(tabs[0])
)
break;
case "getShortcuts":
sendResponse(shortcuts);
break;
}
});
// This function updates the extension's toolbar icon
function UpdateTheme()
{
// Updating badge counter
chrome.browserAction.setBadgeText({ text: collections.length < 1 ? "" : collections.length.toString() });
if (chrome.theme) // Firefox sets theme automatically
return;
var theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
var iconStatus = collections.length ? "full" : "empty";
@@ -98,48 +204,45 @@ chrome.tabs.onActivated.addListener(UpdateTheme);
// Set current tabs aside
function SaveCollection()
{
chrome.tabs.query({ currentWindow: true }, (rawTabs) =>
var tabs = tabsToSave.filter(i => i.url != chrome.runtime.getURL("TabsAside.html") && !i.pinned && !i.url.includes("//newtab") && !i.url.includes("about:blank") && !i.url.includes("about:home"));
if (tabs.length < 1)
{
var tabs = rawTabs.filter(i => i.url != chrome.runtime.getURL("TabsAside.html") && !i.pinned && !i.url.includes("//newtab"));
alert(chrome.i18n.getMessage("noTabsToSave"));
return;
}
if (tabs.length < 1)
{
alert("No tabs available to save");
return;
}
var collection =
{
timestamp: Date.now(),
tabsCount: tabs.length,
titles: tabs.map(tab => tab.title ?? ""),
links: tabs.map(tab => tab.url ?? ""),
icons: tabs.map(tab => tab.favIconUrl ?? ""),
thumbnails: tabs.map(tab => thumbnails.find(i => i.tabId == tab.id)?.url ?? "")
};
var collection =
{
timestamp: Date.now(),
tabsCount: tabs.length,
titles: tabs.map(tab => tab.title ?? ""),
links: tabs.map(tab => tab.url ?? ""),
icons: tabs.map(tab => tab.favIconUrl ?? ""),
thumbnails: tabs.map(tab => thumbnails.find(i => i.tabId == tab.id)?.url ?? "")
};
var rawData;
if (localStorage.getItem("sets") === null)
rawData = [collection];
else
{
rawData = JSON.parse(localStorage.getItem("sets"));
rawData.unshift(collection);
}
var rawData;
if (localStorage.getItem("sets") === null)
rawData = [collection];
else
{
rawData = JSON.parse(localStorage.getItem("sets"));
rawData.unshift(collection);
}
localStorage.setItem("sets", JSON.stringify(rawData));
localStorage.setItem("sets", JSON.stringify(rawData));
collections = JSON.parse(localStorage.getItem("sets"));
collections = JSON.parse(localStorage.getItem("sets"));
var newTabId;
chrome.tabs.create({}, (tab) =>
{
newTabId = tab.id;
chrome.tabs.remove(rawTabs.filter(i => !i.pinned && i.id != newTabId).map(tab => tab.id));
});
UpdateTheme();
var newTabId;
chrome.tabs.create({}, (tab) =>
{
newTabId = tab.id;
chrome.tabs.remove(tabsToSave.filter(i => !i.pinned && i.id != newTabId).map(tab => tab.id));
});
UpdateTheme();
}
function DeleteCollection(collectionIndex)
@@ -152,7 +255,7 @@ function DeleteCollection(collectionIndex)
function RestoreCollection(collectionIndex, removeCollection)
{
collections[collectionIndex].links.forEach(i =>
collections[collectionIndex].links.forEach(i =>
{
chrome.tabs.create(
{
@@ -161,10 +264,10 @@ function RestoreCollection(collectionIndex, removeCollection)
},
(createdTab) =>
{
chrome.storage.sync.get({ "loadOnRestore" : false }, values =>
chrome.storage.sync.get({ "loadOnRestore" : true }, values =>
{
if (!values.loadOnRestore)
chrome.tabs.onUpdated.addListener(function DiscardTab(updatedTabId, changeInfo, updatedTab)
if (!(values?.loadOnRestore))
chrome.tabs.onUpdated.addListener(function DiscardTab(updatedTabId, changeInfo, updatedTab)
{
if (updatedTabId === createdTab.id) {
chrome.tabs.onUpdated.removeListener(DiscardTab);
@@ -177,6 +280,10 @@ function RestoreCollection(collectionIndex, removeCollection)
});
});
//We added new tabs by restoring a collection, so we refresh the array of tabs ready to be saved.
GetTabsToSave((returnedTabs) =>
tabsToSave = returnedTabs)
if (!removeCollection)
return;
@@ -260,4 +367,4 @@ chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) =>
{
if (changeInfo.status === "complete")
AppendThumbnail(tabId, tab)
});
});
+69 -10
View File
@@ -1,30 +1,89 @@
{
"name": "Tabs Aside",
"version": "1.5",
"name": "__MSG_name__",
"version": "1.9",
"manifest_version": 2,
"description": "Classic Microsoft Edge \"Tabs Aside\" feature for Chromium browsers",
"author": "Michael \"XFox\" Gordeev",
"permissions":
"description": "__MSG_description__",
"author": "__MSG_author__",
"default_locale": "en",
"permissions":
[
"tabs",
"unlimitedStorage",
"storage",
"<all_urls>"
"<all_urls>",
"contextMenus"
],
"icons":
"icons":
{
"128": "icons/light/empty/128.png",
"48": "icons/light/empty/48.png",
"32": "icons/light/empty/32.png",
"16": "icons/light/empty/16.png"
},
"browser_action": { "default_icon": "icons/light/empty/32.png" },
"browser_action":
{
"default_icon": "icons/light/empty/32.png",
"theme_icons":
[
{
"size": 128,
"dark": "icons/light/full/128.png",
"light": "icons/dark/full/128.png"
},
{
"size": 48,
"dark": "icons/light/full/48.png",
"light": "icons/dark/full/48.png"
},
{
"size": 32,
"dark": "icons/light/full/32.png",
"light": "icons/dark/full/32.png"
},
{
"size": 16,
"dark": "icons/light/full/16.png",
"light": "icons/dark/full/16.png"
}
]
},
"web_accessible_resources": [ "*" ],
"background":
{
"scripts": [ "js/background.js" ],
"persistent": false
},
"commands":
{
"set-aside":
{
"description": "__MSG_setAside__",
"suggested_key":
{
"default": "Alt+Left",
"mac": "MacCtrl+T"
}
},
"toggle-pane":
{
"description": "__MSG_togglePaneContext__",
"suggested_key":
{
"default": "Alt+P",
"mac": "Command+Shift+P"
}
}
},
"browser_specific_settings":
{
"gecko":
{
"id": "tabsaside@xfox111.net",
"strict_min_version": "58.0"
}
}
}
}