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

Compare commits

...

17 Commits

Author SHA1 Message Date
xfox111 fa43a95a40 Patch 2.0.3
- [Firefox] Fixed checkmark icon in settings
2021-04-23 17:54:32 +03:00
xfox111 e8f7c55e2f Added .gitignore 2021-04-23 17:12:16 +03:00
whitesource-bolt-for-github[bot] 3d7bc4eb77 Add .whitesource configuration file (#76) 2021-03-22 15:30:44 +03:00
Michael Gordeev 847baae44e Patch 2.0.2 (#71)
- Fixed missing fonts
2021-03-05 17:39:41 +03:00
Michael Gordeev aa1d38eb47 Patch 2.0.1 (#70)
- Fixed inversed list in the panel
2021-03-04 00:20:57 +03:00
Michael Gordeev 8da01f34e1 Major 2.0 (#51)
- Added cloud storage for sets
- WeblySleek UI font replaced with Segoe fonts
- Reworked icons (#55)
- Removed changelog auto-opening on install
- 'Contributors' option replaced with 'Changelog' one
- Added icons to menu items
- Updated UI with Fluent UI compliance
- Added tooltip on how to use selective activation (#57)
- Added Italian language (#52) by @blackcat-917
- Added Brazilian Portuguese language (#53) by @amandafilizola
- Updated Firefox CI/CD config
- Updated README: rearranged badges and added demo GIF
- Updated documentation

Co-authored-by: Michael Gordeev <michael@xfox111.net>
Co-authored-by: blackcat-917 <53786619+blackcat-917@users.noreply.github.com>
Co-authored-by: Amanda de Paiva Filizola <amandapaivafilizola@gmail.com>
Co-authored-by: Amine A <15179425+AmineI@users.noreply.github.com>
Co-authored-by: Dustin Jiang <56217843+Dustin-Jiang@users.noreply.github.com>
2021-03-03 17:06:30 +03:00
Michael Gordeev 4559b25739 Removed changelog opening on extension update (#50)
Updated Firefox CD config
2020-09-22 20:41:46 +00:00
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
60 changed files with 1913 additions and 570 deletions
+13
View File
@@ -0,0 +1,13 @@
## What's new
## How to install
1. Download attached archive and unpack it
2. Enable Developers mode on your browser extensions page
3. Click "Load unpacked" button and navigate to the extension root folder (contains `manifest.json`)
4. Done!
*On Firefox you should open manifest file instead of extension's folder
**Note:** If you delete extension folder it will disappear from your browser
_Sideloaded extensions don't replace officially installed ones_
+70
View File
@@ -0,0 +1,70 @@
name: CI
on:
release:
types: [published]
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 & publish'
id: web-ext-sign
uses: kewisch/action-web-ext@v1
with:
cmd: sign
channel: listed
source: ${{ steps.web-ext-build.outputs.target }}
apiKey: ${{ secrets.FIREFOX_API_KEY }}
apiSecret: ${{ secrets.FIREFOX_CLIENT_SECRET }}
- name: Drop artifacts
uses: actions/upload-artifact@v2
with:
name: 'Firefox Artefacts'
path: ${{ steps.web-ext-build.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: Upload artifact
uses: xresloader/upload-to-github-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
file: ./TabsAside.zip
tags: true
draft: false
- name: Drop artifacts
uses: actions/upload-artifact@v2
with:
name: 'Chrome Artifacts'
path: ./TabsAside.zip
+1
View File
@@ -0,0 +1 @@
\.vscode
+13
View File
@@ -0,0 +1,13 @@
{
"version": "0.2.0",
"configurations":
[
{
"name": "Debug",
"type": "firefox",
"request": "launch",
"reAttach": true,
"addonPath": "${workspaceFolder}"
}
]
}
+12
View File
@@ -0,0 +1,12 @@
{
"scanSettings": {
"baseBranches": []
},
"checkRunSettings": {
"vulnerableCheckRunConclusionLevel": "failure",
"displayMode": "diff"
},
"issueSettings": {
"minSeverityLevel": "LOW"
}
}
+12 -12
View File
@@ -14,21 +14,21 @@ appearance, race, religion, or sexual identity and orientation.
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
- The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
@@ -68,9 +68,9 @@ members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
available at <https://www.contributor-covenant.org/version/1/4/code-of-conduct.html>
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
<https://www.contributor-covenant.org/faq>
+37 -27
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)
@@ -13,7 +13,7 @@ There are many ways in which you can contribute, beyond writing code. The goal o
- [Writing Good Bug Reports and Feature Requests](#writing-good-bug-reports-and-feature-requests)
- [Final Checklist](#final-checklist)
- [Follow Your Issue](#follow-your-issue)
- [Contributing to codebase](#contributing-to-codebase)
- [Contributing to the codebase](#contributing-to-the-codebase)
- [Deploy test version on your browser](#deploy-test-version-on-your-browser)
- [Development workflow](#development-workflow)
- [Release](#release)
@@ -35,20 +35,20 @@ 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:
* 👍 - upvote
* 👎 - downvote
- 👍 - upvote
- 👎 - downvote
If you cannot find an existing issue that describes your bug or feature, create a new issue using the guidelines below.
@@ -68,6 +68,7 @@ Please include the following with each issue:
### Final Checklist
Please remember to do the following:
- [ ] Search the issue repository to ensure your report is a new issue
- [ ] Separate issues reports
- [ ] Include as much information as you can to your report
@@ -77,31 +78,35 @@ Don't feel bad if the developers can't reproduce the issue right away. They will
### Follow Your Issue
Once your report is submitted, be sure to stay in touch with the devs in case they need more help from you.
## Contributing to codebase
If you are interested in writing code to fix issues or implement new awesome features you can follow this guidelines to get a better result
## Contributing to the codebase
If you are interested in writing code to fix issues or implement new awesome features you can follow these guidelines to get a better result
### 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
```bash
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`)
4. Done!
Next time you make any changes to the codebase, reload extension by toggling it off and on or by pressing "Reload" button on extensions list page
Next time you make any changes to the codebase, reload the extension by toggling it off and on or by pressing "Reload" button on the extensions list page
> **Note:** You can also check [this article](https://xfox111.net/46hsgv) to get more information about debugging web extensions
### Development workflow
This section represents how contributors should interact with codebase implementing features and fixing bugs
1. Getting assigned to the issue
2. Creating a repository fork
3. Making changes to codebase
5. Creating a pull request to `master`
6. Reviewing & completing PR
7. Done
3. Making changes to the codebase
4. Creating a pull request to `master`
5. Reviewing & completing PR
6. Done
#### Release
Next stage is release. Release performs on every push to master (which makes functional changes to the source code). Release performs manually by @XFox111 into: Chrome webstore, Edge webstore and GitHub releases
The next stage is the release. Release performs on every push to master (which makes functional changes to the source code). Release performs manually by @XFox111 into: Chrome webstore, Edge webstore and GitHub releases
### Coding guidelines
#### Indentation
@@ -109,6 +114,7 @@ We use tabs, not spaces.
#### Names
The project naming rules inherit [.NET Naming Guidelines](https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/naming-guidelines). Nevertheless there'is some distinction with the guidelines as well as additions to the one:
- Use `camelCase` for variables instead of `CamelCase` stated in [Capitalization Conventions](https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/capitalization-conventions#capitalization-rules-for-identifiers)
- Use `snake_case` for file names
@@ -122,13 +128,16 @@ Use "double quotes" wherever it's possible
- Prefer to use lambda functions
- Put curly braces on new lines
- Wrong:
```
```javascript
if (condition) {
...
}
```
- Correct:
```
```javascript
if (condition)
{
...
@@ -143,20 +152,21 @@ Use "double quotes" wherever it's possible
- `function FunctionName ()`
- Use ternary conditionals wherever it's possible
- Wrong:
```
```javascript
var s;
if (condition)
s = "Life";
else
s = "Death"
s = "Death";
```
- Correct:
```
```javascript
var s = condition ? "Life" : "Death";
```
- Do not surround loop and conditional bodies with curly braces if they can be avoided
- Wrong:
```
```javascript
if (condition)
{
console.log("Hello, World!");
@@ -167,7 +177,8 @@ Use "double quotes" wherever it's possible
}
```
- Correct
```
```javascript
if (condition)
console.log("Hello, World!");
else
@@ -175,7 +186,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).
@@ -197,8 +208,7 @@ To enable us to quickly review and accept your pull requests, always create one
Pull requests that fix typos are welcomed but please make sure it doesn't touch multiple feature areas, otherwise it will be difficult to review. Pull requests only fixing spell check errors in source code are not recommended.
## Thank You!
Your contributions to open source, large or small, make great projects like this possible. Thank you for taking the time to contribute.
## Attribution
This Contribution Guidelines are adapted from the [Contributing to VS Code](https://github.com/microsoft/vscode/blob/master/CONTRIBUTING.md)
These Contribution Guidelines are adapted from the [Contributing to VS Code](https://github.com/microsoft/vscode/blob/master/CONTRIBUTING.md)
+33 -23
View File
@@ -1,58 +1,68 @@
# 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)
[![Mozilla Add-on](https://img.shields.io/amo/rating/ms-edge-tabs-aside?label=Firefox%20rating)](https://addons.mozilla.org/firefox/addon/ms-edge-tabs-aside/)
[![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/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)
[![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)
[![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/)
[![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)
[![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
![Tabs aside demo](https://xfox111.net/knrp7b)
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
- Sync your saved tabs between different PCs
- **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
> ©2021 Michael "XFox" Gordeev
Font copyrights: Microsoft Corportation ©2021 (Additional ELUA applied)
Licensed under [MIT License](https://opensource.org/licenses/MIT)
+58 -18
View File
@@ -8,40 +8,80 @@
<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 loc="name">Tabs aside</h1>
<button loc_alt="options" class="btn more" title="Options"></button>
<button loc_alt="options" class="btn more" title="Options">&#xE712;<span class="updateBadge"></span></button>
<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>
<label class="checkbox control">
<input id="loadOnRestore" type="checkbox" />
<span class="mark"></span>
<span loc="loadOnRestore">Load tabs on restore</span>
</label>
<label class="checkbox control">
<input id="swapIconAction" type="checkbox" />
<span class="mark"></span>
<span loc="swapIconAction">Set tabs aside on extension icon click (Alt+P or right-click to open the pane)</span>
</label>
<label class="checkbox control">
<input id="showDeleteDialog" type="checkbox" />
<span class="mark"></span>
<span loc="showDeleteDialog">Show confirmation dialog before deleting an item</span>
</label>
<hr />
<div>
<button loc="github" value="https://github.com/xfox111/ChromiumTabsAside">Visit GitHub page</button>
<button loc="contributors" value="https://github.com/XFox111/ChromiumTabsAside/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>
<button value="https://github.com/xfox111/TabsAsideExtension">
<img src="icons/github.svg"/>
<span loc="github">Visit GitHub page</span>
</button>
<button value="https://github.com/XFox111/TabsAsideExtension/releases/latest">
<img src="icons/list.svg"/>
<span loc="changelog">Changelog</span>
<span class="updateBadge"></span>
</button>
<button feedback-button>
<img src="icons/feedback.svg"/>
<span loc="feedback">Leave feedback</span>
</button>
<button value="https://buymeacoffee.com/xfox111">
<img style="filter: none !important;" src="icons/bmc.svg"/>
<span loc="buyMeACoffee">Buy me a coffee!</span>
</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>)
<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">&#xE711;</button>
<a class="saveTabs">
<span class="iconArrowRight">&#xE72A;</span>
<span loc="setAside">Set current tabs aside</span>
<span loc_alt="setMultipleTabsAsideTooltip" class="iconQuestionCircle" title="Tip : You can set aside specific tabs by selecting them (by holding CTRL or SHIFT and clicking on the tabs) before clicking on the TabsAside extension">&#xE9CE;</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> <span loc="setAside">Set current tabs aside</span></a>
<hr />
</header>
<section>
+31 -46
View File
@@ -6,7 +6,7 @@
},
"description":
{
"message": "Classic Microsoft Edge \"Tabs Aside\" feature for Chromium browsers",
"message": "Classic Microsoft Edge \"Tabs Aside\" feature",
"description": "Extension description"
},
"author":
@@ -19,14 +19,24 @@
"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 (Alt+P or right-click to open the pane)",
"message": "Set tabs aside on extension icon click (%TOGGLE_SHORTCUT% or right-click to open the pane)",
"description": "Label for option"
},
"github":
@@ -34,9 +44,9 @@
"message": "Visit GitHub page",
"description": "Link title"
},
"contributors":
"changelog":
{
"message": "Project contributors",
"message": "Changelog",
"description": "Link title"
},
"feedback":
@@ -59,6 +69,11 @@
"message": "Set current tabs aside",
"description": "Save collection action name. Used in the pane, extension context menu and manifest shortcuts"
},
"setMultipleTabsAsideTooltip":
{
"message": "Tip : You can set aside specific tabs by selecting them (by holding CTRL or SHIFT and clicking on the tabs) before clicking on the TabsAside extension",
"description": "Tooltip displayed on hover in the pane to explain how to set specific tabs aside"
},
"nothingSaved":
{
"message": "You have no aside tabs",
@@ -109,49 +124,19 @@
"message": "No tabs available to save",
"description": "Alert dialog message when there's no tabs to save"
},
"errorSavingTabs":
{
"message": "Tabs could not be set aside. You may be trying to set too many tabs aside at once, or have too many tabs already set aside.",
"description": "Alert dialog message when there is an issue saving tabs"
},
"olderDataMigrationFailed":
{
"message": "Some tabs set aside from a previous version could not be migrated. They have been backed up as browser bookmarks.",
"description": "Alert dialog message when there is an issue migrating previous versions data"
},
"tabs":
{
"message": "Tabs",
"description": "Collection tabs counter label"
},
"ago":
{
"message": "ago",
"description": "Human friendly timestamp part (e.g. 15 hour(s) ago)"
},
"minutes":
{
"message": "minute(s)",
"description": "Human friendly timestamp part (e.g. 15 minute(s) ago)"
},
"hours":
{
"message": "hour(s)",
"description": "Human friendly timestamp part (e.g. 15 hour(s) ago)"
},
"days":
{
"message": "day(s)",
"description": "Human friendly timestamp part (e.g. 15 day(s) ago)"
},
"weeks":
{
"message": "week(s)",
"description": "Human friendly timestamp part (e.g. 15 week(s) ago)"
},
"months":
{
"message": "month(s)",
"description": "Human friendly timestamp part (e.g. 15 month(s) ago)"
},
"years":
{
"message": "years(s)",
"description": "Human friendly timestamp part (e.g. 15 years(s) ago)"
},
"justNow":
{
"message": "Just now",
"description": "Human friendly timestamp part"
"message": "items",
"description": "Collection tabs counter label (e.g. 8 items)"
}
}
+142
View File
@@ -0,0 +1,142 @@
{
"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"
},
"changelog":
{
"message": "Registro de cambios",
"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"
},
"setMultipleTabsAsideTooltip":
{
"message": "Consejo: puede dejar de lado pestañas específicas seleccionándolas (manteniendo presionada la tecla CTRL o SHIFT y haciendo clic en las pestañas) antes de hacer clic en la extensión TabsAside",
"description": "Tooltip displayed on hover in the pane to explain how to set specific tabs aside"
},
"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"
},
"errorSavingTabs":
{
"message": "Las pestañas no se pueden dejar de lado. Es posible que esté intentando dejar demasiadas pestañas a un lado a la vez o que ya haya reservado demasiadas pestañas.",
"description": "Alert dialog message when there is an issue saving tabs"
},
"olderDataMigrationFailed":
{
"message": "Algunas pestañas apartadas de una versión anterior no se pudieron migrar. Se han respaldado como marcadores del navegador.",
"description": "Alert dialog message when there is an issue migrating previous versions data"
},
"tabs":
{
"message": "items",
"description": "Collection tabs counter label (e.g. 8 items)"
}
}
+142
View File
@@ -0,0 +1,142 @@
{
"name":
{
"message": "Schede a parte",
"description": "name of the extension. Displayed in the manifest and pane header"
},
"description":
{
"message": "La funzione del classico Microsoft Edge \"Tabs Aside\"",
"description": "Extension description"
},
"author":
{
"message": "Michael \"XFox\" Gordeev",
"description": "Author name"
},
"options":
{
"message": "Opzioni",
"description": "Alternative text for options button in the pane"
},
"closePanel":
{
"message": "Chiudi",
"description": "Alternative text for close panel button"
},
"loadOnRestore":
{
"message": "Riapri le schede al ripristino",
"description": "Label for option"
},
"showDeleteDialog":
{
"message": "Mostra una finestra di conferma prima di eliminare qualcosa",
"description": "Label for option"
},
"swapIconAction":
{
"message": "Metti da parte le schede in Tabs Aside al click sull'estensione (%TOGGLE_SHORTCUT% o click con il tasto destro sull'icona dell'estensione per aprire il pannello)",
"description": "Label for option"
},
"github":
{
"message": "Visita la pagina di GitHub",
"description": "Link title"
},
"changelog":
{
"message": "Registro dei cambiamenti",
"description": "Link title"
},
"feedback":
{
"message": "Lascia un feedback",
"description": "Link title"
},
"buyMeACoffee":
{
"message": "Comprami un caffè!",
"description": "Link title"
},
"credits":
{
"message": "Sviluppato da Michael 'XFox' Gordeev",
"description": "Options menu credits"
},
"setAside":
{
"message": "Sposta le schede correnti in Tabs Aside",
"description": "Save collection action name. Used in the pane, extension context menu and manifest shortcuts"
},
"setMultipleTabsAsideTooltip":
{
"message": "Suggerimento: puoi mettere da parte schede specifiche selezionandole (tenendo premuto CTRL o MAIUSC e facendo clic sulle schede) e cliccando sull'estensione Tabs Aside.",
"description": "Tooltip displayed on hover in the pane to explain how to set specific tabs aside"
},
"nothingSaved":
{
"message": "Non hai schede da parte qua",
"description": "Placeholder for empty pane"
},
"removeTab":
{
"message": "Rimuovi la scheda da questo gruppo di schede",
"description": "Button hint on a tab card"
},
"restoreTabs":
{
"message": "Ripristina schede",
"description": "Collection restore action link name"
},
"more":
{
"message": "Altro...",
"description": "Collections' more button title"
},
"restoreNoRemove":
{
"message": "Ripristina senza rimuovere le schede da da Tabs Aside",
"description": "Context action item name"
},
"removeCollection":
{
"message": "Rimuovi gruppo di schede",
"description": "Collection remove action name"
},
"removeCollectionConfirm":
{
"message": "Sei sicuro di voler eliminare questo gruppo di schede?",
"description": "Prompt dialog content on collection deletion"
},
"removeTabConfirm":
{
"message": "Sei sucuro di voler rimuovere questa scheda?",
"description": "Prompt dialog content on one tab deletion"
},
"togglePaneContext":
{
"message": "Mostra/nascondi il pannello di Tabs Aside",
"description": "Context action name. Used in extension context menu and manifest shortcuts"
},
"noTabsToSave":
{
"message": "Nessuna scheda da salvare",
"description": "Alert dialog message when there's no tabs to save"
},
"errorSavingTabs":
{
"message": "Queste schede non possono essere messe da parte. Forse perché stai cercando di mettere da parte troppe schede o perché ce ne sono già troppe messe da parte.",
"description": "Alert dialog message when there is an issue saving tabs"
},
"olderDataMigrationFailed":
{
"message": "Alcune schede messe da parte in una versione precedente non potevano essere mantenute. Sono state salvate come preferiti del browser.",
"description": "Alert dialog message when there is an issue migrating previous versions data"
},
"tabs":
{
"message": "schede",
"description": "Collection tabs counter label (e.g. 8 items)"
}
}
+142
View File
@@ -0,0 +1,142 @@
{
"name":
{
"message": "Reservar abas",
"description": "Extension name. Displayed in the manifest and pane header"
},
"description":
{
"message": "Função \"Reservar abas\" do Microsoft Edge Clássico",
"description": "Extension description"
},
"author":
{
"message": "Michael \"XFox\" Gordeev",
"description": "Author name"
},
"options":
{
"message": "Opções",
"description": "Alternative text for options button in the pane"
},
"closePanel":
{
"message": "Fechar",
"description": "Alternative text for close panel button"
},
"loadOnRestore":
{
"message": "Carregar abas ao restaurar",
"description": "Label for option"
},
"showDeleteDialog":
{
"message": "Mostrar diálogo de confirmação antes de deletar um item",
"description": "Label for option"
},
"swapIconAction":
{
"message": "Reservar abas ao apertar o ícone da extensão (%TOGGLE_SHORTCUT% ou clique-direito para abrir o painel)",
"description": "Label for option"
},
"github":
{
"message": "Visite a página do GitHub",
"description": "Link title"
},
"changelog":
{
"message": "Registro de mudanças",
"description": "Link title"
},
"feedback":
{
"message": "Deixe um comentário",
"description": "Link title"
},
"buyMeACoffee":
{
"message": "Me compre um café!",
"description": "Link title"
},
"credits":
{
"message": "Desenvolvido por Michael 'XFox' Gordeev",
"description": "Options menu credits"
},
"setAside":
{
"message": "Reservas as abas atuais",
"description": "Save collection action name. Used in the pane, extension context menu and manifest shortcuts"
},
"setMultipleTabsAsideTooltip":
{
"message": "Dica: Você pode reservar abas específicas ao selecioná-las (ao apertar CTRL ou SHIFT e clicando nas abas) antes de clicar na extensão Reservar Abas",
"description": "Tooltip displayed on hover in the pane to explain how to set specific tabs aside"
},
"nothingSaved":
{
"message": "Você não tem abas reservadas",
"description": "Placeholder for empty pane"
},
"removeTab":
{
"message": "Remover aba da coleção",
"description": "Button hint on a tab card"
},
"restoreTabs":
{
"message": "Restaurar abas",
"description": "Collection restore action link name"
},
"more":
{
"message": "Mais...",
"description": "Collections' more button title"
},
"restoreNoRemove":
{
"message": "Restaurar sem remover",
"description": "Context action item name"
},
"removeCollection":
{
"message": "Remover coleção",
"description": "Collection remove action name"
},
"removeCollectionConfirm":
{
"message": "Você tem certeza que quer deletar esta coleção?",
"description": "Prompt dialog content on collection deletion"
},
"removeTabConfirm":
{
"message": "Você tem certeza que quer deletar esta aba?",
"description": "Prompt dialog content on one tab deletion"
},
"togglePaneContext":
{
"message": "Alternar painel de abas",
"description": "Context action name. Used in extension context menu and manifest shortcuts"
},
"noTabsToSave":
{
"message": "Não há abas disponíveis para salvar",
"description": "Alert dialog message when there's no tabs to save"
},
"errorSavingTabs":
{
"message": "As abas não puderam ser reservadas. Você pode ter tentado reservar muitas abas de uma vez só, ou pode ter abas demais já reservadas.",
"description": "Alert dialog message when there is an issue saving tabs"
},
"olderDataMigrationFailed":
{
"message": "Algumas abas reservadas de uma versão antiga não puderam ser migradas. Elas foram salvas como marcadores do navegador.",
"description": "Alert dialog message when there is an issue migrating previous versions data"
},
"tabs":
{
"message": "itens",
"description": "Collection tabs counter label (e.g. 8 items)"
}
}
+31 -46
View File
@@ -6,7 +6,7 @@
},
"description":
{
"message": "Функиця классического Microsoft Edge на браузерах Chromium",
"message": "Функиця отложенных вкладок классического Microsoft Edge",
"description": "Extension description"
},
"author":
@@ -19,14 +19,24 @@
"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": "Откладывать вкладки при нажатии на иконку расширения (Alt+P или правая кнопка мыши для открытия панели)",
"message": "Откладывать вкладки при нажатии на иконку расширения (%TOGGLE_SHORTCUT% или правая кнопка мыши для открытия панели)",
"description": "Label for option"
},
"github":
@@ -34,9 +44,9 @@
"message": "Страница GitHub",
"description": "Link title"
},
"contributors":
"changelog":
{
"message": "Свой вклад вложили",
"message": "Список изменений",
"description": "Link title"
},
"feedback":
@@ -59,6 +69,11 @@
"message": "Отложить открытые вкладки",
"description": "Save collection action name. Used in the pane, extension context menu and manifest shortcuts"
},
"setMultipleTabsAsideTooltip":
{
"message": "Подсказка: теперь вы можете откладывать только некоторые вкладки. Просто выделите нужные вкладки с помощью Shift или Ctrl перед использованием расширения",
"description": "Tooltip displayed on hover in the pane to explain how to set specific tabs aside"
},
"nothingSaved":
{
"message": "У вас нет отложенных вкладок",
@@ -109,49 +124,19 @@
"message": "Нет доступных для сохранения вкладок",
"description": "Alert dialog message when there's no tabs to save"
},
"errorSavingTabs":
{
"message": "Не удается отложить вкладки. Возможно, вы пытаетесь отложить слишком много вкладок за раз или уже отложили слишком много вкладок",
"description": "Alert dialog message when there is an issue saving tabs"
},
"olderDataMigrationFailed":
{
"message": "Не удалось перенести отложенные вкладки, сохраненные в старой версии расширения. Они были сохранены в закладках",
"description": "Alert dialog message when there is an issue migrating previous versions data"
},
"tabs":
{
"message": "Вкладок",
"description": "Collection tabs counter label"
},
"ago":
{
"message": "назад",
"description": "Human friendly timestamp part (e.g. 15 hour(s) ago)"
},
"minutes":
{
"message": "минут(ы)",
"description": "Human friendly timestamp part (e.g. 15 minute(s) ago)"
},
"hours":
{
"message": "час(ов)",
"description": "Human friendly timestamp part (e.g. 15 hour(s) ago)"
},
"days":
{
"message": "дней",
"description": "Human friendly timestamp part (e.g. 15 day(s) ago)"
},
"weeks":
{
"message": "недель",
"description": "Human friendly timestamp part (e.g. 15 week(s) ago)"
},
"months":
{
"message": "месяц(ев)",
"description": "Human friendly timestamp part (e.g. 15 month(s) ago)"
},
"years":
{
"message": "лет",
"description": "Human friendly timestamp part (e.g. 15 years(s) ago)"
},
"justNow":
{
"message": "Только что",
"description": "Human friendly timestamp part"
"message": "вкладок",
"description": "Collection tabs counter label (e.g. 8 items)"
}
}
+142
View File
@@ -0,0 +1,142 @@
{
"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"
},
"changelog":
{
"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"
},
"setMultipleTabsAsideTooltip":
{
"message": "提示:在单击TabsAside扩展名之前,可以通过选择特定选项卡(按住CTRL或SHIFT并单击选项卡)来搁置它们",
"description": "Tooltip displayed on hover in the pane to explain how to set specific tabs aside"
},
"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"
},
"errorSavingTabs":
{
"message": "标签页无法被搁置. 可能是因为您尝试一次性搁置过多的标签页, 或是先前搁置的标签页过多.",
"description": "Alert dialog message when there is an issue saving tabs"
},
"olderDataMigrationFailed":
{
"message": "一部分搁置的标签页无法从上一个版本中迁移. 这些标签页已经作为浏览器书签被备份.",
"description": "Alert dialog message when there is an issue migrating previous versions data"
},
"tabs":
{
"message": "项",
"description": "Collection tabs counter label (e.g. 8 items)"
}
}
+116 -47
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,7 +29,9 @@
right: 0px;
top: 0px;
bottom: 0px;
overflow: auto;
overflow: hidden;
display: grid;
grid-template-rows: auto 1fr;
width: 100%;
min-width: 500px;
@@ -28,15 +41,13 @@
border-width: 0px 0px 0px 1px;
box-shadow: 6px 0px 12px black;
font-size: 13px;
transform: translateX(110%); /* pane is hidded */
transition: .2s;
}
aside[embedded]
{
width: 40% !important;
width: 500px !important;
}
.tabsAside.pane[opened]
@@ -47,35 +58,34 @@
/* Pane header*/
.tabsAside.pane > header
{
margin: 20px 40px;
}
.tabsAside.pane > header > div
{
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;
grid-template-columns: 1fr auto auto;
grid-column-gap: 10px;
grid-row-gap: 20px;
}
.tabsAside.pane > header > div > h1
.tabsAside.pane > header > h1
{
margin: 10px 0px;
font-weight: normal;
font-size: 21pt;
margin: 0px 5px;
font-weight: 400;
font-size: 24px;
}
.tabsAside.pane > header > div > button
.tabsAside.pane > header > button .updateBadge
{
margin: auto;
position: absolute;
bottom: 2px;
right: 2px;
}
.tabsAside.pane > header > div > nav
.tabsAside.pane > header > nav
{
top: 70px;
right: 40px;
}
.tabsAside.pane > header nav > div
{
box-shadow: 0px 4px 5px -2px rgba(100, 100, 100, .5);
top: 45px;
right: 55px;
}
.tabsAside.pane > header nav > p
@@ -88,25 +98,53 @@
text-decoration: none;
}
.iconArrowRight
.tabsAside.pane > header nav > p > a:hover
{
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");
text-decoration: underline;
}
.tabsAside.pane > header > hr
.saveTabs
{
border: 1px solid #8a8a8a;
display: inline-grid;
grid-template-columns: 16px auto 16px;
grid-column-gap: 15px;
margin-right: auto;
}
.saveTabs:hover
{
text-decoration: none !important;
}
.saveTabs:hover span:nth-child(2)
{
text-decoration: underline;
}
.iconArrowRight,.iconQuestionCircle
{
width: 16px;
height: 16px;
display: inline-block;
font-family: "SegoeMDL2Assets";
margin: 2px;
}
.iconQuestionCircle
{
font-size: small;
padding-top: 2px;
margin-bottom: 0;
}
.tabsAside.pane section
{
overflow: auto;
}
.tabsAside.pane > section > h2
{
margin: 0px 40px;
margin: 20px;
font-weight: normal;
}
@@ -116,11 +154,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,16 +189,21 @@
.collectionSet > .header > small
{
color: gray;
visibility: visible !important;
}
.collectionSet > .header > span
.collectionSet > .header > input
{
margin: 0px;
visibility: visible !important;
font-weight: 600;
border: none;
background: transparent;
}
.collectionSet > .header > a
.collectionSet > .header > input:hover
{
font-size: 11pt;
border: 1px solid black;
}
.collectionSet > .header > div > nav
@@ -149,8 +215,7 @@
/* Tabs collection */
.collectionSet > .set
{
margin: 0px 0px 0px 20px;
padding: 10px 40px 10px 20px;
padding: 5px 10px;
white-space: nowrap;
overflow: auto;
}
@@ -172,21 +237,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("moz-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 +280,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;
}
@@ -224,6 +292,7 @@
overflow: hidden;
margin: auto 0px;
margin-right: 10px;
font-size: 12px;
}
.collectionSet > .set > div:hover > div > span
{
+68 -6
View File
@@ -9,25 +9,57 @@
color: white;
}
.tabsAside[darkmode] .pane header
{
background-color: #3b3b3b;
}
.tabsAside[darkmode] nav hr
{
filter: invert(1);
}
.tabsAside[darkmode] .saveTabs > div
{
filter: invert();
}
/* Button style */
.tabsAside[darkmode] .pane button
.tabsAside[darkmode] button
{
filter: invert();
color: white;
}
.tabsAside[darkmode] .pane button:hover
.tabsAside[darkmode] .pane button:hover,
.tabsAside[darkmode] .pane .control.checkbox:hover
{
background-color: gray;
}
.tabsAside[darkmode] .pane button img,
.tabsAside[darkmode] .pane label > input + span
{
filter: invert();
}
.tabsAside[darkmode] .pane button:active
{
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 +82,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,11 +104,33 @@
.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 */
.tabsAside[darkmode] .pane nav
{
background-color: #3f3f3f;
background-color: #4a4a4a;
}
.tabsAside[darkmode] .listviewSwitch > div
{
border-radius: 1px;
background-color: gray;
}
.tabsAside[darkmode] .tabsAside .listviewSwitch.tile > div
{
background-color: #c2c2c2;
}
.tabsAside[darkmode] .tabsAside[listview] .listviewSwitch.tile > div
{
background-color: gray;
}
.tabsAside[darkmode] .tabsAside[listview] .listviewSwitch.list > div
{
background-color: #c2c2c2;
}
+128 -17
View File
@@ -1,3 +1,16 @@
.updateBadge
{
display: none;
}
.tabsAside[updated] .updateBadge
{
width: 10px;
height: 10px;
background-color: #0078d7;
border-radius: 5px;
display: block;
}
/* Custom scrollbar */
.tabsAside ::-webkit-scrollbar
{
@@ -17,7 +30,7 @@
.tabsAside
{
font-family: 'DefaultFont';
font-family: "SegoeUI", "SegoeMDL2Assets" !important;
font-size: 14px;
user-select: none;
}
@@ -41,15 +54,18 @@
/* Buttons style */
.tabsAside button
{
width: 32px;
height: 32px;
width: 28px;
height: 28px;
font-size: 16px;
background-color: transparent;
border: none;
cursor: pointer;
outline: none !important;
position: relative;
}
.tabsAside button:hover
{
background-color: #c6c6c6;
background-color: #f2f2f2;
}
.tabsAside button:active
{
@@ -62,23 +78,64 @@
user-select: none;
position: absolute;
width: 250px;
width: 390px;
font-size: 12px;
box-shadow: 0px 0px 10px black;
box-shadow: 0px 4px 10px rgba(0,0,0,.25);
background-color: white;
border-radius: 5px;
z-index: 10;
visibility: hidden;
padding: 4px 0px;
}
.tabsAside nav hr
{
border: none;
height: 1px;
background-color: rgba(0, 0, 0, .1);
margin: 4px 0px
}
.tabsAside nav button
{
align-content: center;
text-align: start;
padding: 0px 10px;
padding: 0px 12px;
width: 100%;
height: 32px;
font-family: "SegoeUI";
font-size: 12px;
display: grid;
grid-template-columns: auto 1fr auto;
grid-column-gap: 14px;
}
.tabsAside nav button:hover
{
background-color: #eeee;
}
.tabsAside nav button img:first-child
{
width: 16px;
height: 16px;
}
.tabsAside nav button img:nth-child(3)
{
width: 12px;
height: 12px;
}
.tabsAside nav button *:nth-child(3)
{
align-self: center;
}
.tabsAside button + nav:active,
@@ -91,25 +148,79 @@
.btn
{
background-repeat: no-repeat;
background-size: 15px;
background-size: 12px;
background-position: center;
font-family: "SegoeUI", "SegoeMDL2Assets";
}
.btn.more
.control.checkbox
{
display: block;
position: relative;
padding: 8px 12px 8px 42px;
box-sizing: border-box;
cursor: pointer;
min-height: 32px;
}
.control.checkbox:hover
{
background-image: url("chrome-extension://__MSG_@@extension_id__/icons/more.svg");
background-color: #eeeeee;
}
.btn.remove
.control.checkbox input
{
background-image: url("chrome-extension://__MSG_@@extension_id__/icons/cancel.svg");
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.control.checkbox input + span
{
position: absolute;
left: 12px;
height: 16px;
width: 16px;
transition-property: background, border, border-color;
transition-duration: 200ms;
transition-timing-function: cubic-bezier(0.4, 0, 0.23, 1);
}
.control.checkbox input + span::after
{
position: absolute;
color: rgba(0, 0, 0, .4);
font-family: "SegoeMDL2Assets";
content: "\E73E";
width: 16px;
height: 16px;
font-size: 16px;
text-align: center;
display: none;
}
.control.checkbox input:checked + span::after
{
display: block;
color: black;
}
.control.checkbox:hover input + span::after
{
display: block;
}
@font-face
{
font-family: 'DefaultFont';
font-family: "SegoeUI";
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("../fonts/segoeui.ttf") format("truetype"),
url("../fonts/segoeui.woff") format("woff")
}
@font-face
{
font-family: "SegoeMDL2Assets";
src: local("Segoe MDL2 Assets"),
url("../fonts/segoemdl2.ttf") format("truetype"),
url("../fonts/segoemdl2.woff") format("woff")
}
+59
View File
@@ -0,0 +1,59 @@
.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;
margin: 0px auto;
}
.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;
}
+1
View File
@@ -0,0 +1 @@
You may use the Segoe MDL2 Assets and Segoe UI fonts or glyphs included in this file (“Software”) solely to design, develop and test your programs for Microsoft Office. Microsoft Office includes but is not limited to any software product or service branded by trademark, trade dress, copyright or some other recognized means, as a product or service of Microsoft Office. This license does not grant you the right to distribute or sublicense all or part of the Software to any third party. By using the Software you agree to these terms. If you do not agree to these terms, do not use the Segoe MDL2 and Segoe UI Fonts or Glyphs.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
-5
View File
@@ -1,5 +0,0 @@
<!-- Exported by Character Map UWP -->
<svg height="100%" viewBox="0 42 1021 941" width="100%" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M1021,512L550.5,982.5L505.5,937.5L899,544L0,544L0,480L899,480L505.5,86.5L550.5,41.5Z" fill="#000000" fill-opacity="1">
</path>
</svg>

Before

Width:  |  Height:  |  Size: 320 B

+29
View File
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="36px" viewBox="0 0 24 36" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 59 (86127) - https://sketch.com -->
<title>Group</title>
<desc>Created with Sketch.</desc>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Group-2" transform="translate(-17.000000, -7.000000)">
<g id="Logo">
<g id="Group-4" transform="translate(17.000000, 8.000000)">
<g id="Group">
<g id="Logo" transform="translate(0.559947, 0.000000)">
<polygon id="Fill-1" fill="#FF9100" points="11.2752139 6.65517263 2.53594776 6.60250871 6.82755165 33.7158335 7.76390159 33.7158335 16.3471094 33.7158335 17.2834593 33.7158335 21.5750632 6.60250871"></polygon>
<polygon id="Fill-2" fill="#FFDD00" points="11.2752139 6.65517263 2.53594776 6.60250871 6.82755165 33.7158335 7.76390159 33.7158335 14.1622929 33.7158335 15.0986428 33.7158335 19.3902467 6.60250871"></polygon>
<polygon id="Fill-3" fill="#FFFFFF" points="0.0390145809 6.60252433 22.5894423 6.60252433 22.5894423 4.10216009 0.0390145809 4.10216009"></polygon>
<polygon id="Stroke-4" stroke="#000000" stroke-width="1.17043743" points="0.0390145809 6.60252433 22.5894423 6.60252433 22.5894423 4.10216009 0.0390145809 4.10216009"></polygon>
<polygon id="Fill-6" fill="#FFFFFF" points="18.2198093 0.0390681913 12.8357971 0.0390681913 9.63660147 0.0390681913 4.25258931 0.0390681913 2.61397692 3.78961456 9.63660147 3.78961456 12.8357971 3.78961456 19.8584217 3.78961456"></polygon>
<g id="Group-11" transform="translate(0.936350, 0.000000)" stroke-width="1.17043743">
<polygon id="Stroke-7" stroke="#050505" points="17.2834593 0.0390681913 11.8994472 0.0390681913 8.70025153 0.0390681913 3.31623937 0.0390681913 1.67762698 3.78961456 8.70025153 3.78961456 11.8994472 3.78961456 18.9220717 3.78961456"></polygon>
<polygon id="Stroke-9" stroke="#000000" points="10.3388639 6.65517263 0.0390145809 6.60250871 4.33061848 33.7158335 5.26696842 33.7158335 15.4107594 33.7158335 16.3471094 33.7158335 20.6387133 6.60250871"></polygon>
</g>
<polygon id="Fill-12" fill="#FFFFFF" points="21.8871799 14.2598898 11.6059795 14.2598898 10.8664191 14.2598898 0.585218713 14.2598898 2.50832543 25.0427106 11.2361993 24.9487126 19.9640731 25.0427106"></polygon>
<polygon id="Stroke-13" stroke="#000000" stroke-width="1.17043743" points="21.8871799 14.2598898 11.6059795 14.2598898 10.8664191 14.2598898 0.585218713 14.2598898 2.50832543 25.0427106 11.2361993 24.9487126 19.9640731 25.0427106"></polygon>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

-5
View File
@@ -1,5 +0,0 @@
<!-- Exported by Character Map UWP -->
<svg height="100%" viewBox="122 122 781 781" width="100%" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M557.5,512L902.5,857.5L857.5,902.5L512,557.5L166.5,902.5L121.5,857.5L466.5,512L121.5,166.5L166.5,121.5L512,466.5L857.5,121.5L902.5,166.5Z" fill="#000000" fill-opacity="1">
</path>
</svg>

Before

Width:  |  Height:  |  Size: 375 B

+5
View File
@@ -0,0 +1,5 @@
<!-- Exported by Character Map UWP -->
<svg height="100%" viewBox="106 234 813 580" width="100%" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M918.5,278.5L384,813.5L105.5,534.5L150.5,489.5L384,722.5L873.5,233.5Z" fill="#000000" fill-opacity="1">
</path>
</svg>

After

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

+5
View File
@@ -0,0 +1,5 @@
<!-- Exported by Character Map UWP -->
<svg height="100%" viewBox="0 0 1024 1024" width="100%" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M479,664C512.667,677.333 543.333,695.083 571,717.25C598.667,739.417 622.333,764.833 642,793.5C661.667,822.167 676.917,853.333 687.75,887C698.583,920.667 704,955.667 704,992L704,1024L640,1024L640,992C640,952.333 632.417,915 617.25,880C602.083,845 581.5,814.5 555.5,788.5C529.5,762.5 499,741.917 464,726.75C429,711.583 391.667,704 352,704C312.333,704 275,711.583 240,726.75C205,741.917 174.5,762.5 148.5,788.5C122.5,814.5 101.917,845 86.75,880C71.5833,915 64,952.333 64,992L64,1024L0,1024L0,992C0,955.667 5.41667,920.667 16.25,887C27.0833,853.333 42.3333,822.167 62,793.5C81.6667,764.833 105.333,739.417 133,717.25C160.667,695.083 191.333,677.333 225,664C210,653.667 196.5,641.667 184.5,628C172.5,614.333 162.333,599.583 154,583.75C145.667,567.917 139.25,551.25 134.75,533.75C130.25,516.25 128,498.333 128,480C128,449 133.917,419.917 145.75,392.75C157.583,365.583 173.583,341.917 193.75,321.75C213.917,301.583 237.583,285.583 264.75,273.75C291.917,261.917 321,256 352,256C383,256 412.083,261.917 439.25,273.75C466.417,285.583 490.083,301.583 510.25,321.75C530.417,341.917 546.417,365.583 558.25,392.75C570.083,419.917 576,449 576,480C576,498.333 573.75,516.25 569.25,533.75C564.75,551.25 558.333,567.917 550,583.75C541.667,599.583 531.5,614.333 519.5,628C507.5,641.667 494,653.667 479,664ZM352,640C374,640 394.667,635.833 414,627.5C433.333,619.167 450.25,607.75 464.75,593.25C479.25,578.75 490.75,561.75 499.25,542.25C507.75,522.75 512,502 512,480C512,458 507.75,437.333 499.25,418C490.75,398.667 479.25,381.75 464.75,367.25C450.25,352.75 433.333,341.25 414,332.75C394.667,324.25 374,320 352,320C330,320 309.25,324.25 289.75,332.75C270.25,341.25 253.25,352.75 238.75,367.25C224.25,381.75 212.833,398.667 204.5,418C196.167,437.333 192,458 192,480C192,502 196.167,522.75 204.5,542.25C212.833,561.75 224.25,578.75 238.75,593.25C253.25,607.75 270.25,619.167 289.75,627.5C309.25,635.833 330,640 352,640ZM1024,0L1024,512L896,512L704,704L704,512L640,512L640,448L768,448L768,549.5L869.5,448L960,448L960,64L320,64L320,179C309.333,180.333 298.583,182.083 287.75,184.25C276.917,186.417 266.333,189.333 256,193L256,0Z" fill="#000000" fill-opacity="1">
</path>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg>
<!--
Font Awesome Free 5.4.1 by @fontawesome - https://fontawesome.com
License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
-->

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 424 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

+5
View File
@@ -0,0 +1,5 @@
<!-- Exported by Character Map UWP -->
<svg height="100%" viewBox="7 111 1017 721" width="100%" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M1024,192L1024,256L384,256L384,192ZM384,384L1024,384L1024,448L384,448ZM384,576L1024,576L1024,640L384,640ZM384,768L1024,768L1024,832L384,832ZM239,495L273,529L96,706L7,617L41,583L96,638ZM239,111L273,145L96,322L7,233L41,199L96,254Z" fill="#000000" fill-opacity="1">
</path>
</svg>

After

Width:  |  Height:  |  Size: 465 B

-5
View File
@@ -1,5 +0,0 @@
<!-- Exported by Character Map UWP -->
<svg height="100%" viewBox="64 448 896 128" width="100%" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M128,448C137,448 145.333,449.667 153,453C160.667,456.333 167.417,460.917 173.25,466.75C179.083,472.583 183.667,479.333 187,487C190.333,494.667 192,503 192,512C192,521 190.333,529.333 187,537C183.667,544.667 179.083,551.417 173.25,557.25C167.417,563.083 160.667,567.667 153,571C145.333,574.333 137,576 128,576C119,576 110.667,574.333 103,571C95.3333,567.667 88.5833,563.083 82.75,557.25C76.9167,551.417 72.3333,544.667 69,537C65.6667,529.333 64,521 64,512C64,503 65.6667,494.667 69,487C72.3333,479.333 76.9167,472.583 82.75,466.75C88.5833,460.917 95.3333,456.333 103,453C110.667,449.667 119,448 128,448ZM512,448C521,448 529.333,449.667 537,453C544.667,456.333 551.417,460.917 557.25,466.75C563.083,472.583 567.667,479.333 571,487C574.333,494.667 576,503 576,512C576,521 574.333,529.333 571,537C567.667,544.667 563.083,551.417 557.25,557.25C551.417,563.083 544.667,567.667 537,571C529.333,574.333 521,576 512,576C503,576 494.667,574.333 487,571C479.333,567.667 472.583,563.083 466.75,557.25C460.917,551.417 456.333,544.667 453,537C449.667,529.333 448,521 448,512C448,503 449.667,494.667 453,487C456.333,479.333 460.917,472.583 466.75,466.75C472.583,460.917 479.333,456.333 487,453C494.667,449.667 503,448 512,448ZM896,448C905,448 913.333,449.667 921,453C928.667,456.333 935.417,460.917 941.25,466.75C947.083,472.583 951.667,479.333 955,487C958.333,494.667 960,503 960,512C960,521 958.333,529.333 955,537C951.667,544.667 947.083,551.417 941.25,557.25C935.417,563.083 928.667,567.667 921,571C913.333,574.333 905,576 896,576C887,576 878.667,574.333 871,571C863.333,567.667 856.583,563.083 850.75,557.25C844.917,551.417 840.333,544.667 837,537C833.667,529.333 832,521 832,512C832,503 833.667,494.667 837,487C840.333,479.333 844.917,472.583 850.75,466.75C856.583,460.917 863.333,456.333 871,453C878.667,449.667 887,448 896,448Z" fill="#000000" fill-opacity="1">
</path>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

+155 -55
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)
@@ -71,14 +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"];
// 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) =>
{
@@ -94,11 +100,11 @@ function Initialize()
})
);
// Exntension browser icon action
// Extension browser icon action
var swapIconAction = document.querySelector("#swapIconAction");
chrome.storage.sync.get(
{ "setAsideOnClick": false },
values => swapIconAction.checked = values.setAsideOnClick
values => swapIconAction.checked = values?.setAsideOnClick ?? false
);
chrome.storage.onChanged.addListener((changes, namespace) =>
{
@@ -114,6 +120,62 @@ function Initialize()
})
);
// 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 = () =>
{
@@ -128,11 +190,34 @@ function Initialize()
window.open(i.value, "_blank");
});
chrome.runtime.sendMessage({ command: "loadData" }, (collections) =>
// Showing changelog badge if updated
chrome.storage.local.get({ "showUpdateBadge": false }, values =>
{
if (document.querySelector(".tabsAside.pane section div") == null)
collections.forEach(i =>
AddCollection(i));
if (values.showUpdateBadge)
{
pane.setAttribute("updated", "");
let settingsButton = document.querySelector("header .btn.more");
settingsButton.addEventListener("focusout", () =>
{
if (!pane.hasAttribute("updated"))
return;
pane.removeAttribute("updated");
chrome.storage.local.set({ "showUpdateBadge": false });
});
}
});
chrome.runtime.sendMessage({ command: "loadData" }, ({ collections, thumbnails }) =>
ReloadCollections(collections, thumbnails));
chrome.runtime.onMessage.addListener((message) =>
{
switch (message.command)
{
case "reloadCollections":
ReloadCollections(message.collections, message.thumbnails);
break;
}
});
setTimeout(() => pane.setAttribute("opened", ""), 100);
@@ -142,9 +227,24 @@ 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("span[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)
function ReloadCollections(collections, thumbnails)
{
document.querySelector(".tabsAside section h2").removeAttribute("hidden");
document.querySelectorAll(".tabsAside section > div").forEach(i => i.remove());
for (var collection of Object.values(collections).reverse())
AddCollection(collection, thumbnails);
}
function AddCollection(collection, thumbnails)
{
var list = document.querySelector(".tabsAside section");
list.querySelector("h2").setAttribute("hidden", "");
@@ -152,31 +252,28 @@ function AddCollection(collection)
var rawTabs = "";
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] + "'" + (thumbnails[collection.links[i]]?.pageCapture ? " style='background-image: url(" + thumbnails[collection.links[i]].pageCapture + ")'" : "") + " value='" + collection.links[i] + "'>" +
"<div>" +
"<div" + ((collection.icons[i] == 0 || collection.icons[i] == null) ? "" : " style='background-image: url(\"" + collection.icons[i] + "\")'") + "></div>" +
"<div" + (!thumbnails[collection.links[i]]?.iconUrl ? "" : " style='background-image: url(\"" + thumbnails[collection.links[i]].iconUrl + "\")'") + "></div>" +
"<span>" + collection.titles[i] + "</span>" +
"<button loc_alt='name' class='btn remove' title='Remove tab from collection'></button>" +
"<button loc_alt='removeTab' class='btn remove' title='Remove tab from collection'>&#xE711;</button>" +
"</div>" +
"</div>";
}
list.innerHTML +=
"<div class='collectionSet'>" +
"<div class='collectionSet' id='set_"+collection.timestamp+"'>" +
"<div class='header'>" +
"<span>" + chrome.i18n.getMessage("tabs") + ": " + collection.links.length + "</span>" +
"<small>" + GetAgo(collection.timestamp) + "</small>" +
"<input type='text' value='" + (collection.name ?? new Date(collection.timestamp).toDateString()) + "'/>" +
"<a loc='restoreTabs' class='restoreCollection'>Restore tabs</a>" +
"<div>" +
"<button loc_alt='more' class='btn more' title='More...'></button>" +
"<button loc_alt='more' class='btn more' title='More...'>&#xE712;</button>" +
"<nav>" +
"<button loc='restoreNoRemove' class='restoreCollection noDelete'>Restore without removing</button>" +
"</nav>" +
"</div>" +
"<button loc_alt='removeCollection' class='btn remove' title='Remove collection'></button>" +
"<button loc_alt='removeCollection' class='btn remove' title='Remove collection'>&#xE711;</button>" +
"<small>" + collection.links.length + " " + chrome.i18n.getMessage("tabs") +"</small>" +
"</div>" +
"<div class='set' class='tabsList'>" + rawTabs + "</div>" +
@@ -184,26 +281,33 @@ function AddCollection(collection)
UpdateLocale();
list.querySelectorAll("input").forEach(i =>
i.addEventListener("focusout",(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 =>
i.onclick = () => RestoreTabs(i.parentElement.parentElement.parentElement.parentElement, false));
list.querySelectorAll(".openTab").forEach(i =>
i.onclick = () =>
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 = () =>
RemoveOneTab(i.parentElement.parentElement));
}
function SetTabsAside()
@@ -211,13 +315,23 @@ function SetTabsAside()
chrome.runtime.sendMessage({ command: "saveTabs" });
}
function RenameCollection(collectionData, name)
{
chrome.runtime.sendMessage(
{
command: "renameCollection",
newName: name,
collectionKey: collectionData.id
});
}
function RestoreTabs(collectionData, removeCollection = true)
{
chrome.runtime.sendMessage(
{
command: "restoreTabs",
removeCollection: removeCollection,
collectionIndex: Array.prototype.slice.call(collectionData.parentElement.children).indexOf(collectionData) - 1
collectionKey: collectionData.id
},
() =>
{
@@ -229,32 +343,37 @@ function RestoreTabs(collectionData, removeCollection = true)
function RemoveTabs(collectionData)
{
if (!confirm(chrome.i18n.getMessage("removeCollectionConfirm")))
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
collectionKey: collectionData.id
},
() => RemoveCollectionElement(collectionData)
);
});
}
function RemoveOneTab(tabData)
{
if (!confirm(chrome.i18n.getMessage("removeTabConfirm")))
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,
collectionKey: tabData.parentElement.parentElement.id,
tabIndex: Array.prototype.slice.call(tabData.parentElement.children).indexOf(tabData)
},
() =>
{
tabData.parentElement.previousElementSibling.children[0].textContent = chrome.i18n.getMessage("tabs") + ": " + (tabData.parentElement.children.length - 1);
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);
@@ -264,26 +383,7 @@ function RemoveOneTab(tabData)
else
RemoveElement(tabData);
});
}
function GetAgo(timestamp)
{
var minutes = (Date.now() - timestamp) / 60000;
if (minutes < 1)
return chrome.i18n.getMessage("justNow");
else if (minutes < 60)
return Math.floor(minutes) + " " + chrome.i18n.getMessage("minutes") + " " + chrome.i18n.getMessage("ago");
else if (minutes < 24 * 60)
return Math.floor(minutes / 60) + " " + chrome.i18n.getMessage("hours") + " " + chrome.i18n.getMessage("ago");
else if (minutes < 7 * 24 * 60)
return Math.floor(minutes / 24 / 60) + " " + chrome.i18n.getMessage("days") + " " + chrome.i18n.getMessage("ago");
else if (minutes < 30 * 24 * 60)
return Math.floor(minutes / 7 / 24 / 60) + " " + chrome.i18n.getMessage("weeks") + " " + chrome.i18n.getMessage("ago");
else if (minutes < 365 * 24 * 60)
return Math.floor(minutes / 30 / 24 / 60) + " " + chrome.i18n.getMessage("months") + " " + chrome.i18n.getMessage("ago");
else
return Math.floor(minutes / 365 / 24 / 60) + " " + chrome.i18n.getMessage("years") + " " + chrome.i18n.getMessage("ago");
});
}
function RemoveElement(el)
@@ -294,7 +394,7 @@ function RemoveElement(el)
function RemoveCollectionElement(el)
{
RemoveElement(el);
if (el.parentElement.children.length < 2)
if (el.parentElement.children.length < 3)
setTimeout(() => document.querySelector(".tabsAside.pane > section > h2").removeAttribute("hidden"), 250);
RemoveElement(el);
}
+309 -134
View File
@@ -1,7 +1,28 @@
function TogglePane(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 = [];
var syncEnabled = true; //This variable controls whether to use the chrome sync storage, along with its size limitations, or the much larger chrome local storage. The option is currently not exposed to the users.
var collectionStorage = syncEnabled ? chrome.storage.sync : chrome.storage.local;
//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,
@@ -40,8 +61,11 @@ function TogglePane(tab)
}
}
function ProcessCommand(command)
function ProcessCommand (command)
{
GetTabsToSave((returnedTabs) =>
{
tabsToSave = returnedTabs;
switch(command)
{
case "set-aside":
@@ -57,40 +81,216 @@ function ProcessCommand(command)
)
break;
}
});
}
chrome.browserAction.onClicked.addListener((tab) =>
{
GetTabsToSave((returnedTabs) =>
{
tabsToSave = returnedTabs;
chrome.storage.sync.get({ "setAsideOnClick": false }, values =>
{
if (values.setAsideOnClick)
if (values?.setAsideOnClick)
SaveCollection();
else
TogglePane(tab);
});
});
});
// Adding context menu options
chrome.contextMenus.create(
{
id: "toggle-pane",
contexts: ['all'],
title: chrome.i18n.getMessage("togglePaneContext")
}
);
chrome.contextMenus.create(
{
id: "set-aside",
contexts: ['all'],
title: chrome.i18n.getMessage("setAside")
}
);
collections = { };
thumbnails = { };
var collections = JSON.parse(localStorage.getItem("sets")) || [];
/**
* Updates the thumbnail storage, then the collection synced storage. Calls the onSuccess callback when successful, and onFailure when failed.
* @param {Object} collectionsToUpdate An object containing one or more collections to be updated. By default, updates the whole "collections" item.
* @param {function} onSuccess A function without arguments, that will be called after the collections and thumbnails values are obtained, if collections are successfully updated
* @param {function} onFailure A function that will be called with the error, after the collections and thumbnails values are obtained, if collections are not successfully updated
*/
function UpdateStorages (collectionsToUpdate = collections, onSuccess = () => null, onFailure = (error) => { throw error.message; })
{
//The collections storage is updated after the thumbnail storage, so that the thumbnails are ready when the collections are updated.
chrome.storage.local.set({ "thumbnails": thumbnails },
() => collectionStorage.set(CompressCollectionsStorage(collectionsToUpdate),
() =>
{
if (!chrome.runtime.lastError)
onSuccess();
else
onFailure(chrome.runtime.lastError);
})
);
//When the collection storage is updated, a listener set up below reacts and updates the collections global variable, so we do not need to update that variable here
}
/**
* Use a compression mechanism to compress collections in an object, one by one.
* @param {Object} collectionsToCompress object of collections to compress
* @returns {Object} Object of compressed stringified collections.
*/
function CompressCollectionsStorage (collectionsToCompress)
{
var compressedStorage = { };
for (var [key, value] of Object.entries(collectionsToCompress)) //For each collection in the uncompressed collectionsToCompress
{
var cloneWithoutTimestamp = Object.assign({ }, value, { timestamp: null });
compressedStorage[key] = LZUTF8.compress(JSON.stringify(cloneWithoutTimestamp), { outputEncoding: "StorageBinaryString" });
}
return compressedStorage;
}
/**
* Load and decompresses the thumbnails and collections global variables from the storage, updates the theme and eventually sends the collections and thumbnails to a callback
* @param {function} callback A function that will be called after the collections and thumbnails values are obtained.
* These collections and thumbnails are sent as a data={"collections":collections,"thumbnails":thumbnails} argument to the callback.
*/
function LoadStorages (callback = () => null)
{
chrome.storage.local.get("thumbnails", values =>
{
thumbnails = values?.thumbnails ?? { };
collectionStorage.get(null, values =>
{
collections = DecompressCollectionsStorage(values);
UpdateBadgeCounter();
callback({ "collections": collections, "thumbnails": thumbnails });
});
});
}
/**
* Use a decompression mechanism to decompress stringified collections in an object, one by one.
* Ignores non collections items (items with a key not starting with "set_"
* @param {Object} compressedCollections object of stringified collections to decompress
* @returns {Object} Object of decompressed and parsed collections.
*/
function DecompressCollectionsStorage (compressedCollections)
{
var decompressedStorage = { };
for (var [key, value] of Object.entries(compressedCollections))
{
if (!key.startsWith("set_"))
continue;
decompressedStorage[key] = JSON.parse(LZUTF8.decompress(value, { inputEncoding: "StorageBinaryString" }));
decompressedStorage[key].timestamp = parseInt(key.substr(4));
}
return decompressedStorage;
}
/**
* Merges a provided collections array with older pre v2 collections,
* saving the result into the new post v2 storage, or into bookmarks on failure.
* Allows to preserve backward compatibility with the localStorage method of storing collections in pre v2 versions.
* @param {Object} collections The current collections object
*/
function MergePreV2Collections ()
{
if (localStorage.getItem("sets"))
{
console.log("Found pre-v2 data");
var old_collections = JSON.parse(localStorage.getItem("sets"));
//Migrate thumbnails and icons to follow the new format .
old_collections.forEach(collection =>
{
for (var i = 0; i < collection.links.length; i++)
thumbnails[collection.links[i]] =
{
"pageCapture": collection.thumbnails[i],
"iconUrl": collection.icons[i]
};
delete collection.thumbnails;
delete collection.icons;
UpdateStorages({ ["set_" + collection.timestamp]: collection },
() => null,
() =>
{
SaveCollectionAsBookmarks(collection);
alert(chrome.i18n.getMessage("olderDataMigrationFailed"));
});
});
localStorage.removeItem("sets");
}
}
function SaveCollectionAsBookmarks (collection)
{
//The id 1 is the browser's bookmark bar
chrome.bookmarks.create({
"parentId": "1",
"title": "TabsAside " + (collection.name ?? new Date(collection.timestamp).toISOString())
},
(collectionFolder) =>
{
for (var i = 0; i < collection.links.length; i++)
chrome.bookmarks.create(
{
"parentId": collectionFolder.id,
"title": collection.titles[i],
"url": collection.links[i]
});
});
}
LoadStorages(MergePreV2Collections);
chrome.storage.onChanged.addListener((changes, namespace) =>
{
if (namespace == "sync")
for (key in changes)
if (key.startsWith("set_"))
{
if (changes[key].newValue)
{
collections[key] = DecompressCollectionsStorage({ [key]: changes[key].newValue })[key];
}
else
delete collections[key];
UpdateBadgeCounter();
chrome.runtime.sendMessage(
{
command: "reloadCollections",
collections: collections,
thumbnails: thumbnails
});
}
});
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((updateInfo) =>
{
if (updateInfo.reason == "update" && updateInfo.previousVersion != chrome.runtime.getManifest()["version"])
chrome.storage.local.set({ "showUpdateBadge": true });
// Adding context menu options, must be done on extension install and update, and probably chrome update as well.
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)
@@ -99,63 +299,55 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) =>
chrome.tabs.create({ url: message.url });
break;
case "loadData":
sendResponse(collections);
break;
LoadStorages(sendResponse); //Sends the collections as a response
return true; //Required to indicate the answer will be sent asynchronously https://developer.chrome.com/extensions/messaging
case "saveTabs":
SaveCollection();
break;
case "restoreTabs":
RestoreCollection(message.collectionIndex, message.removeCollection);
RestoreCollection(message.collectionKey, message.removeCollection);
sendResponse();
break;
case "deleteTabs":
DeleteCollection(message.collectionIndex);
DeleteCollection(message.collectionKey);
sendResponse();
break;
case "removeTab":
RemoveTab(message.collectionIndex, message.tabIndex);
RemoveTab(message.collectionKey, message.tabIndex);
sendResponse();
break;
case "renameCollection":
collections[message.collectionKey].name = message.newName;
UpdateStorages({ [message.collectionKey]: collections[message.collectionKey] });
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()
function UpdateBadgeCounter ()
{
var theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
var iconStatus = collections.length ? "full" : "empty";
var basePath = "icons/" + theme + "/" + iconStatus + "/";
chrome.browserAction.setIcon(
{
path:
{
"128": basePath + "128.png",
"48": basePath + "48.png",
"32": basePath + "32.png",
"16": basePath + "16.png"
}
});
var collectionsLength = Object.keys(collections).length;
// Updating badge counter
if (collections.length < 1)
chrome.browserAction.setBadgeText({ });
else
chrome.browserAction.setBadgeText({ text: collections.length.toString() });
chrome.browserAction.setBadgeText({ text: collectionsLength < 1 ? "" : collectionsLength.toString() });
}
UpdateTheme();
chrome.windows.onFocusChanged.addListener(UpdateTheme);
chrome.tabs.onUpdated.addListener(UpdateTheme);
chrome.tabs.onActivated.addListener(UpdateTheme);
// Set current tabs aside
function SaveCollection()
function SaveCollection ()
{
chrome.tabs.query({ currentWindow: true }, (rawTabs) =>
{
var tabs = rawTabs.filter(i => i.url != chrome.runtime.getURL("TabsAside.html") && !i.pinned && !i.url.includes("//newtab"));
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)
{
@@ -167,47 +359,41 @@ function SaveCollection()
{
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 ?? "")
titles: tabs.map(tab => (tab.title ?? "").substr(0, 100)),
links: tabs.map(tab => tab.url ?? "")
};
var rawData;
if (localStorage.getItem("sets") === null)
rawData = [collection];
else
tabs.forEach(tab => //For each tab to save, Add relevant thumbnails in the thumbnails object
{
rawData = JSON.parse(localStorage.getItem("sets"));
rawData.unshift(collection);
}
thumbnails[tab.url] =
{
"pageCapture": sessionCaptures[tab.url] ?? thumbnails[tab.url]?.pageCapture,
"iconUrl": tab.favIconUrl
};
});
localStorage.setItem("sets", JSON.stringify(rawData));
collections = JSON.parse(localStorage.getItem("sets"));
var newTabId;
UpdateStorages({["set_" + collection.timestamp]: collection}, () =>
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 = tab.id;
chrome.tabs.remove(tabsToSave.filter(i => !i.pinned && i.id != newTabId).map(tab => tab.id));
}),
() => alert(chrome.i18n.getMessage("errorSavingTabs"))
);
}
function DeleteCollection(collectionIndex)
function DeleteCollection (collectionKey)
{
collections = collections.filter(i => i != collections[collectionIndex]);
localStorage.setItem("sets", JSON.stringify(collections));
UpdateTheme();
var deletedUrls = collections[collectionKey].links;
delete collections[collectionKey];
ForEachUnusedUrl(deletedUrls, (url) => delete thumbnails[url]); //We delete the thumbnails that are not used in any other collection.
UpdateStorages({});//Updates the thumbnails storage only, by providing an empty "collectionsToUpdate" object.
collectionStorage.remove(collectionKey);//Remove the collection from the collectionstorage
}
function RestoreCollection(collectionIndex, removeCollection)
function RestoreCollection (collectionKey, removeCollection)
{
collections[collectionIndex].links.forEach(i =>
collections[collectionKey].links.forEach(i =>
{
chrome.tabs.create(
{
@@ -216,14 +402,16 @@ 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)
{
if (updatedTabId === createdTab.id) {
chrome.tabs.onUpdated.removeListener(DiscardTab);
if (!updatedTab.active) {
if (!updatedTab.active)
{
chrome.tabs.discard(updatedTabId);
}
}
@@ -232,53 +420,49 @@ 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;
collections = collections.filter(i => i != collections[collectionIndex]);
localStorage.setItem("sets", JSON.stringify(collections));
UpdateTheme();
DeleteCollection(collectionKey);
}
function RemoveTab(collectionIndex, tabIndex)
function RemoveTab (collectionKey, tabIndex)
{
var set = collections[collectionIndex];
var set = collections[collectionKey];
if (--set.tabsCount < 1)
{
collections = collections.filter(i => i != set);
localStorage.setItem("sets", JSON.stringify(collections));
UpdateTheme();
DeleteCollection(collectionKey);
return;
}
var titles = [];
var links = [];
var icons = [];
var urlToRemove = set.links[tabIndex];
set.titles.splice(tabIndex, 1);
set.links.splice(tabIndex, 1);
for (var i = set.links.length - 1; i >= 0; i--)
{
if (i == tabIndex)
continue;
titles.unshift(set.titles[i]);
links.unshift(set.links[i]);
icons.unshift(set.icons[i]);
}
set.titles = titles;
set.links = links;
set.icons = icons;
localStorage.setItem("sets", JSON.stringify(collections));
UpdateTheme();
ForEachUnusedUrl([urlToRemove], (url) => delete thumbnails[url]);
UpdateStorages({[collectionKey]: set});
}
var thumbnails = [];
/**
* Execute a callback for each url in urlsToFilter that is not in any collection urls
* @param {Array} urlsToFilter array of urls to check
* @param {function} callback callback to execute on an url
*/
function ForEachUnusedUrl (urlsToFilter, callback)
{
for (var i = 0; i < urlsToFilter.length; i++) // If the url of the tab n°i is not present among all the collections, we call the callback on it
if (!Object.values(collections).some(collection => collection.links.some(link => link == urlsToFilter[i])))
callback(urlsToFilter[i]);
}
function AppendThumbnail(tabId, tab)
// page Captures are not always used in a collection, so we keep them in a specific variable until a collection is saved.
var sessionCaptures = { };
function AppendThumbnail (tab)
{
if (!tab.active || !tab.url.startsWith("http"))
return;
@@ -288,31 +472,22 @@ function AppendThumbnail(tabId, tab)
format: "jpeg",
quality: 1
},
(dataUrl) =>
(image) =>
{
if (!dataUrl)
if (!image)
{
console.log("Failed to retrieve thumbnail");
return;
}
console.log("Thumbnail retrieved");
var item = thumbnails.find(i => i.tabId == tabId);
if (item)
item.url = dataUrl;
else
thumbnails.unshift(
{
tabId: tabId,
url: dataUrl
}
);
sessionCaptures[tab.url] = image;
}
);
}
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) =>
{
if (changeInfo.status === "complete")
AppendThumbnail(tabId, tab)
if (changeInfo.status == "complete")
AppendThumbnail(tab);
});
+11
View File
File diff suppressed because one or more lines are too long
+23 -9
View File
@@ -1,6 +1,6 @@
{
"name": "__MSG_name__",
"version": "1.6",
"version": "2.0.3",
"manifest_version": 2,
"description": "__MSG_description__",
"author": "__MSG_author__",
@@ -9,6 +9,7 @@
"permissions":
[
"tabs",
"bookmarks",
"unlimitedStorage",
"storage",
"<all_urls>",
@@ -17,17 +18,20 @@
"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"
"128": "icons/icon-128.png",
"48": "icons/icon-48.png",
"32": "icons/icon-32.png",
"16": "icons/icon-16.png"
},
"browser_action":
{
"default_icon": "icons/icon-32.png"
},
"browser_action": { "default_icon": "icons/light/empty/32.png" },
"web_accessible_resources": [ "*" ],
"background":
{
"scripts": [ "js/background.js" ],
"scripts": [ "js/lib/lzutf8.min.js","js/background.js" ],
"persistent": false
},
@@ -38,7 +42,8 @@
"description": "__MSG_setAside__",
"suggested_key":
{
"default": "Alt+Left"
"default": "Alt+Left",
"mac": "MacCtrl+T"
}
},
"toggle-pane":
@@ -46,8 +51,17 @@
"description": "__MSG_togglePaneContext__",
"suggested_key":
{
"default": "Alt+P"
"default": "Alt+P",
"mac": "Command+Shift+P"
}
}
},
"browser_specific_settings":
{
"gecko":
{
"id": "tabsaside@xfox111.net",
"strict_min_version": "58.0"
}
}
}