├── .eslintignore
├── .stylelintrc.json
├── src
├── fonts
│ ├── icomoon.eot
│ ├── icomoon.ttf
│ ├── icomoon.woff
│ └── icomoon.svg
├── images
│ ├── icon_16.png
│ ├── icon_32.png
│ ├── icon_48.png
│ ├── icon_64.png
│ ├── icon_128.png
│ ├── icon_300.png
│ ├── icon_red.png
│ ├── GitHub-Mark-32px.png
│ ├── GitHub-Mark-64px.png
│ ├── icon_green_dark_128.png
│ ├── icon_green_dark_16.png
│ ├── icon_green_dark_300.png
│ ├── icon_green_dark_32.png
│ ├── icon_green_dark_48.png
│ ├── icon_green_dark_64.png
│ ├── icon_green_light_128.png
│ ├── icon_green_light_16.png
│ ├── icon_green_light_300.png
│ ├── icon_green_light_32.png
│ ├── icon_green_light_48.png
│ ├── icon_green_light_64.png
│ ├── GitHub-Mark-120px-plus.png
│ ├── buy-me-a-coffee-default-yellow.png
│ ├── icon_green_dark.svg
│ ├── icon_red.svg
│ └── icon_green_light.svg
├── js
│ ├── configuration.js
│ ├── constants.js
│ ├── Browser.js
│ ├── utils
│ │ └── index.js
│ ├── pages
│ │ ├── pageAbout.js
│ │ └── pageSettings.js
│ ├── Bookmarks.js
│ └── popup.js
├── css
│ ├── generated_theme
│ │ ├── theme.css
│ │ ├── theme.dark.css
│ │ ├── theme.light.css
│ │ ├── colors.module.css
│ │ ├── typography.module.css
│ │ └── tokens.css
│ ├── loader.css
│ ├── styles.scss
│ └── styles.css
├── templates
│ ├── loader.mustache
│ ├── search-results.mustache
│ ├── about-page.mustache
│ ├── all-bookmarks.mustache
│ ├── search-page.mustache
│ ├── bookmark-card.mustache
│ └── settings-page.mustache
├── popup.html
├── _locales
│ ├── en
│ │ └── messages.json
│ └── uk
│ │ └── messages.json
└── lib
│ └── js
│ └── mustache.min.js
├── store_images
├── badges
│ ├── ms_badge.png
│ ├── amo_badge.png
│ ├── opera_badge.png
│ ├── cws_badge_large_border.png
│ └── cws_badge_small_border.png
├── screenshots
│ ├── ext_screenshot_1.png
│ ├── ext_screenshot_2.png
│ ├── ext_screenshot_3.png
│ ├── chrome_screenshot_1.png
│ ├── chrome_screenshot_1_D.png
│ ├── chrome_screenshot_1_L.png
│ ├── chrome_screenshot_2.png
│ ├── chrome_screenshot_2_D.png
│ ├── chrome_screenshot_2_L.png
│ ├── chrome_screenshot_3.png
│ ├── chrome_screenshot_3_D.png
│ ├── chrome_screenshot_3_L.png
│ ├── chrome_screenshot_4.png
│ ├── chrome_screenshot_4_D.png
│ ├── chrome_screenshot_4_L.png
│ ├── ext_screenshor_edge_3.png
│ ├── ext_screenshot_edge_1.png
│ ├── ext_screenshot_edge_2.png
│ ├── firefox_screenshot_1_D.png
│ ├── firefox_screenshot_1_L.png
│ ├── firefox_screenshot_2_D.png
│ ├── firefox_screenshot_2_L.png
│ ├── firefox_screenshot_3_D.png
│ ├── firefox_screenshot_3_L.png
│ ├── firefox_screenshot_4_D.png
│ ├── firefox_screenshot_4_L.png
│ ├── extension_screenshot_ff_1.png
│ └── extension_screenshot_ff_2.png
└── promo_tiles
│ ├── promo_tile_large.afphoto
│ ├── promo_tile_small.afphoto
│ ├── promo_tile_marquee.afphoto
│ ├── promo_tile_large_920x680.png
│ ├── promo_tile_large_green.afphoto
│ ├── promo_tile_small_440x280.png
│ ├── promo_tile_small_green.afphoto
│ ├── promo_tile_marquee_1400x560.png
│ ├── promo_tile_marquee_green.afphoto
│ ├── promo_tile_large_green_920x680.png
│ ├── promo_tile_small_green_440x280.png
│ └── promo_tile_marquee_green_1400x560.png
├── .gitignore
├── .eslintrc.json
├── set-version.sh
├── .github
└── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
├── package.json
├── LICENSE
├── platform
├── chromium
│ └── manifest.json
└── firefox
│ └── manifest.json
├── pack.sh
├── CHANGELOG.md
├── README.md
└── test_data
└── bookmarks_example.html
/.eslintignore:
--------------------------------------------------------------------------------
1 | src/lib/*
2 | node_modules/*
3 |
--------------------------------------------------------------------------------
/.stylelintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["stylelint-config-standard", "stylelint-config-prettier"]
3 | }
4 |
--------------------------------------------------------------------------------
/src/fonts/icomoon.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/fonts/icomoon.eot
--------------------------------------------------------------------------------
/src/fonts/icomoon.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/fonts/icomoon.ttf
--------------------------------------------------------------------------------
/src/fonts/icomoon.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/fonts/icomoon.woff
--------------------------------------------------------------------------------
/src/images/icon_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/icon_16.png
--------------------------------------------------------------------------------
/src/images/icon_32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/icon_32.png
--------------------------------------------------------------------------------
/src/images/icon_48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/icon_48.png
--------------------------------------------------------------------------------
/src/images/icon_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/icon_64.png
--------------------------------------------------------------------------------
/src/images/icon_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/icon_128.png
--------------------------------------------------------------------------------
/src/images/icon_300.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/icon_300.png
--------------------------------------------------------------------------------
/src/images/icon_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/icon_red.png
--------------------------------------------------------------------------------
/src/images/GitHub-Mark-32px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/GitHub-Mark-32px.png
--------------------------------------------------------------------------------
/src/images/GitHub-Mark-64px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/GitHub-Mark-64px.png
--------------------------------------------------------------------------------
/store_images/badges/ms_badge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/badges/ms_badge.png
--------------------------------------------------------------------------------
/src/images/icon_green_dark_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/icon_green_dark_128.png
--------------------------------------------------------------------------------
/src/images/icon_green_dark_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/icon_green_dark_16.png
--------------------------------------------------------------------------------
/src/images/icon_green_dark_300.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/icon_green_dark_300.png
--------------------------------------------------------------------------------
/src/images/icon_green_dark_32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/icon_green_dark_32.png
--------------------------------------------------------------------------------
/src/images/icon_green_dark_48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/icon_green_dark_48.png
--------------------------------------------------------------------------------
/src/images/icon_green_dark_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/icon_green_dark_64.png
--------------------------------------------------------------------------------
/src/images/icon_green_light_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/icon_green_light_128.png
--------------------------------------------------------------------------------
/src/images/icon_green_light_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/icon_green_light_16.png
--------------------------------------------------------------------------------
/src/images/icon_green_light_300.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/icon_green_light_300.png
--------------------------------------------------------------------------------
/src/images/icon_green_light_32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/icon_green_light_32.png
--------------------------------------------------------------------------------
/src/images/icon_green_light_48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/icon_green_light_48.png
--------------------------------------------------------------------------------
/src/images/icon_green_light_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/icon_green_light_64.png
--------------------------------------------------------------------------------
/store_images/badges/amo_badge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/badges/amo_badge.png
--------------------------------------------------------------------------------
/store_images/badges/opera_badge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/badges/opera_badge.png
--------------------------------------------------------------------------------
/src/images/GitHub-Mark-120px-plus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/GitHub-Mark-120px-plus.png
--------------------------------------------------------------------------------
/src/images/buy-me-a-coffee-default-yellow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/src/images/buy-me-a-coffee-default-yellow.png
--------------------------------------------------------------------------------
/store_images/screenshots/ext_screenshot_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/ext_screenshot_1.png
--------------------------------------------------------------------------------
/store_images/screenshots/ext_screenshot_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/ext_screenshot_2.png
--------------------------------------------------------------------------------
/store_images/screenshots/ext_screenshot_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/ext_screenshot_3.png
--------------------------------------------------------------------------------
/store_images/badges/cws_badge_large_border.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/badges/cws_badge_large_border.png
--------------------------------------------------------------------------------
/store_images/badges/cws_badge_small_border.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/badges/cws_badge_small_border.png
--------------------------------------------------------------------------------
/store_images/promo_tiles/promo_tile_large.afphoto:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/promo_tiles/promo_tile_large.afphoto
--------------------------------------------------------------------------------
/store_images/promo_tiles/promo_tile_small.afphoto:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/promo_tiles/promo_tile_small.afphoto
--------------------------------------------------------------------------------
/store_images/screenshots/chrome_screenshot_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/chrome_screenshot_1.png
--------------------------------------------------------------------------------
/store_images/screenshots/chrome_screenshot_1_D.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/chrome_screenshot_1_D.png
--------------------------------------------------------------------------------
/store_images/screenshots/chrome_screenshot_1_L.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/chrome_screenshot_1_L.png
--------------------------------------------------------------------------------
/store_images/screenshots/chrome_screenshot_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/chrome_screenshot_2.png
--------------------------------------------------------------------------------
/store_images/screenshots/chrome_screenshot_2_D.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/chrome_screenshot_2_D.png
--------------------------------------------------------------------------------
/store_images/screenshots/chrome_screenshot_2_L.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/chrome_screenshot_2_L.png
--------------------------------------------------------------------------------
/store_images/screenshots/chrome_screenshot_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/chrome_screenshot_3.png
--------------------------------------------------------------------------------
/store_images/screenshots/chrome_screenshot_3_D.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/chrome_screenshot_3_D.png
--------------------------------------------------------------------------------
/store_images/screenshots/chrome_screenshot_3_L.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/chrome_screenshot_3_L.png
--------------------------------------------------------------------------------
/store_images/screenshots/chrome_screenshot_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/chrome_screenshot_4.png
--------------------------------------------------------------------------------
/store_images/screenshots/chrome_screenshot_4_D.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/chrome_screenshot_4_D.png
--------------------------------------------------------------------------------
/store_images/screenshots/chrome_screenshot_4_L.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/chrome_screenshot_4_L.png
--------------------------------------------------------------------------------
/store_images/screenshots/ext_screenshor_edge_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/ext_screenshor_edge_3.png
--------------------------------------------------------------------------------
/store_images/screenshots/ext_screenshot_edge_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/ext_screenshot_edge_1.png
--------------------------------------------------------------------------------
/store_images/screenshots/ext_screenshot_edge_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/ext_screenshot_edge_2.png
--------------------------------------------------------------------------------
/store_images/promo_tiles/promo_tile_marquee.afphoto:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/promo_tiles/promo_tile_marquee.afphoto
--------------------------------------------------------------------------------
/store_images/screenshots/firefox_screenshot_1_D.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/firefox_screenshot_1_D.png
--------------------------------------------------------------------------------
/store_images/screenshots/firefox_screenshot_1_L.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/firefox_screenshot_1_L.png
--------------------------------------------------------------------------------
/store_images/screenshots/firefox_screenshot_2_D.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/firefox_screenshot_2_D.png
--------------------------------------------------------------------------------
/store_images/screenshots/firefox_screenshot_2_L.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/firefox_screenshot_2_L.png
--------------------------------------------------------------------------------
/store_images/screenshots/firefox_screenshot_3_D.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/firefox_screenshot_3_D.png
--------------------------------------------------------------------------------
/store_images/screenshots/firefox_screenshot_3_L.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/firefox_screenshot_3_L.png
--------------------------------------------------------------------------------
/store_images/screenshots/firefox_screenshot_4_D.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/firefox_screenshot_4_D.png
--------------------------------------------------------------------------------
/store_images/screenshots/firefox_screenshot_4_L.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/firefox_screenshot_4_L.png
--------------------------------------------------------------------------------
/store_images/promo_tiles/promo_tile_large_920x680.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/promo_tiles/promo_tile_large_920x680.png
--------------------------------------------------------------------------------
/store_images/promo_tiles/promo_tile_large_green.afphoto:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/promo_tiles/promo_tile_large_green.afphoto
--------------------------------------------------------------------------------
/store_images/promo_tiles/promo_tile_small_440x280.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/promo_tiles/promo_tile_small_440x280.png
--------------------------------------------------------------------------------
/store_images/promo_tiles/promo_tile_small_green.afphoto:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/promo_tiles/promo_tile_small_green.afphoto
--------------------------------------------------------------------------------
/store_images/screenshots/extension_screenshot_ff_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/extension_screenshot_ff_1.png
--------------------------------------------------------------------------------
/store_images/screenshots/extension_screenshot_ff_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/screenshots/extension_screenshot_ff_2.png
--------------------------------------------------------------------------------
/store_images/promo_tiles/promo_tile_marquee_1400x560.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/promo_tiles/promo_tile_marquee_1400x560.png
--------------------------------------------------------------------------------
/store_images/promo_tiles/promo_tile_marquee_green.afphoto:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/promo_tiles/promo_tile_marquee_green.afphoto
--------------------------------------------------------------------------------
/store_images/promo_tiles/promo_tile_large_green_920x680.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/promo_tiles/promo_tile_large_green_920x680.png
--------------------------------------------------------------------------------
/store_images/promo_tiles/promo_tile_small_green_440x280.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/promo_tiles/promo_tile_small_green_440x280.png
--------------------------------------------------------------------------------
/store_images/promo_tiles/promo_tile_marquee_green_1400x560.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zaksid/ext-duplicate-bookmarks-finder/HEAD/store_images/promo_tiles/promo_tile_marquee_green_1400x560.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .idea/
3 | .vscode/
4 | node_modules/
5 | build/
6 | *.crx
7 | *.zip
8 |
9 | src/css/material-components-web.css
10 | src/css/styles.css
11 | src/css/styles.css.map
12 |
--------------------------------------------------------------------------------
/src/js/configuration.js:
--------------------------------------------------------------------------------
1 | const Config = {
2 | buyMeACoffeeUrl: 'https://www.buymeacoffee.com/devzaksid',
3 | githubUrl: 'https://github.com/zaksid/ext-duplicate-bookmarks-finder',
4 | };
5 |
6 | export default Config;
7 |
--------------------------------------------------------------------------------
/src/css/generated_theme/theme.css:
--------------------------------------------------------------------------------
1 | @import url(tokens.css);
2 | @import url(colors.module.css);
3 | @import url(typography.module.css);
4 | @import url(theme.light.css) (prefers-color-scheme: light);
5 | @import url(theme.dark.css) (prefers-color-scheme: dark);
6 |
--------------------------------------------------------------------------------
/src/templates/loader.mustache:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/js/constants.js:
--------------------------------------------------------------------------------
1 | export const MainNavBtnIcons = {
2 | APP_MAIN: ' ',
3 | BACK: 'arrow_back',
4 | };
5 |
6 | export const MainNavBtnClasses = {
7 | IS_MENU_CONTENT: 'is-menu-content',
8 | };
9 |
10 | export const Theme = {
11 | DARK: 'dark',
12 | LIGHT: 'light',
13 | SYSTEM: 'system',
14 | };
15 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es2021": true
5 | },
6 | "extends": [
7 | "airbnb-base"
8 | ],
9 | "parserOptions": {
10 | "ecmaVersion": "latest",
11 | "sourceType": "module"
12 | },
13 | "rules": {
14 | "import/extensions": ["error", "always"],
15 | "indent": ["error", 4],
16 | "max-len": ["error", { "code": 100, "comments": 150 }],
17 | "no-plusplus": "off",
18 | "no-use-before-define": "off"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/set-version.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | version=""
4 |
5 | while getopts ":v:" o; do
6 | case "${o}" in
7 | v)
8 | version=${OPTARG}
9 | ;;
10 | *)
11 | ;;
12 | esac
13 | done
14 |
15 | if [[ "$version" == "" ]]
16 | then
17 | echo "⚠️ Please provide version number!"
18 | exit 1
19 | fi
20 |
21 | sed -i '' -r -E "s/(\"version\": \")([0-9]+\.[0-9]+\.?[0-9]?)(\")/\1$version\3/g" platform/chromium/manifest.json
22 | sed -i '' -r -E "s/(\"version\": \")([0-9]+\.[0-9]+\.?[0-9]?)(\")/\1$version\3/g" platform/firefox/manifest.json
23 | sed -i '' -r -E "s/(version-)([0-9]+\.[0-9]+\.?[0-9]?)(\.*)/\1$version\3/g" README.md
24 |
--------------------------------------------------------------------------------
/src/js/Browser.js:
--------------------------------------------------------------------------------
1 | export default class Browser {
2 | constructor(browser) {
3 | this.browser = browser;
4 | this.i18n = browser.i18n;
5 | }
6 |
7 | getBookmarksTree() {
8 | return this.browser.bookmarks.getTree();
9 | }
10 |
11 | getExtensionVersion() {
12 | return this.browser.runtime.getManifest().version;
13 | }
14 |
15 | removeBookmarks(ids = []) {
16 | ids.forEach(async (id) => {
17 | try {
18 | await this.browser.bookmarks.remove(id);
19 | } catch (e) {
20 | console.error(`Cannot delete bookmark with id ${id}: ${e}`);
21 | }
22 | });
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/src/js/utils/index.js:
--------------------------------------------------------------------------------
1 | export function getDefaultExtensionSettings() {
2 | return {
3 | showSeparators: false,
4 | theme: 'system',
5 | ignoredUrls: [],
6 | };
7 | }
8 |
9 | export async function getExtensionSettings(browserInstance) {
10 | const defaults = getDefaultExtensionSettings();
11 | const userSettings = await browserInstance.browser.storage.local.get();
12 |
13 | return Object.keys(userSettings).length ? userSettings : defaults;
14 | }
15 |
16 | export async function setExtensionSettings(browserInstance, userSettings) {
17 | await browserInstance.browser.storage.local.set({
18 | ...userSettings,
19 | });
20 | }
21 |
22 | export function isSeparator(url) {
23 | return /data:/.test(url);
24 | }
25 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help me improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. See error
18 |
19 | **Expected behavior**
20 | A clear and concise description of what you expected to happen.
21 |
22 | **Screenshots**
23 | If applicable, add screenshots to help explain your problem.
24 |
25 | **Desktop (please complete the following information):**
26 | - Browser [e.g. chrome, firefox]
27 | - Browser version
28 | - Extension version
29 |
30 | **Additional context**
31 | Add any other context about the problem here.
32 |
--------------------------------------------------------------------------------
/src/templates/search-results.mustache:
--------------------------------------------------------------------------------
1 | {{# isLoader }}
2 |
3 | {{> loader }}
4 |
5 | {{/ isLoader }}
6 |
7 | {{^ isLoader }}
8 |
9 |
10 | {{i18n.msg_FoundQty}}
11 |
12 |
13 |
14 |
29 | {{/ isLoader }}
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "duplicate-bookmarks-finder",
3 | "version": "1.0.1",
4 | "description": "Find and delete duplicate bookmarks in browser",
5 | "author": "Oleksandr Mahdybor",
6 | "license": "MIT",
7 | "devDependencies": {
8 | "conventional-changelog-cli": "^2.2.2",
9 | "eslint": "^8.21.0",
10 | "eslint-config-airbnb": "^19.0.4",
11 | "eslint-config-airbnb-base": "^15.0.0",
12 | "eslint-plugin-import": "^2.26.0",
13 | "prettier": "^1.19.1",
14 | "prettier-airbnb-config": "^1.0.0",
15 | "sass": "^1.63.6",
16 | "stylelint": "^14.9.1",
17 | "stylelint-config-prettier": "^9.0.3",
18 | "stylelint-config-standard": "^26.0.0"
19 | },
20 | "prettier": "prettier-airbnb-config",
21 | "scripts": {
22 | "lint": "npm run lint:css & npm run lint:js",
23 | "lint:js": "eslint ./src",
24 | "lint:css": "stylelint \"./src/**/*.css\"",
25 | "sass": "sass --watch ./src/css/styles.scss ./src/css/styles.css",
26 | "version": "conventional-changelog -i CHANGELOG.md -s && git add CHANGELOG.md"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/templates/about-page.mustache:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
{{extensionName}}
8 |
{{i18n.txt_version}} {{version}}
9 |
10 |
11 |
31 |
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Oleksandr Mahdybor
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/images/icon_green_dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
9 |
11 |
15 |
16 |
--------------------------------------------------------------------------------
/src/templates/all-bookmarks.mustache:
--------------------------------------------------------------------------------
1 | Found {{bookmarks.length}} bookmarks
2 |
3 |
4 | {{# bookmarks }}
5 |
6 |
7 |
8 |
9 |
24 |
25 |
26 |
27 |
28 | {{index}}) {{title}}
29 |
30 | {{url}}
31 |
32 |
33 | {{myPath}}
34 |
35 |
36 |
37 | {{/ bookmarks }}
38 |
39 |
--------------------------------------------------------------------------------
/src/css/loader.css:
--------------------------------------------------------------------------------
1 | /* stylelint-disable number-max-precision */
2 |
3 | /* generated by https://loading.io/ */
4 |
5 | @keyframes ldio-5y59izw6pev {
6 | 0% { transform: translate(2.37px,2.37px) }
7 | 33.33% { transform: translate(120.87px,2.37px) }
8 | 66.66% { transform: translate(49.77px,120.87px) }
9 | 100% { transform: translate(2.37px,2.37px) }
10 | }
11 |
12 | .ldio-5y59izw6pev div {
13 | box-sizing: content-box;
14 | }
15 |
16 | .ldio-5y59izw6pev > div {
17 | transform: scale(0.42);
18 | transform-origin: 118.5px 118.5px;
19 | }
20 |
21 | .ldio-5y59izw6pev > div > div {
22 | animation: ldio-5y59izw6pev 1.282051282051282s linear infinite;
23 | position: absolute;
24 | }
25 |
26 | .ldio-5y59izw6pev > div > div div:nth-child(1) {
27 | width: 85.32000000000001px;
28 | height: 85.32000000000001px;
29 | border-radius: 50%;
30 | border: 14.22px solid var(--mdc-theme-primary);
31 | }
32 |
33 | .ldio-5y59izw6pev > div > div div:nth-child(2) {
34 | width: 20.145px;
35 | height: 60.435px;
36 | transform: rotate(-45deg);
37 | background: var(--mdc-theme-primary);
38 | border-radius: 0 0 9.48px 9.48px;
39 | position: absolute;
40 | top: 80.58px;
41 | left: 100.72500000000001px;
42 | }
43 |
44 | .loadingio-spinner-magnify-y6n6336d04m {
45 | width: 237px;
46 | height: 237px;
47 | display: inline-block;
48 | overflow: hidden;
49 | background: none;
50 | }
51 |
52 | .ldio-5y59izw6pev {
53 | width: 100%;
54 | height: 100%;
55 | position: relative;
56 | transform: translateZ(0) scale(1);
57 | backface-visibility: hidden;
58 | transform-origin: 0 0; /* see note above */
59 | }
60 |
61 | #loader-wrapper {
62 | display: flex;
63 | align-content: center;
64 | justify-content: center;
65 | }
66 |
--------------------------------------------------------------------------------
/platform/chromium/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 3,
3 | "name": "__MSG_extensionName__",
4 | "description": "__MSG_extensionDescription__",
5 | "version": "1.0.1",
6 | "author": "Oleksandr Mahdybor",
7 | "homepage_url": "https://github.com/zaksid/ext-duplicate-bookmarks-finder",
8 | "permissions": [
9 | "bookmarks",
10 | "storage"
11 | ],
12 | "action": {
13 | "browser_style": true,
14 | "default_popup": "popup.html",
15 | "default_title": "__MSG_extensionName__",
16 | "default_icon": {
17 | "16": "/images/icon_green_dark_16.png",
18 | "32": "/images/icon_green_dark_32.png",
19 | "48": "/images/icon_green_dark_48.png",
20 | "128": "/images/icon_green_dark_128.png"
21 | },
22 | "theme_icons": [{
23 | "dark": "/images/icon_green_light_16.png",
24 | "light": "/images/icon_green_dark_16.png",
25 | "size": 16
26 | }, {
27 | "dark": "/images/icon_green_light_32.png",
28 | "light": "/images/icon_green_dark_32.png",
29 | "size": 32
30 | }, {
31 | "dark": "/images/icon_green_light_48.png",
32 | "light": "/images/icon_green_dark_48.png",
33 | "size": 48
34 | }, {
35 | "dark": "/images/icon_green_light_128.png",
36 | "light": "/images/icon_green_dark_128.png",
37 | "size": 128
38 | }]
39 | },
40 | "icons": {
41 | "16": "/images/icon_green_dark_16.png",
42 | "32": "/images/icon_green_dark_32.png",
43 | "48": "/images/icon_green_dark_48.png",
44 | "128": "/images/icon_green_dark_128.png"
45 | },
46 | "default_locale": "en"
47 | }
48 |
--------------------------------------------------------------------------------
/platform/firefox/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 3,
3 | "name": "__MSG_extensionName__",
4 | "description": "__MSG_extensionDescription__",
5 | "version": "1.0.1",
6 | "author": "Oleksandr Mahdybor",
7 | "homepage_url": "https://github.com/zaksid/ext-duplicate-bookmarks-finder",
8 | "permissions": [
9 | "bookmarks",
10 | "storage"
11 | ],
12 | "browser_specific_settings": {
13 | "gecko": {
14 | "id": "{8dc9d389-4f2c-4f53-909a-7041d9442fa9}"
15 | }
16 | },
17 | "action": {
18 | "default_popup": "popup.html",
19 | "default_title": "__MSG_extensionName__",
20 | "default_icon": {
21 | "16": "/images/icon_green_dark_16.png",
22 | "32": "/images/icon_green_dark_32.png",
23 | "48": "/images/icon_green_dark_48.png",
24 | "128": "/images/icon_green_dark_128.png"
25 | },
26 | "theme_icons": [{
27 | "dark": "/images/icon_green_light_16.png",
28 | "light": "/images/icon_green_dark_16.png",
29 | "size": 16
30 | }, {
31 | "dark": "/images/icon_green_light_32.png",
32 | "light": "/images/icon_green_dark_32.png",
33 | "size": 32
34 | }, {
35 | "dark": "/images/icon_green_light_48.png",
36 | "light": "/images/icon_green_dark_48.png",
37 | "size": 48
38 | }, {
39 | "dark": "/images/icon_green_light_128.png",
40 | "light": "/images/icon_green_dark_128.png",
41 | "size": 128
42 | }]
43 | },
44 | "icons": {
45 | "16": "/images/icon_green_dark_16.png",
46 | "32": "/images/icon_green_dark_32.png",
47 | "48": "/images/icon_green_dark_48.png",
48 | "128": "/images/icon_green_dark_128.png"
49 | },
50 | "default_locale": "en"
51 | }
52 |
--------------------------------------------------------------------------------
/src/js/pages/pageAbout.js:
--------------------------------------------------------------------------------
1 | /* global Mustache */
2 |
3 | import Config from '../configuration.js';
4 | import { MainNavBtnIcons, MainNavBtnClasses } from '../constants.js';
5 |
6 | export default async function initAboutPage(browserInstance) {
7 | const appBarNavBtn = document.querySelector('#app-bar-nav-btn');
8 | const appBarTitle = document.querySelector('#app-bar-title');
9 |
10 | appBarNavBtn.innerHTML = MainNavBtnIcons.BACK;
11 | appBarNavBtn.classList.add(MainNavBtnClasses.IS_MENU_CONTENT);
12 |
13 | appBarTitle.innerHTML = browserInstance.i18n.getMessage('menu_about');
14 |
15 | const aboutTemplateResponse = await fetch('templates/about-page.mustache');
16 | const aboutTemplate = await aboutTemplateResponse.text();
17 |
18 | document.querySelector('#main-content').innerHTML = Mustache.render(aboutTemplate, {
19 | extensionName: browserInstance.i18n.getMessage('extensionName'),
20 | version: browserInstance.getExtensionVersion(),
21 | buyMeACoffeeUrl: Config.buyMeACoffeeUrl,
22 | githubUrl: Config.githubUrl,
23 | i18n: {
24 | alt_extIcon: browserInstance.i18n.getMessage('alt_extIcon'),
25 | txt_version: browserInstance.i18n.getMessage('txt_version'),
26 | },
27 | });
28 |
29 | const isFirefox = navigator.userAgent.includes('Firefox');
30 | if (isFirefox) {
31 | // For some reason Firefox doesn't close pop-up when navigating via external links.
32 | // When using just `window.close()` the pop-up is closed but the link is opened in a new window instead af a new tab.
33 | // Need to manually open the link and then close the pop-up.
34 | document.querySelectorAll('.external-link').forEach((elem) => {
35 | elem.addEventListener('click', (event) => {
36 | event.preventDefault();
37 |
38 | const { currentTarget } = event;
39 |
40 | window.open(currentTarget.href, currentTarget.target, 'noopener,noreferrer');
41 | window.close();
42 | });
43 | });
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/css/generated_theme/theme.dark.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --md-sys-color-primary: var(--md-sys-color-primary-dark);
3 | --md-sys-color-on-primary: var(--md-sys-color-on-primary-dark);
4 | --md-sys-color-primary-container: var(--md-sys-color-primary-container-dark);
5 | --md-sys-color-on-primary-container: var(--md-sys-color-on-primary-container-dark);
6 | --md-sys-color-secondary: var(--md-sys-color-secondary-dark);
7 | --md-sys-color-on-secondary: var(--md-sys-color-on-secondary-dark);
8 | --md-sys-color-secondary-container: var(--md-sys-color-secondary-container-dark);
9 | --md-sys-color-on-secondary-container: var(--md-sys-color-on-secondary-container-dark);
10 | --md-sys-color-tertiary: var(--md-sys-color-tertiary-dark);
11 | --md-sys-color-on-tertiary: var(--md-sys-color-on-tertiary-dark);
12 | --md-sys-color-tertiary-container: var(--md-sys-color-tertiary-container-dark);
13 | --md-sys-color-on-tertiary-container: var(--md-sys-color-on-tertiary-container-dark);
14 | --md-sys-color-error: var(--md-sys-color-error-dark);
15 | --md-sys-color-error-container: var(--md-sys-color-error-container-dark);
16 | --md-sys-color-on-error: var(--md-sys-color-on-error-dark);
17 | --md-sys-color-on-error-container: var(--md-sys-color-on-error-container-dark);
18 | --md-sys-color-background: var(--md-sys-color-background-dark);
19 | --md-sys-color-on-background: var(--md-sys-color-on-background-dark);
20 | --md-sys-color-surface: var(--md-sys-color-surface-dark);
21 | --md-sys-color-on-surface: var(--md-sys-color-on-surface-dark);
22 | --md-sys-color-surface-variant: var(--md-sys-color-surface-variant-dark);
23 | --md-sys-color-on-surface-variant: var(--md-sys-color-on-surface-variant-dark);
24 | --md-sys-color-outline: var(--md-sys-color-outline-dark);
25 | --md-sys-color-inverse-on-surface: var(--md-sys-color-inverse-on-surface-dark);
26 | --md-sys-color-inverse-surface: var(--md-sys-color-inverse-surface-dark);
27 | --md-sys-color-inverse-primary: var(--md-sys-color-inverse-primary-dark);
28 | --md-sys-color-shadow: var(--md-sys-color-shadow-dark);
29 | --md-sys-color-surface-tint: var(--md-sys-color-surface-tint-dark);
30 | --md-sys-color-outline-variant: var(--md-sys-color-outline-variant-dark);
31 | --md-sys-color-scrim: var(--md-sys-color-scrim-dark);
32 | }
33 |
--------------------------------------------------------------------------------
/src/css/generated_theme/theme.light.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --md-sys-color-primary: var(--md-sys-color-primary-light);
3 | --md-sys-color-on-primary: var(--md-sys-color-on-primary-light);
4 | --md-sys-color-primary-container: var(--md-sys-color-primary-container-light);
5 | --md-sys-color-on-primary-container: var(--md-sys-color-on-primary-container-light);
6 | --md-sys-color-secondary: var(--md-sys-color-secondary-light);
7 | --md-sys-color-on-secondary: var(--md-sys-color-on-secondary-light);
8 | --md-sys-color-secondary-container: var(--md-sys-color-secondary-container-light);
9 | --md-sys-color-on-secondary-container: var(--md-sys-color-on-secondary-container-light);
10 | --md-sys-color-tertiary: var(--md-sys-color-tertiary-light);
11 | --md-sys-color-on-tertiary: var(--md-sys-color-on-tertiary-light);
12 | --md-sys-color-tertiary-container: var(--md-sys-color-tertiary-container-light);
13 | --md-sys-color-on-tertiary-container: var(--md-sys-color-on-tertiary-container-light);
14 | --md-sys-color-error: var(--md-sys-color-error-light);
15 | --md-sys-color-error-container: var(--md-sys-color-error-container-light);
16 | --md-sys-color-on-error: var(--md-sys-color-on-error-light);
17 | --md-sys-color-on-error-container: var(--md-sys-color-on-error-container-light);
18 | --md-sys-color-background: var(--md-sys-color-background-light);
19 | --md-sys-color-on-background: var(--md-sys-color-on-background-light);
20 | --md-sys-color-surface: var(--md-sys-color-surface-light);
21 | --md-sys-color-on-surface: var(--md-sys-color-on-surface-light);
22 | --md-sys-color-surface-variant: var(--md-sys-color-surface-variant-light);
23 | --md-sys-color-on-surface-variant: var(--md-sys-color-on-surface-variant-light);
24 | --md-sys-color-outline: var(--md-sys-color-outline-light);
25 | --md-sys-color-inverse-on-surface: var(--md-sys-color-inverse-on-surface-light);
26 | --md-sys-color-inverse-surface: var(--md-sys-color-inverse-surface-light);
27 | --md-sys-color-inverse-primary: var(--md-sys-color-inverse-primary-light);
28 | --md-sys-color-shadow: var(--md-sys-color-shadow-light);
29 | --md-sys-color-surface-tint: var(--md-sys-color-surface-tint-light);
30 | --md-sys-color-outline-variant: var(--md-sys-color-outline-variant-light);
31 | --md-sys-color-scrim: var(--md-sys-color-scrim-light);
32 | }
33 |
--------------------------------------------------------------------------------
/src/templates/search-page.mustache:
--------------------------------------------------------------------------------
1 |
9 |
10 |
18 |
19 |
20 |
21 |
22 |
23 | {{> loader }}
24 |
25 |
26 |
27 |
34 |
35 |
36 |
37 |
43 |
44 | {{i18n.bookmarksDeleteConfirmationTitle}}
45 |
46 |
{{i18n.bookmarksDeleteConfirmation}}
47 |
48 |
49 |
50 | {{i18n.btn_cancel}}
51 |
52 |
53 |
54 | {{i18n.btn_delete}}
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/pack.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | help="
4 | \n
5 | Usage \n
6 | \t bash pack.sh \n\n
7 | Example \n
8 | \t bash pack.sh -p firefox -m dev \n\n
9 | Options \n
10 | \t -p \t\t platform - Platform to build for. \n
11 | \t\t\t Possible values: 'firefox', 'opera'. Default (param not provided) 'chromium'. \n\n
12 | \t -m \t\t mode - Packing mode. \n
13 | \t\t\t If 'dev' is specified - development mode. Create only build folder, don't zip. \n
14 | \t\t\t Otherwise mode=publishing (create zip, remove build folder).
15 | "
16 |
17 | platform="chromium"
18 | mode="publishing"
19 | is_help=false
20 |
21 | while getopts ":p:m:h" o; do
22 | case "${o}" in
23 | p)
24 | arg=${OPTARG}
25 |
26 | if [[ "$arg" == "firefox" ]]; then
27 | platform="firefox"
28 | elif [[ "$arg" == "opera" ]]; then
29 | platform="opera"
30 | fi
31 |
32 | echo -e "> Platform: ${platform}"
33 | ;;
34 | m)
35 | arg=${OPTARG}
36 |
37 | if [[ "$arg" == "dev" ]]; then
38 | mode="development"
39 | fi
40 |
41 | echo -e "> Mode: ${mode}"
42 | ;;
43 | h)
44 | is_help=true
45 | ;;
46 | *)
47 | ;;
48 | esac
49 | done
50 |
51 | if [[ "$is_help" = true ]]; then
52 | echo -e ${help}
53 | exit 0
54 | fi
55 |
56 | if [[ -d "build" ]]; then
57 | rm -rf build
58 | fi
59 |
60 | mkdir build
61 | cp -r ./src/* build/
62 | rm -f build/manifest*.*
63 | rm build/css/*.scss
64 | rm build/css/*.map
65 |
66 | if [[ "$platform" == "opera" ]]; then
67 | cp ./platform/chromium/manifest.json build
68 | else
69 | cp ./platform/${platform}/manifest.json build
70 | fi
71 |
72 | if [[ "$platform" == "opera" ]]; then
73 | echo -e "> Renaming mustashe templates for Opera..."
74 | find build/templates/ -name '*.mustache' -exec sh -c 'mv "$0" "${0%.mustache}.mustache.html"' {} \;
75 | sed -i '' -r -E "s/\.mustache/\.mustache\.html/g" build/js/popup.js
76 | echo -e "> ...done"
77 | fi
78 |
79 | #if [[ "$mode" == "development" ]]; then
80 | cd build
81 | zip -r extension-${platform}.zip *
82 | mv extension-${platform}.zip ../
83 | cd ../
84 |
85 | if [[ "$platform" == "opera" ]]; then
86 | mv extension-${platform}.zip extension-${platform}.crx
87 | fi
88 | #fi
89 |
90 | if [[ "$mode" == "publishing" ]]; then
91 | rm -rf build
92 | fi
93 |
--------------------------------------------------------------------------------
/src/images/icon_red.svg:
--------------------------------------------------------------------------------
1 |
2 |
13 |
15 |
34 |
38 |
39 |
--------------------------------------------------------------------------------
/src/templates/bookmark-card.mustache:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{url}}
4 |
{{i18n.txt_cardMatchNo}}
5 |
6 |
7 |
8 |
9 | {{#items}}
10 |
11 |
12 |
13 |
14 |
29 |
30 |
31 |
32 |
33 | {{title}}
34 | {{#url}}
35 |
38 | {{/url}}
39 |
40 |
41 | {{i18n.txt_path}}
42 | {{myPath}}
43 |
44 |
45 |
46 | {{/items}}
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/src/images/icon_green_light.svg:
--------------------------------------------------------------------------------
1 |
2 |
16 |
18 |
37 |
41 |
42 |
--------------------------------------------------------------------------------
/src/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/src/fonts/icomoon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Generated by IcoMoon
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/js/Bookmarks.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-continue */
2 |
3 | import { getExtensionSettings, isSeparator } from './utils/index.js';
4 |
5 | export default class Bookmarks {
6 | constructor(bookmarksTree) {
7 | this.processedBookmarks = [];
8 | this.#prepareBookmarks(bookmarksTree);
9 | }
10 |
11 | #prepareBookmarks(bookmarksInput, path = '') {
12 | // Make a copy of an array to work with
13 | const bookmarks = bookmarksInput.slice();
14 |
15 | for (let i = 0; i < bookmarks.length; i++) {
16 | const bookmark = bookmarks[i];
17 |
18 | if (bookmark.url) {
19 | if (path) {
20 | if (!('myPath' in bookmark)) {
21 | bookmark.myPath = '';
22 | }
23 | bookmark.myPath += `${path}/`;
24 | }
25 | this.processedBookmarks.push(bookmark);
26 | }
27 |
28 | if (bookmark.children) {
29 | this.#prepareBookmarks(bookmark.children, `${path ? `${path}/` : ''}${bookmark.title}`);
30 | }
31 | }
32 | }
33 |
34 | getBookmarks() {
35 | return this.processedBookmarks;
36 | }
37 |
38 | async getDuplicates(browserInstance) {
39 | const array = this.processedBookmarks.slice();
40 | const matches = {};
41 | const httpsRegex = /http(s)?/;
42 | const settings = await getExtensionSettings(browserInstance);
43 |
44 | const isIgnored = (url) => {
45 | if ('ignoredUrls' in settings && settings.ignoredUrls.length) {
46 | const matchesIgnoredPattern = settings.ignoredUrls.some((pattern) => {
47 | const regex = new RegExp(pattern);
48 | return regex.test(url);
49 | });
50 |
51 | if (matchesIgnoredPattern) {
52 | return true;
53 | }
54 | }
55 |
56 | if (settings && 'showSeparators' in settings) {
57 | if (settings.showSeparators) {
58 | return false;
59 | }
60 | return isSeparator(url);
61 | }
62 |
63 | return false;
64 | };
65 |
66 | for (let i = 0; i < array.length; i++) {
67 | const currentElem = array[i];
68 | let wasMatched = false;
69 |
70 | if (isIgnored(currentElem.url)) {
71 | continue;
72 | }
73 |
74 | for (let j = i + 1; j < array.length; j++) {
75 | const url1 = currentElem.url.replace(httpsRegex);
76 | const url2 = array[j].url.replace(httpsRegex);
77 |
78 | if (url1 === url2) {
79 | if (!(i in matches)) {
80 | matches[i] = [];
81 | }
82 | matches[i].push(array[j]);
83 | array.splice(j, 1);
84 | wasMatched = true;
85 | j--; // adjust due to deleted element
86 | }
87 | }
88 |
89 | if (wasMatched) {
90 | matches[i].unshift(currentElem);
91 | }
92 | }
93 |
94 | return Object.values(matches);
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/_locales/en/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "extensionName": {
3 | "message": "Duplicate Bookmarks Finder",
4 | "description": "Name of the extension."
5 | },
6 | "extensionDescription": {
7 | "message": "Find and delete duplicate bookmarks.",
8 | "description": "Description of the extension."
9 | },
10 | "extensionDescriptionLong": {
11 | "message": "This extension allows to find and delete duplicate bookmarks.",
12 | "description": "Long description of the extension."
13 | },
14 | "btn_add": {
15 | "message": "Add"
16 | },
17 | "btn_findDuplicates": {
18 | "message": "Find"
19 | },
20 | "btn_cancel": {
21 | "message": "Cancel"
22 | },
23 | "btn_delete": {
24 | "message": "Delete"
25 | },
26 | "btn_deleteSelected": {
27 | "message": "Delete selected"
28 | },
29 | "btn_resetSettings": {
30 | "message": "Reset to defaults"
31 | },
32 | "bookmarksDeleteConfirmationTitle": {
33 | "message": "Delete selected bookmarks?"
34 | },
35 | "bookmarksDeleteConfirmation": {
36 | "message": "This action cannot be undone!"
37 | },
38 | "bookmarksDeletedMsg": {
39 | "message": "Selected bookmarks have been deleted."
40 | },
41 | "input_hint_ignoredUrlRegex": {
42 | "message": "Ignored URLs regex"
43 | },
44 | "menu_about": {
45 | "message": "About"
46 | },
47 | "menu_settings": {
48 | "message": "Settings"
49 | },
50 | "msgFoundQty": {
51 | "message": "Found $QTY$ duplicates",
52 | "placeholders": {
53 | "qty": {
54 | "content": "$1",
55 | "example": "0"
56 | }
57 | }
58 | },
59 | "msgFoundNone": {
60 | "message": "🥳 No duplicates found!"
61 | },
62 | "settingTheme": {
63 | "message": "Theme"
64 | },
65 | "settingThemeDark": {
66 | "message": "Dark"
67 | },
68 | "settingThemeLight": {
69 | "message": "Light"
70 | },
71 | "settingThemeSystem": {
72 | "message": "OS Default"
73 | },
74 | "settingIgnoredPatterns": {
75 | "message": "Ignored URL patterns"
76 | },
77 | "settingIgnoredPatternsDescription": {
78 | "message": "Patterns of URLs that should be ignored (RegExp)"
79 | },
80 | "settingShowSeparators": {
81 | "message": "Show separators"
82 | },
83 | "settingShowSeparatorsDescription": {
84 | "message": "Show bookmarks separators in search results"
85 | },
86 | "txt_cardMatchNo": {
87 | "message": "Match $NO$",
88 | "placeholders": {
89 | "no": {
90 | "content": "$1",
91 | "example": "1"
92 | }
93 | }
94 | },
95 | "alt_extIcon": {
96 | "message": "Extension icon"
97 | },
98 | "txt_path": {
99 | "message": "Path:"
100 | },
101 | "txt_separator": {
102 | "message": "Separator"
103 | },
104 | "txt_separators": {
105 | "message": "Separators"
106 | },
107 | "txt_version": {
108 | "message": "Version: "
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 1.0.1 (2024-01-25)
2 |
3 | * Fixes #49: Remove "Buy Me A Coffee" script to Comply with Chrome Web Store Policy (#51) ([dc4cc3d](https://github.com/zaksid/ext-duplicate-bookmarks-finder/commit/dc4cc3d)), closes [#49](https://github.com/zaksid/ext-duplicate-bookmarks-finder/issues/49) [#51](https://github.com/zaksid/ext-duplicate-bookmarks-finder/issues/51)
4 | * Fixes #50: Firefox doesn't close pop-up when navigating via external link (#52) ([5833b4e](https://github.com/zaksid/ext-duplicate-bookmarks-finder/commit/5833b4e)), closes [#50](https://github.com/zaksid/ext-duplicate-bookmarks-finder/issues/50) [#52](https://github.com/zaksid/ext-duplicate-bookmarks-finder/issues/52)
5 |
6 |
7 | ## 1.0.0 (2023-06-30)
8 |
9 | * Added dark mode. Restyled with Material You ([ed1039a](https://github.com/zaksid/ext-duplicate-bookmarks-finder/commit/ed1039a))
10 |
11 |
12 | ## 0.3.0 (2022-12-09)
13 |
14 | * 🛠 Fixes #24: Top App Bar is not fixed (#25) ([85f7dc1](https://github.com/zaksid/ext-duplicate-bookmarks-finder/commit/85f7dc1)), closes [#24](https://github.com/zaksid/ext-duplicate-bookmarks-finder/issues/24)
15 | * Resolve #38 Add extension settings. Fixes #37 Bookmark separators are included in duplicates ([5e1606d](https://github.com/zaksid/ext-duplicate-bookmarks-finder/commit/5e1606d)), closes [#38](https://github.com/zaksid/ext-duplicate-bookmarks-finder/issues/38) [#37](https://github.com/zaksid/ext-duplicate-bookmarks-finder/issues/37)
16 |
17 | ## 0.2.0 (2022-08-09)
18 | * 🛠 Updated project structure and build process ([c51869f](https://github.com/zaksid/ext-duplicate-bookmarks-finder/commit/c51869f))
19 |
20 | ## 0.1.4 (2022-08-07)
21 | * 📦 Resolve #9: Create sh script to zip necessary files for publishing (#11) ([67ee736](https://github.com/zaksid/ext-duplicate-bookmarks-finder/commit/67ee736)), closes [#9](https://github.com/zaksid/ext-duplicate-bookmarks-finder/issues/9) [#11](https://github.com/zaksid/ext-duplicate-bookmarks-finder/issues/11)
22 | * 🚑 Resolve #7: Add eslint / stylelint (#10) ([9fa2558](https://github.com/zaksid/ext-duplicate-bookmarks-finder/commit/9fa2558)), closes [#7](https://github.com/zaksid/ext-duplicate-bookmarks-finder/issues/7) [#10](https://github.com/zaksid/ext-duplicate-bookmarks-finder/issues/10)
23 |
24 | ## 0.1.3 (2022-08-07)
25 | * 🛠 Fixes #2: Remove empty card after deleting bookmarks (#6) ([7cf2120](https://github.com/zaksid/ext-duplicate-bookmarks-finder/commit/7cf2120)), closes [#2](https://github.com/zaksid/ext-duplicate-bookmarks-finder/issues/2) [#6](https://github.com/zaksid/ext-duplicate-bookmarks-finder/issues/6)
26 |
27 | ## 0.1.2 (2022-08-07)
28 | * Fixes #3: Menu toggle on menu icon click (#5) ([829d3a5](https://github.com/zaksid/ext-duplicate-bookmarks-finder/commit/829d3a5)), closes [#3](https://github.com/zaksid/ext-duplicate-bookmarks-finder/issues/3) [#5](https://github.com/zaksid/ext-duplicate-bookmarks-finder/issues/5)
29 |
30 | ## 0.1.1 (2022-08-07)
31 | * Refactor (move classes to modules). Added loader (#4) ([5e05056](https://github.com/zaksid/ext-duplicate-bookmarks-finder/commit/5e05056)), closes [#4](https://github.com/zaksid/ext-duplicate-bookmarks-finder/issues/4)
32 |
33 | ## 0.1 (2022-08-01)
34 | * 🎉 Initial commit ([0d2cbd1](https://github.com/zaksid/ext-duplicate-bookmarks-finder/commit/0d2cbd1))
35 |
--------------------------------------------------------------------------------
/src/_locales/uk/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "extensionName": {
3 | "message": "Пошук дублікатів закладок",
4 | "description": "Name of the extension."
5 | },
6 | "extensionDescription": {
7 | "message": "Пошук і видалення дублікатів закладок.",
8 | "description": "Description of the extension."
9 | },
10 | "extensionDescriptionLong": {
11 | "message": "Це розширення дозволяє легко знайти та видалити дублікати закладок у браузер.",
12 | "description": "Long description of the extension."
13 | },
14 | "btn_add": {
15 | "message": "Додати"
16 | },
17 | "btn_findDuplicates": {
18 | "message": "Пошук"
19 | },
20 | "btn_cancel": {
21 | "message": "Скасувати"
22 | },
23 | "btn_delete": {
24 | "message": "Видалити"
25 | },
26 | "btn_deleteSelected": {
27 | "message": "Видалити вибрані"
28 | },
29 | "btn_resetSettings": {
30 | "message": "Скинути до стандартних налаштуваннь"
31 | },
32 | "bookmarksDeleteConfirmationTitle": {
33 | "message": "Видалити вибрані закладки?"
34 | },
35 | "bookmarksDeleteConfirmation": {
36 | "message": "Цю дію не можна відмінити!"
37 | },
38 | "bookmarksDeletedMsg": {
39 | "message": "Вибрані закладки було видалено."
40 | },
41 | "input_hint_ignoredUrlRegex": {
42 | "message": "Регулярка для ігнорування закладок"
43 | },
44 | "menu_about": {
45 | "message": "Про розширення"
46 | },
47 | "menu_settings": {
48 | "message": "Налаштування"
49 | },
50 | "msgFoundQty": {
51 | "message": "Знайдено дублікатів: $QTY$",
52 | "placeholders": {
53 | "qty": {
54 | "content": "$1",
55 | "example": "0"
56 | }
57 | }
58 | },
59 | "msgFoundNone": {
60 | "message": "🥳 Дублікатів не знайдено!"
61 | },
62 | "settingTheme": {
63 | "message": "Тема"
64 | },
65 | "settingThemeDark": {
66 | "message": "Темна"
67 | },
68 | "settingThemeLight": {
69 | "message": "Світла"
70 | },
71 | "settingThemeSystem": {
72 | "message": "Як на пристрої"
73 | },
74 | "settingIgnoredPatterns": {
75 | "message": "Ігноровані URL адреси"
76 | },
77 | "settingIgnoredPatternsDescription": {
78 | "message": "Зразки URLs адрес (регулярки) які ігноруватимуться при пошуку"
79 | },
80 | "settingShowSeparators": {
81 | "message": "Показувати роздільники"
82 | },
83 | "settingShowSeparatorsDescription": {
84 | "message": "Показувати роздільники закладок під час пошуку"
85 | },
86 | "txt_cardMatchNo": {
87 | "message": "Співпадіння $NO$",
88 | "placeholders": {
89 | "no": {
90 | "content": "$1",
91 | "example": "1"
92 | }
93 | }
94 | },
95 | "alt_extIcon": {
96 | "message": "Іконка розширення"
97 | },
98 | "txt_path": {
99 | "message": "Шлях:"
100 | },
101 | "txt_separator": {
102 | "message": "Роздільник"
103 | },
104 | "txt_separators": {
105 | "message": "Роздільники"
106 | },
107 | "txt_version": {
108 | "message": "Версія:"
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Duplicate Bookmarks Finder
2 |
3 | 
4 | 
5 |
6 | Find and delete duplicate bookmarks.
7 |
8 | Available for the following browsers:
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | This extension is a browser pop-up that allows to find and delete duplicate bookmarks.
26 |
27 | ### Features:
28 | - List all duplicated bookmarks
29 | - Delete selected duplicates
30 | - Dark/Light mode
31 | - Use RegEx to ignore particular URL patterns
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | ---
46 |
47 | ## Developer section
48 |
49 | ### Dev dependencies
50 |
51 | To install dev dependencies run:
52 | ```
53 | npm i
54 | ```
55 |
56 | Available npm scripts:
57 | - `lint:js` - run eslint on project.
58 | - `lint:css` - run stylelint on project.
59 | - `lint` - lint both js and css.
60 | - `sass` - start Sass watch task.
61 |
62 | ### Updating extension version
63 |
64 | To update extension version in all places where it's contained (manifests, README...), run
65 | ```
66 | bash set-version.sh -v X.Y.Z
67 | ```
68 |
69 | ### Packing extension for store
70 |
71 | To prepare zip archive for publishing in browser app store, run
72 | ```
73 | bash pack.sh
74 | ```
75 | Options:
76 | * `-p` - platform - Platform to build for. Possible values: `firefox`, `opera`. Default (param not provided) `chromium`.
77 | * `-m` - mode - Packing mode. If `dev` is specified - development mode.Create only build folder, don't zip. Otherwise mode=publishing (create zip, remove build folder).
78 |
79 | ### Recommended workflow
80 | 1. Make changes.
81 | - Run `npm run sass` to start watch task to monitor scss changes.
82 | 2. Run `bash set-version.sh -v X.Y.Z`.
83 | 3. Commit changes.
84 | 4. Update version in `package.json` + run `npm i`.
85 | 5. Run `npm run version`.
86 | 6. Commit `package.json`, `package-lock.json` and `CHANGELOG.md` files.
87 | 7. Tag (`git tag vX.Y.Z`).
88 | 8. Push.
89 |
90 | ## Possible further enhancements
91 |
92 | * [ ] Find -> new search/find again
93 | * [ ] Rewrite with modules + Webpack?
94 | * [ ] Sort by folder path
95 | * [ ] Delete folder if empty after deletion duplicates
96 |
--------------------------------------------------------------------------------
/src/css/generated_theme/colors.module.css:
--------------------------------------------------------------------------------
1 | .primary {
2 | background-color: var(--md-sys-color-primary);
3 | }
4 | .primary-text {
5 | color: var(--md-sys-color-primary);
6 | }
7 | .on-primary {
8 | background-color: var(--md-sys-color-on-primary);
9 | }
10 | .on-primary-text {
11 | color: var(--md-sys-color-on-primary);
12 | }
13 | .primary-container {
14 | background-color: var(--md-sys-color-primary-container);
15 | }
16 | .primary-container-text {
17 | color: var(--md-sys-color-primary-container);
18 | }
19 | .on-primary-container {
20 | background-color: var(--md-sys-color-on-primary-container);
21 | }
22 | .on-primary-container-text {
23 | color: var(--md-sys-color-on-primary-container);
24 | }
25 | .secondary {
26 | background-color: var(--md-sys-color-secondary);
27 | }
28 | .secondary-text {
29 | color: var(--md-sys-color-secondary);
30 | }
31 | .on-secondary {
32 | background-color: var(--md-sys-color-on-secondary);
33 | }
34 | .on-secondary-text {
35 | color: var(--md-sys-color-on-secondary);
36 | }
37 | .secondary-container {
38 | background-color: var(--md-sys-color-secondary-container);
39 | }
40 | .secondary-container-text {
41 | color: var(--md-sys-color-secondary-container);
42 | }
43 | .on-secondary-container {
44 | background-color: var(--md-sys-color-on-secondary-container);
45 | }
46 | .on-secondary-container-text {
47 | color: var(--md-sys-color-on-secondary-container);
48 | }
49 | .tertiary {
50 | background-color: var(--md-sys-color-tertiary);
51 | }
52 | .tertiary-text {
53 | color: var(--md-sys-color-tertiary);
54 | }
55 | .on-tertiary {
56 | background-color: var(--md-sys-color-on-tertiary);
57 | }
58 | .on-tertiary-text {
59 | color: var(--md-sys-color-on-tertiary);
60 | }
61 | .tertiary-container {
62 | background-color: var(--md-sys-color-tertiary-container);
63 | }
64 | .tertiary-container-text {
65 | color: var(--md-sys-color-tertiary-container);
66 | }
67 | .on-tertiary-container {
68 | background-color: var(--md-sys-color-on-tertiary-container);
69 | }
70 | .on-tertiary-container-text {
71 | color: var(--md-sys-color-on-tertiary-container);
72 | }
73 | .error {
74 | background-color: var(--md-sys-color-error);
75 | }
76 | .error-text {
77 | color: var(--md-sys-color-error);
78 | }
79 | .error-container {
80 | background-color: var(--md-sys-color-error-container);
81 | }
82 | .error-container-text {
83 | color: var(--md-sys-color-error-container);
84 | }
85 | .on-error {
86 | background-color: var(--md-sys-color-on-error);
87 | }
88 | .on-error-text {
89 | color: var(--md-sys-color-on-error);
90 | }
91 | .on-error-container {
92 | background-color: var(--md-sys-color-on-error-container);
93 | }
94 | .on-error-container-text {
95 | color: var(--md-sys-color-on-error-container);
96 | }
97 | .background {
98 | background-color: var(--md-sys-color-background);
99 | }
100 | .background-text {
101 | color: var(--md-sys-color-background);
102 | }
103 | .on-background {
104 | background-color: var(--md-sys-color-on-background);
105 | }
106 | .on-background-text {
107 | color: var(--md-sys-color-on-background);
108 | }
109 | .surface {
110 | background-color: var(--md-sys-color-surface);
111 | }
112 | .surface-text {
113 | color: var(--md-sys-color-surface);
114 | }
115 | .on-surface {
116 | background-color: var(--md-sys-color-on-surface);
117 | }
118 | .on-surface-text {
119 | color: var(--md-sys-color-on-surface);
120 | }
121 | .surface-variant {
122 | background-color: var(--md-sys-color-surface-variant);
123 | }
124 | .surface-variant-text {
125 | color: var(--md-sys-color-surface-variant);
126 | }
127 | .on-surface-variant {
128 | background-color: var(--md-sys-color-on-surface-variant);
129 | }
130 | .on-surface-variant-text {
131 | color: var(--md-sys-color-on-surface-variant);
132 | }
133 | .outline {
134 | background-color: var(--md-sys-color-outline);
135 | }
136 | .outline-text {
137 | color: var(--md-sys-color-outline);
138 | }
139 | .inverse-on-surface {
140 | background-color: var(--md-sys-color-inverse-on-surface);
141 | }
142 | .inverse-on-surface-text {
143 | color: var(--md-sys-color-inverse-on-surface);
144 | }
145 | .inverse-surface {
146 | background-color: var(--md-sys-color-inverse-surface);
147 | }
148 | .inverse-surface-text {
149 | color: var(--md-sys-color-inverse-surface);
150 | }
151 | .inverse-primary {
152 | background-color: var(--md-sys-color-inverse-primary);
153 | }
154 | .inverse-primary-text {
155 | color: var(--md-sys-color-inverse-primary);
156 | }
157 | .shadow {
158 | background-color: var(--md-sys-color-shadow);
159 | }
160 | .shadow-text {
161 | color: var(--md-sys-color-shadow);
162 | }
163 | .surface-tint {
164 | background-color: var(--md-sys-color-surface-tint);
165 | }
166 | .surface-tint-text {
167 | color: var(--md-sys-color-surface-tint);
168 | }
169 | .outline-variant {
170 | background-color: var(--md-sys-color-outline-variant);
171 | }
172 | .outline-variant-text {
173 | color: var(--md-sys-color-outline-variant);
174 | }
175 | .scrim {
176 | background-color: var(--md-sys-color-scrim);
177 | }
178 | .scrim-text {
179 | color: var(--md-sys-color-scrim);
180 | }
181 |
--------------------------------------------------------------------------------
/test_data/bookmarks_example.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | Bookmarks
7 | Bookmarks
8 |
9 |
Bookmarks bar
10 |
11 |
zaksid (Alexander Mahdybor) · GitHub
12 | test
13 |
14 |
gh test
15 | gh test 2
16 | JavaScript modules - JavaScript | MDN
17 | subfolder
18 |
19 |
gh sub
20 |
21 |
gh test 3
22 |
23 |
mdn
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/js/pages/pageSettings.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable func-names, prefer-destructuring */
2 | /* global mdc, Mustache */
3 |
4 | import { getDefaultExtensionSettings, getExtensionSettings, setExtensionSettings } from '../utils/index.js';
5 | import { MainNavBtnIcons, MainNavBtnClasses, Theme } from '../constants.js';
6 |
7 | const { MDCSwitch } = mdc.switchControl;
8 | const { MDCTextField } = mdc.textField;
9 |
10 | export default async function initSettingsPage(browserInstance) {
11 | const appBarNavBtn = document.querySelector('#app-bar-nav-btn');
12 | const appBarTitle = document.querySelector('#app-bar-title');
13 |
14 | appBarNavBtn.innerHTML = MainNavBtnIcons.BACK;
15 | appBarNavBtn.classList.add(MainNavBtnClasses.IS_MENU_CONTENT);
16 |
17 | appBarTitle.innerHTML = browserInstance.i18n.getMessage('menu_settings');
18 |
19 | const isFirefox = navigator.userAgent.includes('Firefox');
20 | const userSettings = await getExtensionSettings(browserInstance);
21 | const onOffSettings = [];
22 |
23 | if (isFirefox) {
24 | onOffSettings.push({
25 | id: 'showSeparators',
26 | name: browserInstance.i18n.getMessage('settingShowSeparators'),
27 | description: browserInstance.i18n.getMessage('settingShowSeparatorsDescription'),
28 | value: userSettings.showSeparators,
29 | });
30 | }
31 |
32 | const settingsTemplateResponse = await fetch('templates/settings-page.mustache');
33 | const settingsTemplate = await settingsTemplateResponse.text();
34 |
35 | document.querySelector('#main-content').innerHTML = await Mustache.render(settingsTemplate, {
36 | onOffSettings,
37 | ignoredUrls: userSettings.ignoredUrls,
38 | selectedTheme: userSettings.theme,
39 | theme: {
40 | isDark: userSettings.theme === Theme.DARK,
41 | isLight: userSettings.theme === Theme.LIGHT,
42 | isSystem: userSettings.theme === Theme.SYSTEM,
43 | },
44 | i18n: {
45 | btn_add: browserInstance.i18n.getMessage('btn_add'),
46 | btn_resetSettings: browserInstance.i18n.getMessage('btn_resetSettings'),
47 | input_hint_ignoredUrlRegex: browserInstance.i18n.getMessage('input_hint_ignoredUrlRegex'),
48 | settingIgnoredPatterns: browserInstance.i18n.getMessage('settingIgnoredPatterns'),
49 | settingIgnoredPatternsDescription: browserInstance.i18n.getMessage('settingIgnoredPatternsDescription'),
50 | settingTheme: browserInstance.i18n.getMessage('settingTheme'),
51 | settingThemeDark: browserInstance.i18n.getMessage('settingThemeDark'),
52 | settingThemeLight: browserInstance.i18n.getMessage('settingThemeLight'),
53 | settingThemeSystem: browserInstance.i18n.getMessage('settingThemeSystem'),
54 | },
55 | });
56 |
57 | const settingsPage = document.querySelector('#settings-page');
58 | settingsPage.querySelectorAll('.mdc-text-field').forEach((inputElement) => {
59 | // eslint-disable-next-line no-new
60 | new MDCTextField(inputElement);
61 | });
62 |
63 | const form = settingsPage.querySelector('#ignored-patterns-form');
64 | form.addEventListener('submit', async function (event) {
65 | event.preventDefault();
66 |
67 | const formData = new FormData(this);
68 | const ignoredUrls = formData.get('ignoredUrls');
69 | // TODO: Move to mustache template to avoid duplication?
70 | const chipMarkup = `
71 |
72 |
73 |
75 |
76 | ${ignoredUrls}
77 | close
79 |
80 |
81 | `;
82 |
83 | const newChip = document.createElement('span');
84 | newChip.innerHTML = chipMarkup;
85 | document.querySelector('.mdc-chip-set__chips').append(newChip);
86 |
87 | userSettings.ignoredUrls = [...userSettings.ignoredUrls, ignoredUrls];
88 | await setExtensionSettings(browserInstance, userSettings);
89 |
90 | this.reset();
91 | });
92 |
93 | settingsPage.querySelectorAll('.mdc-switch').forEach((switchElem) => {
94 | const switchControl = new MDCSwitch(switchElem);
95 | switchControl.selected = userSettings[switchControl.root.name];
96 |
97 | switchElem.addEventListener('click', async (event) => {
98 | const settingsElem = event.currentTarget;
99 | if (settingsElem.name in userSettings) {
100 | userSettings[settingsElem.name] = settingsElem.attributes['aria-checked'].value === 'true';
101 | await setExtensionSettings(browserInstance, userSettings);
102 | }
103 | });
104 | });
105 |
106 | const themeButtons = settingsPage.querySelectorAll('.theme-btn');
107 |
108 | // Handle theme switching
109 | themeButtons.forEach((elem) => {
110 | elem.addEventListener('click', async (event) => {
111 | const currentButton = event.currentTarget;
112 | const val = currentButton.dataset.theme;
113 |
114 | userSettings.theme = val;
115 | document.documentElement.className = val === Theme.SYSTEM ? '' : val;
116 |
117 | await setExtensionSettings(browserInstance, userSettings);
118 | themeButtons.forEach((btn) => btn.classList.remove('selected'));
119 | currentButton.classList.add('selected');
120 | });
121 | });
122 |
123 | const removeIgnoredUrl = async function () {
124 | const urlPattern = this.dataset.val;
125 | const index = userSettings.ignoredUrls.indexOf(urlPattern);
126 |
127 | userSettings.ignoredUrls.splice(index, 1);
128 | await setExtensionSettings(browserInstance, userSettings);
129 |
130 | this.closest('.mdc-chip').remove();
131 | };
132 |
133 | settingsPage.addEventListener('click', function (event) {
134 | // loop parent nodes from the target to the delegation node
135 | for (let target = event.target; target && target !== this; target = target.parentNode) {
136 | if (target.classList.contains('remove-ignored-url')) {
137 | removeIgnoredUrl.call(target, event);
138 | break;
139 | }
140 | }
141 | }, false);
142 |
143 | settingsPage.querySelector('#reset-settings').addEventListener('click', async () => {
144 | await setExtensionSettings(browserInstance, getDefaultExtensionSettings());
145 | await initSettingsPage(browserInstance);
146 | document.documentElement.className = '';
147 | });
148 | }
149 |
--------------------------------------------------------------------------------
/src/templates/settings-page.mustache:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | restart_alt
5 | {{i18n.btn_resetSettings}}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | {{i18n.settingTheme}}
14 |
15 |
16 |
17 |
18 |
19 |
20 | brightness_4
21 | {{i18n.settingThemeSystem}}
22 |
23 |
24 |
25 |
26 | light_mode
27 | {{i18n.settingThemeLight}}
28 |
29 |
30 |
31 |
32 | dark_mode
33 | {{i18n.settingThemeDark}}
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | {{# onOffSettings }}
42 |
43 |
44 |
45 |
46 |
47 | {{name}}
48 |
49 | {{#description}}
50 | {{description}}
51 | {{/description}}
52 |
53 |
55 |
56 |
57 |
58 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | {{/ onOffSettings }}
76 |
77 |
78 |
79 |
80 |
81 |
82 | {{i18n.settingIgnoredPatterns}}
83 |
84 | {{i18n.settingIgnoredPatternsDescription}}
85 |
86 |
106 |
107 |
108 |
109 | {{#ignoredUrls}}
110 |
111 |
112 |
114 |
115 | {{.}}
116 | close
118 |
119 |
120 |
121 | {{/ignoredUrls}}
122 |
123 |
124 |
125 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/src/css/generated_theme/typography.module.css:
--------------------------------------------------------------------------------
1 | .display-large{
2 | font-family: var(--md-sys-typescale-display-large-font-family-name);
3 | font-style: var(--md-sys-typescale-display-large-font-family-style);
4 | font-weight: var(--md-sys-typescale-display-large-font-weight);
5 | font-size: var(--md-sys-typescale-display-large-font-size);
6 | letter-spacing: var(--md-sys-typescale-display-large-tracking);
7 | line-height: var(--md-sys-typescale-display-large-height);
8 | text-transform: var(--md-sys-typescale-display-large-text-transform);
9 | text-decoration: var(--md-sys-typescale-display-large-text-decoration);
10 | }
11 | .display-medium{
12 | font-family: var(--md-sys-typescale-display-medium-font-family-name);
13 | font-style: var(--md-sys-typescale-display-medium-font-family-style);
14 | font-weight: var(--md-sys-typescale-display-medium-font-weight);
15 | font-size: var(--md-sys-typescale-display-medium-font-size);
16 | letter-spacing: var(--md-sys-typescale-display-medium-tracking);
17 | line-height: var(--md-sys-typescale-display-medium-height);
18 | text-transform: var(--md-sys-typescale-display-medium-text-transform);
19 | text-decoration: var(--md-sys-typescale-display-medium-text-decoration);
20 | }
21 | .display-small{
22 | font-family: var(--md-sys-typescale-display-small-font-family-name);
23 | font-style: var(--md-sys-typescale-display-small-font-family-style);
24 | font-weight: var(--md-sys-typescale-display-small-font-weight);
25 | font-size: var(--md-sys-typescale-display-small-font-size);
26 | letter-spacing: var(--md-sys-typescale-display-small-tracking);
27 | line-height: var(--md-sys-typescale-display-small-height);
28 | text-transform: var(--md-sys-typescale-display-small-text-transform);
29 | text-decoration: var(--md-sys-typescale-display-small-text-decoration);
30 | }
31 | .headline-large{
32 | font-family: var(--md-sys-typescale-headline-large-font-family-name);
33 | font-style: var(--md-sys-typescale-headline-large-font-family-style);
34 | font-weight: var(--md-sys-typescale-headline-large-font-weight);
35 | font-size: var(--md-sys-typescale-headline-large-font-size);
36 | letter-spacing: var(--md-sys-typescale-headline-large-tracking);
37 | line-height: var(--md-sys-typescale-headline-large-height);
38 | text-transform: var(--md-sys-typescale-headline-large-text-transform);
39 | text-decoration: var(--md-sys-typescale-headline-large-text-decoration);
40 | }
41 | .headline-medium{
42 | font-family: var(--md-sys-typescale-headline-medium-font-family-name);
43 | font-style: var(--md-sys-typescale-headline-medium-font-family-style);
44 | font-weight: var(--md-sys-typescale-headline-medium-font-weight);
45 | font-size: var(--md-sys-typescale-headline-medium-font-size);
46 | letter-spacing: var(--md-sys-typescale-headline-medium-tracking);
47 | line-height: var(--md-sys-typescale-headline-medium-height);
48 | text-transform: var(--md-sys-typescale-headline-medium-text-transform);
49 | text-decoration: var(--md-sys-typescale-headline-medium-text-decoration);
50 | }
51 | .headline-small{
52 | font-family: var(--md-sys-typescale-headline-small-font-family-name);
53 | font-style: var(--md-sys-typescale-headline-small-font-family-style);
54 | font-weight: var(--md-sys-typescale-headline-small-font-weight);
55 | font-size: var(--md-sys-typescale-headline-small-font-size);
56 | letter-spacing: var(--md-sys-typescale-headline-small-tracking);
57 | line-height: var(--md-sys-typescale-headline-small-height);
58 | text-transform: var(--md-sys-typescale-headline-small-text-transform);
59 | text-decoration: var(--md-sys-typescale-headline-small-text-decoration);
60 | }
61 | .body-large{
62 | font-family: var(--md-sys-typescale-body-large-font-family-name);
63 | font-style: var(--md-sys-typescale-body-large-font-family-style);
64 | font-weight: var(--md-sys-typescale-body-large-font-weight);
65 | font-size: var(--md-sys-typescale-body-large-font-size);
66 | letter-spacing: var(--md-sys-typescale-body-large-tracking);
67 | line-height: var(--md-sys-typescale-body-large-height);
68 | text-transform: var(--md-sys-typescale-body-large-text-transform);
69 | text-decoration: var(--md-sys-typescale-body-large-text-decoration);
70 | }
71 | .body-medium{
72 | font-family: var(--md-sys-typescale-body-medium-font-family-name);
73 | font-style: var(--md-sys-typescale-body-medium-font-family-style);
74 | font-weight: var(--md-sys-typescale-body-medium-font-weight);
75 | font-size: var(--md-sys-typescale-body-medium-font-size);
76 | letter-spacing: var(--md-sys-typescale-body-medium-tracking);
77 | line-height: var(--md-sys-typescale-body-medium-height);
78 | text-transform: var(--md-sys-typescale-body-medium-text-transform);
79 | text-decoration: var(--md-sys-typescale-body-medium-text-decoration);
80 | }
81 | .body-small{
82 | font-family: var(--md-sys-typescale-body-small-font-family-name);
83 | font-style: var(--md-sys-typescale-body-small-font-family-style);
84 | font-weight: var(--md-sys-typescale-body-small-font-weight);
85 | font-size: var(--md-sys-typescale-body-small-font-size);
86 | letter-spacing: var(--md-sys-typescale-body-small-tracking);
87 | line-height: var(--md-sys-typescale-body-small-height);
88 | text-transform: var(--md-sys-typescale-body-small-text-transform);
89 | text-decoration: var(--md-sys-typescale-body-small-text-decoration);
90 | }
91 | .label-large{
92 | font-family: var(--md-sys-typescale-label-large-font-family-name);
93 | font-style: var(--md-sys-typescale-label-large-font-family-style);
94 | font-weight: var(--md-sys-typescale-label-large-font-weight);
95 | font-size: var(--md-sys-typescale-label-large-font-size);
96 | letter-spacing: var(--md-sys-typescale-label-large-tracking);
97 | line-height: var(--md-sys-typescale-label-large-height);
98 | text-transform: var(--md-sys-typescale-label-large-text-transform);
99 | text-decoration: var(--md-sys-typescale-label-large-text-decoration);
100 | }
101 | .label-medium{
102 | font-family: var(--md-sys-typescale-label-medium-font-family-name);
103 | font-style: var(--md-sys-typescale-label-medium-font-family-style);
104 | font-weight: var(--md-sys-typescale-label-medium-font-weight);
105 | font-size: var(--md-sys-typescale-label-medium-font-size);
106 | letter-spacing: var(--md-sys-typescale-label-medium-tracking);
107 | line-height: var(--md-sys-typescale-label-medium-height);
108 | text-transform: var(--md-sys-typescale-label-medium-text-transform);
109 | text-decoration: var(--md-sys-typescale-label-medium-text-decoration);
110 | }
111 | .label-small{
112 | font-family: var(--md-sys-typescale-label-small-font-family-name);
113 | font-style: var(--md-sys-typescale-label-small-font-family-style);
114 | font-weight: var(--md-sys-typescale-label-small-font-weight);
115 | font-size: var(--md-sys-typescale-label-small-font-size);
116 | letter-spacing: var(--md-sys-typescale-label-small-tracking);
117 | line-height: var(--md-sys-typescale-label-small-height);
118 | text-transform: var(--md-sys-typescale-label-small-text-transform);
119 | text-decoration: var(--md-sys-typescale-label-small-text-decoration);
120 | }
121 | .title-large{
122 | font-family: var(--md-sys-typescale-title-large-font-family-name);
123 | font-style: var(--md-sys-typescale-title-large-font-family-style);
124 | font-weight: var(--md-sys-typescale-title-large-font-weight);
125 | font-size: var(--md-sys-typescale-title-large-font-size);
126 | letter-spacing: var(--md-sys-typescale-title-large-tracking);
127 | line-height: var(--md-sys-typescale-title-large-height);
128 | text-transform: var(--md-sys-typescale-title-large-text-transform);
129 | text-decoration: var(--md-sys-typescale-title-large-text-decoration);
130 | }
131 | .title-medium{
132 | font-family: var(--md-sys-typescale-title-medium-font-family-name);
133 | font-style: var(--md-sys-typescale-title-medium-font-family-style);
134 | font-weight: var(--md-sys-typescale-title-medium-font-weight);
135 | font-size: var(--md-sys-typescale-title-medium-font-size);
136 | letter-spacing: var(--md-sys-typescale-title-medium-tracking);
137 | line-height: var(--md-sys-typescale-title-medium-height);
138 | text-transform: var(--md-sys-typescale-title-medium-text-transform);
139 | text-decoration: var(--md-sys-typescale-title-medium-text-decoration);
140 | }
141 | .title-small{
142 | font-family: var(--md-sys-typescale-title-small-font-family-name);
143 | font-style: var(--md-sys-typescale-title-small-font-family-style);
144 | font-weight: var(--md-sys-typescale-title-small-font-weight);
145 | font-size: var(--md-sys-typescale-title-small-font-size);
146 | letter-spacing: var(--md-sys-typescale-title-small-tracking);
147 | line-height: var(--md-sys-typescale-title-small-height);
148 | text-transform: var(--md-sys-typescale-title-small-text-transform);
149 | text-decoration: var(--md-sys-typescale-title-small-text-decoration);
150 | }
151 |
--------------------------------------------------------------------------------
/src/lib/js/mustache.min.js:
--------------------------------------------------------------------------------
1 | (function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?module.exports=factory():typeof define==="function"&&define.amd?define(factory):(global=global||self,global.Mustache=factory())})(this,function(){"use strict";var objectToString=Object.prototype.toString;var isArray=Array.isArray||function isArrayPolyfill(object){return objectToString.call(object)==="[object Array]"};function isFunction(object){return typeof object==="function"}function typeStr(obj){return isArray(obj)?"array":typeof obj}function escapeRegExp(string){return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function hasProperty(obj,propName){return obj!=null&&typeof obj==="object"&&propName in obj}function primitiveHasOwnProperty(primitive,propName){return primitive!=null&&typeof primitive!=="object"&&primitive.hasOwnProperty&&primitive.hasOwnProperty(propName)}var regExpTest=RegExp.prototype.test;function testRegExp(re,string){return regExpTest.call(re,string)}var nonSpaceRe=/\S/;function isWhitespace(string){return!testRegExp(nonSpaceRe,string)}var entityMap={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};function escapeHtml(string){return String(string).replace(/[&<>"'`=\/]/g,function fromEntityMap(s){return entityMap[s]})}var whiteRe=/\s*/;var spaceRe=/\s+/;var equalsRe=/\s*=/;var curlyRe=/\s*\}/;var tagRe=/#|\^|\/|>|\{|&|=|!/;function parseTemplate(template,tags){if(!template)return[];var lineHasNonSpace=false;var sections=[];var tokens=[];var spaces=[];var hasTag=false;var nonSpace=false;var indentation="";var tagIndex=0;function stripSpace(){if(hasTag&&!nonSpace){while(spaces.length)delete tokens[spaces.pop()]}else{spaces=[]}hasTag=false;nonSpace=false}var openingTagRe,closingTagRe,closingCurlyRe;function compileTags(tagsToCompile){if(typeof tagsToCompile==="string")tagsToCompile=tagsToCompile.split(spaceRe,2);if(!isArray(tagsToCompile)||tagsToCompile.length!==2)throw new Error("Invalid tags: "+tagsToCompile);openingTagRe=new RegExp(escapeRegExp(tagsToCompile[0])+"\\s*");closingTagRe=new RegExp("\\s*"+escapeRegExp(tagsToCompile[1]));closingCurlyRe=new RegExp("\\s*"+escapeRegExp("}"+tagsToCompile[1]))}compileTags(tags||mustache.tags);var scanner=new Scanner(template);var start,type,value,chr,token,openSection;while(!scanner.eos()){start=scanner.pos;value=scanner.scanUntil(openingTagRe);if(value){for(var i=0,valueLength=value.length;i"){token=[type,value,start,scanner.pos,indentation,tagIndex,lineHasNonSpace]}else{token=[type,value,start,scanner.pos]}tagIndex++;tokens.push(token);if(type==="#"||type==="^"){sections.push(token)}else if(type==="/"){openSection=sections.pop();if(!openSection)throw new Error('Unopened section "'+value+'" at '+start);if(openSection[1]!==value)throw new Error('Unclosed section "'+openSection[1]+'" at '+start)}else if(type==="name"||type==="{"||type==="&"){nonSpace=true}else if(type==="="){compileTags(value)}}stripSpace();openSection=sections.pop();if(openSection)throw new Error('Unclosed section "'+openSection[1]+'" at '+scanner.pos);return nestTokens(squashTokens(tokens))}function squashTokens(tokens){var squashedTokens=[];var token,lastToken;for(var i=0,numTokens=tokens.length;i0?sections[sections.length-1][4]:nestedTokens;break;default:collector.push(token)}}return nestedTokens}function Scanner(string){this.string=string;this.tail=string;this.pos=0}Scanner.prototype.eos=function eos(){return this.tail===""};Scanner.prototype.scan=function scan(re){var match=this.tail.match(re);if(!match||match.index!==0)return"";var string=match[0];this.tail=this.tail.substring(string.length);this.pos+=string.length;return string};Scanner.prototype.scanUntil=function scanUntil(re){var index=this.tail.search(re),match;switch(index){case-1:match=this.tail;this.tail="";break;case 0:match="";break;default:match=this.tail.substring(0,index);this.tail=this.tail.substring(index)}this.pos+=match.length;return match};function Context(view,parentContext){this.view=view;this.cache={".":this.view};this.parent=parentContext}Context.prototype.push=function push(view){return new Context(view,this)};Context.prototype.lookup=function lookup(name){var cache=this.cache;var value;if(cache.hasOwnProperty(name)){value=cache[name]}else{var context=this,intermediateValue,names,index,lookupHit=false;while(context){if(name.indexOf(".")>0){intermediateValue=context.view;names=name.split(".");index=0;while(intermediateValue!=null&&index")value=this.renderPartial(token,context,partials,config);else if(symbol==="&")value=this.unescapedValue(token,context);else if(symbol==="name")value=this.escapedValue(token,context,config);else if(symbol==="text")value=this.rawValue(token);if(value!==undefined)buffer+=value}return buffer};Writer.prototype.renderSection=function renderSection(token,context,partials,originalTemplate,config){var self=this;var buffer="";var value=context.lookup(token[1]);function subRender(template){return self.render(template,context,partials,config)}if(!value)return;if(isArray(value)){for(var j=0,valueLength=value.length;j0||!lineHasNonSpace)){partialByNl[i]=filteredIndentation+partialByNl[i]}}return partialByNl.join("\n")};Writer.prototype.renderPartial=function renderPartial(token,context,partials,config){if(!partials)return;var tags=this.getConfigTags(config);var value=isFunction(partials)?partials(token[1]):partials[token[1]];if(value!=null){var lineHasNonSpace=token[6];var tagIndex=token[5];var indentation=token[4];var indentedValue=value;if(tagIndex==0&&indentation){indentedValue=this.indentPartial(value,indentation,lineHasNonSpace)}var tokens=this.parse(indentedValue,tags);return this.renderTokens(tokens,context,partials,indentedValue,config)}};Writer.prototype.unescapedValue=function unescapedValue(token,context){var value=context.lookup(token[1]);if(value!=null)return value};Writer.prototype.escapedValue=function escapedValue(token,context,config){var escape=this.getConfigEscape(config)||mustache.escape;var value=context.lookup(token[1]);if(value!=null)return typeof value==="number"&&escape===mustache.escape?String(value):escape(value)};Writer.prototype.rawValue=function rawValue(token){return token[1]};Writer.prototype.getConfigTags=function getConfigTags(config){if(isArray(config)){return config}else if(config&&typeof config==="object"){return config.tags}else{return undefined}};Writer.prototype.getConfigEscape=function getConfigEscape(config){if(config&&typeof config==="object"&&!isArray(config)){return config.escape}else{return undefined}};var mustache={name:"mustache.js",version:"4.2.0",tags:["{{","}}"],clearCache:undefined,escape:undefined,parse:undefined,render:undefined,Scanner:undefined,Context:undefined,Writer:undefined,set templateCache(cache){defaultWriter.templateCache=cache},get templateCache(){return defaultWriter.templateCache}};var defaultWriter=new Writer;mustache.clearCache=function clearCache(){return defaultWriter.clearCache()};mustache.parse=function parse(template,tags){return defaultWriter.parse(template,tags)};mustache.render=function render(template,view,partials,config){if(typeof template!=="string"){throw new TypeError('Invalid template! Template should be a "string" '+'but "'+typeStr(template)+'" was given as the first '+"argument for mustache#render(template, view, partials)")}return defaultWriter.render(template,view,partials,config)};mustache.escape=escapeHtml;mustache.Scanner=Scanner;mustache.Context=Context;mustache.Writer=Writer;return mustache});
2 |
--------------------------------------------------------------------------------
/src/css/generated_theme/tokens.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --md-source: #496500;
3 | /* primary */
4 | --md-ref-palette-primary0: #000000;
5 | --md-ref-palette-primary10: #141f00;
6 | --md-ref-palette-primary20: #253600;
7 | --md-ref-palette-primary25: #2e4100;
8 | --md-ref-palette-primary30: #374e00;
9 | --md-ref-palette-primary35: #415a00;
10 | --md-ref-palette-primary40: #4b6703;
11 | --md-ref-palette-primary50: #638120;
12 | --md-ref-palette-primary60: #7c9b39;
13 | --md-ref-palette-primary70: #96b751;
14 | --md-ref-palette-primary80: #b0d369;
15 | --md-ref-palette-primary90: #ccef82;
16 | --md-ref-palette-primary95: #dafe8e;
17 | --md-ref-palette-primary98: #f2ffd0;
18 | --md-ref-palette-primary99: #faffe5;
19 | --md-ref-palette-primary100: #ffffff;
20 | /* secondary */
21 | --md-ref-palette-secondary0: #000000;
22 | --md-ref-palette-secondary10: #171e09;
23 | --md-ref-palette-secondary20: #2c331d;
24 | --md-ref-palette-secondary25: #373e27;
25 | --md-ref-palette-secondary30: #424a31;
26 | --md-ref-palette-secondary35: #4e553c;
27 | --md-ref-palette-secondary40: #5a6148;
28 | --md-ref-palette-secondary50: #727a5f;
29 | --md-ref-palette-secondary60: #8c9477;
30 | --md-ref-palette-secondary70: #a7af90;
31 | --md-ref-palette-secondary80: #c2caaa;
32 | --md-ref-palette-secondary90: #dee6c5;
33 | --md-ref-palette-secondary95: #ecf5d3;
34 | --md-ref-palette-secondary98: #f5fddb;
35 | --md-ref-palette-secondary99: #faffe5;
36 | --md-ref-palette-secondary100: #ffffff;
37 | /* tertiary */
38 | --md-ref-palette-tertiary0: #000000;
39 | --md-ref-palette-tertiary10: #00201d;
40 | --md-ref-palette-tertiary20: #013732;
41 | --md-ref-palette-tertiary25: #12423d;
42 | --md-ref-palette-tertiary30: #204e48;
43 | --md-ref-palette-tertiary35: #2c5a54;
44 | --md-ref-palette-tertiary40: #396660;
45 | --md-ref-palette-tertiary50: #527f79;
46 | --md-ref-palette-tertiary60: #6b9992;
47 | --md-ref-palette-tertiary70: #86b4ad;
48 | --md-ref-palette-tertiary80: #a0d0c8;
49 | --md-ref-palette-tertiary90: #bcece4;
50 | --md-ref-palette-tertiary95: #cafaf2;
51 | --md-ref-palette-tertiary98: #e4fffa;
52 | --md-ref-palette-tertiary99: #f2fffc;
53 | --md-ref-palette-tertiary100: #ffffff;
54 | /* neutral */
55 | --md-ref-palette-neutral0: #000000;
56 | --md-ref-palette-neutral10: #1b1c17;
57 | --md-ref-palette-neutral20: #30312c;
58 | --md-ref-palette-neutral25: #3b3c36;
59 | --md-ref-palette-neutral30: #474742;
60 | --md-ref-palette-neutral35: #52534d;
61 | --md-ref-palette-neutral40: #5e5f59;
62 | --md-ref-palette-neutral50: #777771;
63 | --md-ref-palette-neutral60: #91918a;
64 | --md-ref-palette-neutral70: #acaba4;
65 | --md-ref-palette-neutral80: #c8c7bf;
66 | --md-ref-palette-neutral90: #e4e3db;
67 | --md-ref-palette-neutral95: #f2f1e9;
68 | --md-ref-palette-neutral98: #fbf9f1;
69 | --md-ref-palette-neutral99: #fefcf4;
70 | --md-ref-palette-neutral100: #ffffff;
71 | /* neutral-variant */
72 | --md-ref-palette-neutral-variant0: #000000;
73 | --md-ref-palette-neutral-variant10: #1a1d13;
74 | --md-ref-palette-neutral-variant20: #2f3227;
75 | --md-ref-palette-neutral-variant25: #3a3d32;
76 | --md-ref-palette-neutral-variant30: #45483c;
77 | --md-ref-palette-neutral-variant35: #515448;
78 | --md-ref-palette-neutral-variant40: #5d6053;
79 | --md-ref-palette-neutral-variant50: #76786b;
80 | --md-ref-palette-neutral-variant60: #909284;
81 | --md-ref-palette-neutral-variant70: #aaad9e;
82 | --md-ref-palette-neutral-variant80: #c6c8b9;
83 | --md-ref-palette-neutral-variant90: #e2e4d4;
84 | --md-ref-palette-neutral-variant95: #f0f2e2;
85 | --md-ref-palette-neutral-variant98: #f9fbea;
86 | --md-ref-palette-neutral-variant99: #fcfeed;
87 | --md-ref-palette-neutral-variant100: #ffffff;
88 | /* error */
89 | --md-ref-palette-error0: #000000;
90 | --md-ref-palette-error10: #410002;
91 | --md-ref-palette-error20: #690005;
92 | --md-ref-palette-error25: #7e0007;
93 | --md-ref-palette-error30: #93000a;
94 | --md-ref-palette-error35: #a80710;
95 | --md-ref-palette-error40: #ba1a1a;
96 | --md-ref-palette-error50: #de3730;
97 | --md-ref-palette-error60: #ff5449;
98 | --md-ref-palette-error70: #ff897d;
99 | --md-ref-palette-error80: #ffb4ab;
100 | --md-ref-palette-error90: #ffdad6;
101 | --md-ref-palette-error95: #ffedea;
102 | --md-ref-palette-error98: #fff8f7;
103 | --md-ref-palette-error99: #fffbff;
104 | --md-ref-palette-error100: #ffffff;
105 | /* light */
106 | --md-sys-color-primary-light: #4b6703;
107 | --md-sys-color-on-primary-light: #ffffff;
108 | --md-sys-color-primary-container-light: #ccef82;
109 | --md-sys-color-on-primary-container-light: #141f00;
110 | --md-sys-color-secondary-light: #5a6148;
111 | --md-sys-color-on-secondary-light: #ffffff;
112 | --md-sys-color-secondary-container-light: #dee6c5;
113 | --md-sys-color-on-secondary-container-light: #171e09;
114 | --md-sys-color-tertiary-light: #396660;
115 | --md-sys-color-on-tertiary-light: #ffffff;
116 | --md-sys-color-tertiary-container-light: #bcece4;
117 | --md-sys-color-on-tertiary-container-light: #00201d;
118 | --md-sys-color-error-light: #ba1a1a;
119 | --md-sys-color-error-container-light: #ffdad6;
120 | --md-sys-color-on-error-light: #ffffff;
121 | --md-sys-color-on-error-container-light: #410002;
122 | --md-sys-color-background-light: #fefcf4;
123 | --md-sys-color-on-background-light: #1b1c17;
124 | --md-sys-color-surface-light: #fefcf4;
125 | --md-sys-color-on-surface-light: #1b1c17;
126 | --md-sys-color-surface-variant-light: #e2e4d4;
127 | --md-sys-color-on-surface-variant-light: #45483c;
128 | --md-sys-color-outline-light: #76786b;
129 | --md-sys-color-inverse-on-surface-light: #f2f1e9;
130 | --md-sys-color-inverse-surface-light: #30312c;
131 | --md-sys-color-inverse-primary-light: #b0d369;
132 | --md-sys-color-shadow-light: #000000;
133 | --md-sys-color-surface-tint-light: #4b6703;
134 | --md-sys-color-outline-variant-light: #c6c8b9;
135 | --md-sys-color-scrim-light: #000000;
136 | /* dark */
137 | --md-sys-color-primary-dark: #b0d369;
138 | --md-sys-color-on-primary-dark: #253600;
139 | --md-sys-color-primary-container-dark: #374e00;
140 | --md-sys-color-on-primary-container-dark: #ccef82;
141 | --md-sys-color-secondary-dark: #c2caaa;
142 | --md-sys-color-on-secondary-dark: #2c331d;
143 | --md-sys-color-secondary-container-dark: #424a31;
144 | --md-sys-color-on-secondary-container-dark: #dee6c5;
145 | --md-sys-color-tertiary-dark: #a0d0c8;
146 | --md-sys-color-on-tertiary-dark: #013732;
147 | --md-sys-color-tertiary-container-dark: #204e48;
148 | --md-sys-color-on-tertiary-container-dark: #bcece4;
149 | --md-sys-color-error-dark: #ffb4ab;
150 | --md-sys-color-error-container-dark: #93000a;
151 | --md-sys-color-on-error-dark: #690005;
152 | --md-sys-color-on-error-container-dark: #ffdad6;
153 | --md-sys-color-background-dark: #1b1c17;
154 | --md-sys-color-on-background-dark: #e4e3db;
155 | --md-sys-color-surface-dark: #1b1c17;
156 | --md-sys-color-on-surface-dark: #e4e3db;
157 | --md-sys-color-surface-variant-dark: #45483c;
158 | --md-sys-color-on-surface-variant-dark: #c6c8b9;
159 | --md-sys-color-outline-dark: #909284;
160 | --md-sys-color-inverse-on-surface-dark: #1b1c17;
161 | --md-sys-color-inverse-surface-dark: #e4e3db;
162 | --md-sys-color-inverse-primary-dark: #4b6703;
163 | --md-sys-color-shadow-dark: #000000;
164 | --md-sys-color-surface-tint-dark: #b0d369;
165 | --md-sys-color-outline-variant-dark: #45483c;
166 | --md-sys-color-scrim-dark: #000000;
167 | /* display - large */
168 | --md-sys-typescale-display-large-font-family-name: Roboto;
169 | --md-sys-typescale-display-large-font-family-style: Regular;
170 | --md-sys-typescale-display-large-font-weight: 400px;
171 | --md-sys-typescale-display-large-font-size: 57px;
172 | --md-sys-typescale-display-large-line-height: 64px;
173 | --md-sys-typescale-display-large-letter-spacing: -0.25px;
174 | /* display - medium */
175 | --md-sys-typescale-display-medium-font-family-name: Roboto;
176 | --md-sys-typescale-display-medium-font-family-style: Regular;
177 | --md-sys-typescale-display-medium-font-weight: 400px;
178 | --md-sys-typescale-display-medium-font-size: 45px;
179 | --md-sys-typescale-display-medium-line-height: 52px;
180 | --md-sys-typescale-display-medium-letter-spacing: 0px;
181 | /* display - small */
182 | --md-sys-typescale-display-small-font-family-name: Roboto;
183 | --md-sys-typescale-display-small-font-family-style: Regular;
184 | --md-sys-typescale-display-small-font-weight: 400px;
185 | --md-sys-typescale-display-small-font-size: 36px;
186 | --md-sys-typescale-display-small-line-height: 44px;
187 | --md-sys-typescale-display-small-letter-spacing: 0px;
188 | /* headline - large */
189 | --md-sys-typescale-headline-large-font-family-name: Roboto;
190 | --md-sys-typescale-headline-large-font-family-style: Regular;
191 | --md-sys-typescale-headline-large-font-weight: 400px;
192 | --md-sys-typescale-headline-large-font-size: 32px;
193 | --md-sys-typescale-headline-large-line-height: 40px;
194 | --md-sys-typescale-headline-large-letter-spacing: 0px;
195 | /* headline - medium */
196 | --md-sys-typescale-headline-medium-font-family-name: Roboto;
197 | --md-sys-typescale-headline-medium-font-family-style: Regular;
198 | --md-sys-typescale-headline-medium-font-weight: 400px;
199 | --md-sys-typescale-headline-medium-font-size: 28px;
200 | --md-sys-typescale-headline-medium-line-height: 36px;
201 | --md-sys-typescale-headline-medium-letter-spacing: 0px;
202 | /* headline - small */
203 | --md-sys-typescale-headline-small-font-family-name: Roboto;
204 | --md-sys-typescale-headline-small-font-family-style: Regular;
205 | --md-sys-typescale-headline-small-font-weight: 400px;
206 | --md-sys-typescale-headline-small-font-size: 24px;
207 | --md-sys-typescale-headline-small-line-height: 32px;
208 | --md-sys-typescale-headline-small-letter-spacing: 0px;
209 | /* body - large */
210 | --md-sys-typescale-body-large-font-family-name: Roboto;
211 | --md-sys-typescale-body-large-font-family-style: Regular;
212 | --md-sys-typescale-body-large-font-weight: 400px;
213 | --md-sys-typescale-body-large-font-size: 16px;
214 | --md-sys-typescale-body-large-line-height: 24px;
215 | --md-sys-typescale-body-large-letter-spacing: 0.50px;
216 | /* body - medium */
217 | --md-sys-typescale-body-medium-font-family-name: Roboto;
218 | --md-sys-typescale-body-medium-font-family-style: Regular;
219 | --md-sys-typescale-body-medium-font-weight: 400px;
220 | --md-sys-typescale-body-medium-font-size: 14px;
221 | --md-sys-typescale-body-medium-line-height: 20px;
222 | --md-sys-typescale-body-medium-letter-spacing: 0.25px;
223 | /* body - small */
224 | --md-sys-typescale-body-small-font-family-name: Roboto;
225 | --md-sys-typescale-body-small-font-family-style: Regular;
226 | --md-sys-typescale-body-small-font-weight: 400px;
227 | --md-sys-typescale-body-small-font-size: 12px;
228 | --md-sys-typescale-body-small-line-height: 16px;
229 | --md-sys-typescale-body-small-letter-spacing: 0.40px;
230 | /* label - large */
231 | --md-sys-typescale-label-large-font-family-name: Roboto;
232 | --md-sys-typescale-label-large-font-family-style: Medium;
233 | --md-sys-typescale-label-large-font-weight: 500px;
234 | --md-sys-typescale-label-large-font-size: 14px;
235 | --md-sys-typescale-label-large-line-height: 20px;
236 | --md-sys-typescale-label-large-letter-spacing: 0.10px;
237 | /* label - medium */
238 | --md-sys-typescale-label-medium-font-family-name: Roboto;
239 | --md-sys-typescale-label-medium-font-family-style: Medium;
240 | --md-sys-typescale-label-medium-font-weight: 500px;
241 | --md-sys-typescale-label-medium-font-size: 12px;
242 | --md-sys-typescale-label-medium-line-height: 16px;
243 | --md-sys-typescale-label-medium-letter-spacing: 0.50px;
244 | /* label - small */
245 | --md-sys-typescale-label-small-font-family-name: Roboto;
246 | --md-sys-typescale-label-small-font-family-style: Medium;
247 | --md-sys-typescale-label-small-font-weight: 500px;
248 | --md-sys-typescale-label-small-font-size: 11px;
249 | --md-sys-typescale-label-small-line-height: 16px;
250 | --md-sys-typescale-label-small-letter-spacing: 0.50px;
251 | /* title - large */
252 | --md-sys-typescale-title-large-font-family-name: Roboto;
253 | --md-sys-typescale-title-large-font-family-style: Regular;
254 | --md-sys-typescale-title-large-font-weight: 400px;
255 | --md-sys-typescale-title-large-font-size: 22px;
256 | --md-sys-typescale-title-large-line-height: 28px;
257 | --md-sys-typescale-title-large-letter-spacing: 0px;
258 | /* title - medium */
259 | --md-sys-typescale-title-medium-font-family-name: Roboto;
260 | --md-sys-typescale-title-medium-font-family-style: Medium;
261 | --md-sys-typescale-title-medium-font-weight: 500px;
262 | --md-sys-typescale-title-medium-font-size: 16px;
263 | --md-sys-typescale-title-medium-line-height: 24px;
264 | --md-sys-typescale-title-medium-letter-spacing: 0.15px;
265 | /* title - small */
266 | --md-sys-typescale-title-small-font-family-name: Roboto;
267 | --md-sys-typescale-title-small-font-family-style: Medium;
268 | --md-sys-typescale-title-small-font-weight: 500px;
269 | --md-sys-typescale-title-small-font-size: 14px;
270 | --md-sys-typescale-title-small-line-height: 20px;
271 | --md-sys-typescale-title-small-letter-spacing: 0.10px;
272 | }
273 |
--------------------------------------------------------------------------------
/src/js/popup.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/extensions, no-new */
2 | /* global mdc, chrome, browser, Mustache */
3 |
4 | import Browser from './Browser.js';
5 | import Bookmarks from './Bookmarks.js';
6 | import initSettingsPage from './pages/pageSettings.js';
7 | import initAboutPage from './pages/pageAbout.js';
8 | import { isSeparator, getExtensionSettings } from './utils/index.js';
9 | import { MainNavBtnIcons, MainNavBtnClasses, Theme } from './constants.js';
10 |
11 | const { MDCDialog } = mdc.dialog;
12 | const { MDCMenu } = mdc.menu;
13 | const { MDCMenuSurface } = mdc.menuSurface;
14 | const { MDCRipple } = mdc.ripple;
15 | const { MDCSnackbar } = mdc.snackbar;
16 | const { MDCTopAppBar } = mdc.topAppBar;
17 |
18 | const Classes = {
19 | MENU_OPENED: 'menu-opened', // also used for CSS rotate
20 | };
21 |
22 | const state = {
23 | duplicatesSearchResult: null,
24 | selectedDuplicates: null,
25 | };
26 |
27 | const browserInstance = new Browser(navigator.userAgent.includes('Chrome') ? chrome : browser);
28 |
29 | document.addEventListener('DOMContentLoaded', init.bind(this, false));
30 |
31 | async function init(isReInit) {
32 | await initSearchTemplate();
33 | initMDCComponents(isReInit);
34 |
35 | const appBarTitle = document.querySelector('#app-bar-title');
36 | appBarTitle.innerHTML = browserInstance.i18n.getMessage('extensionName');
37 |
38 | const menuSettingsText = document.querySelector('#menuitem-settings .mdc-list-item__text');
39 | menuSettingsText.innerHTML = browserInstance.i18n.getMessage('menu_settings');
40 |
41 | const menuAboutText = document.querySelector('#menuitem-about .mdc-list-item__text');
42 | menuAboutText.innerHTML = browserInstance.i18n.getMessage('menu_about');
43 |
44 | const findBookmarksForm = document.querySelector('#find-bookmarks');
45 | findBookmarksForm.addEventListener('submit', findBookmarksHandler);
46 |
47 | // TODO
48 | // const showAllBtn = document.querySelector('#showAll');
49 | // showAllBtn.addEventListener('click', showAllHandler)
50 |
51 | const appBarNavBtn = document.querySelector('#app-bar-nav-btn');
52 | appBarNavBtn.addEventListener('click', mainNavBtnClickHandler);
53 |
54 | if (state.duplicatesSearchResult) {
55 | await renderSearchResults(state.duplicatesSearchResult);
56 |
57 | if (state.selectedDuplicates) {
58 | state.selectedDuplicates.forEach((id) => {
59 | document.querySelector(`#${id}`).click();
60 | });
61 | }
62 | }
63 |
64 | const userSettings = await getExtensionSettings(browserInstance);
65 | document.documentElement.className = userSettings.theme === Theme.SYSTEM ? '' : userSettings.theme;
66 | }
67 |
68 | // TODO
69 | // eslint-disable-next-line no-unused-vars
70 | async function showAllHandler() {
71 | const searchResultsContainer = document.querySelector('#search-results');
72 |
73 | searchResultsContainer.hidden = false;
74 |
75 | const bookmarksTree = await browserInstance.getBookmarksTree();
76 | const bookmarksInstance = new Bookmarks(bookmarksTree);
77 | const bookmarks = bookmarksInstance.getBookmarks();
78 |
79 | const allBookmarksTemplateResponse = await fetch('templates/all-bookmarks.mustache');
80 | const allBookmarksTemplate = await allBookmarksTemplateResponse.text();
81 |
82 | searchResultsContainer.innerHTML = Mustache.render(allBookmarksTemplate, {
83 | bookmarks,
84 | });
85 |
86 | console.log(bookmarks);
87 | }
88 |
89 | async function initSearchTemplate() {
90 | const searchTemplateResponse = await fetch('templates/search-page.mustache');
91 | const searchCardTemplate = await searchTemplateResponse.text();
92 |
93 | const loaderTemplateResponse = await fetch('templates/loader.mustache');
94 | const loaderTemplate = await loaderTemplateResponse.text();
95 |
96 | document.querySelector('#main-content').innerHTML = Mustache.render(searchCardTemplate, {
97 | i18n: {
98 | btn_findDuplicates: browserInstance.i18n.getMessage('btn_findDuplicates'),
99 | btn_cancel: browserInstance.i18n.getMessage('btn_cancel'),
100 | btn_delete: browserInstance.i18n.getMessage('btn_delete'),
101 | bookmarksDeleteConfirmationTitle: browserInstance.i18n.getMessage('bookmarksDeleteConfirmationTitle'),
102 | bookmarksDeleteConfirmation: browserInstance.i18n.getMessage('bookmarksDeleteConfirmation'),
103 | bookmarksDeletedMsg: browserInstance.i18n.getMessage('bookmarksDeletedMsg'),
104 | },
105 | }, {
106 | loader: loaderTemplate,
107 | });
108 | }
109 |
110 | function initMDCComponents(isReInit) {
111 | const findButton = document.querySelector('.mdc-button');
112 | const topAppBarElement = document.querySelector('.mdc-top-app-bar');
113 |
114 | new MDCRipple(findButton);
115 | new MDCTopAppBar(topAppBarElement);
116 |
117 | if (!isReInit) {
118 | const menu = new MDCMenu(document.querySelector('.mdc-menu'));
119 | const menuBtn = document.querySelector('#app-bar-menu-btn');
120 | const menuSurface = new MDCMenuSurface(document.querySelector('.mdc-menu-surface'));
121 | menuSurface.listen('MDCMenuSurface:closed', () => menuBtn.classList.remove(Classes.MENU_OPENED));
122 | menuBtn.addEventListener('click', openMenuHandler.bind(this, menu, menuBtn));
123 | }
124 | }
125 |
126 | function mainNavBtnClickHandler() {
127 | if (this.classList.contains(MainNavBtnClasses.IS_MENU_CONTENT)) {
128 | const appBarNavBtn = document.querySelector('#app-bar-nav-btn');
129 | appBarNavBtn.innerHTML = MainNavBtnIcons.APP_MAIN;
130 |
131 | init(true);
132 | }
133 | }
134 |
135 | function openMenuHandler(menu, menuBtn) {
136 | const classes = menuBtn.classList;
137 | const classMenuOpened = Classes.MENU_OPENED;
138 |
139 | if (classes.contains(classMenuOpened)) {
140 | classes.remove(classMenuOpened);
141 | return;
142 | }
143 |
144 | classes.add(classMenuOpened);
145 | // eslint-disable-next-line no-param-reassign
146 | menu.open = true;
147 |
148 | const menuItemAbout = document.querySelector('#menuitem-about');
149 | menuItemAbout.addEventListener('click', initAboutPage.bind(this, browserInstance));
150 |
151 | const menuItemSettings = document.querySelector('#menuitem-settings');
152 | menuItemSettings.addEventListener('click', initSettingsPage.bind(this, browserInstance));
153 | }
154 |
155 | async function findBookmarksHandler(event) {
156 | event.preventDefault();
157 |
158 | const shouldClear = !!state.duplicatesSearchResult;
159 |
160 | state.duplicatesSearchResult = null;
161 |
162 | const searchResultsContainer = document.querySelector('#search-results');
163 | searchResultsContainer.hidden = false;
164 |
165 | if (shouldClear) {
166 | const searchResultsTemplate = await (await fetch('templates/search-results.mustache')).text();
167 | const loaderTemplate = await (await fetch('templates/loader.mustache')).text();
168 |
169 | searchResultsContainer.innerHTML = Mustache.render(searchResultsTemplate, {
170 | isLoader: true,
171 | }, {
172 | loader: loaderTemplate,
173 | });
174 | }
175 |
176 | const bookmarksTree = await browserInstance.getBookmarksTree();
177 | const bookmarksInstance = new Bookmarks(bookmarksTree);
178 | const bookmarks = await bookmarksInstance.getDuplicates(browserInstance);
179 |
180 | state.duplicatesSearchResult = bookmarks;
181 |
182 | await renderSearchResults(bookmarks);
183 | }
184 |
185 | async function renderSearchResults(array) {
186 | const searchResultsTemplateResponse = await fetch('templates/search-results.mustache');
187 | const searchResultsTemplate = await searchResultsTemplateResponse.text();
188 |
189 | const bookmarkCardTemplateResponse = await fetch('templates/bookmark-card.mustache');
190 | const bookmarkCardTemplate = await bookmarkCardTemplateResponse.text();
191 |
192 | const duplicatesQty = array.length;
193 | const data = {
194 | hasResults: !!duplicatesQty,
195 | cards: array.map((matches, ind) => {
196 | const isSeparatorrr = isSeparator(matches[0]?.url);
197 |
198 | return {
199 | url: isSeparatorrr
200 | ? browserInstance.i18n.getMessage('txt_separators')
201 | : prepareCardTitleUrl(matches[0]?.url),
202 | items: matches.map((match) => {
203 | const obj = { ...match };
204 | if (isSeparatorrr) {
205 | obj.title = browserInstance.i18n.getMessage('txt_separator');
206 | obj.url = '';
207 | }
208 |
209 | return obj;
210 | }),
211 | i18n: {
212 | txt_cardMatchNo: browserInstance.i18n.getMessage('txt_cardMatchNo', (ind + 1).toString()),
213 | txt_path: browserInstance.i18n.getMessage('txt_path'),
214 | },
215 | };
216 | }),
217 | i18n: {
218 | btn_deleteSelected: browserInstance.i18n.getMessage('btn_deleteSelected'),
219 | msg_FoundQty: duplicatesQty
220 | ? browserInstance.i18n.getMessage('msgFoundQty', duplicatesQty.toString())
221 | : browserInstance.i18n.getMessage('msgFoundNone'),
222 | },
223 | };
224 | const partial = {
225 | card: bookmarkCardTemplate,
226 | };
227 |
228 | const searchResultsContainer = document.querySelector('#search-results');
229 | searchResultsContainer.innerHTML = Mustache.render(searchResultsTemplate, data, partial);
230 | searchResultsContainer.hidden = false;
231 |
232 | const duplicateBookmarksForm = document.querySelector('#bookmarks-list');
233 | duplicateBookmarksForm.addEventListener('submit', deleteDuplicatesSubmitHandler);
234 |
235 | const deleteButton = duplicateBookmarksForm.querySelector('.mdc-button');
236 | new MDCRipple(deleteButton);
237 |
238 | const elementSelector = 'duplicate-checkbox';
239 | const handler = () => {
240 | const selectedCheckboxes = duplicateBookmarksForm.querySelectorAll('input[type="checkbox"]:checked');
241 | deleteButton.disabled = !selectedCheckboxes.length;
242 |
243 | state.selectedDuplicates = [];
244 | selectedCheckboxes.forEach((checkbox) => {
245 | state.selectedDuplicates.push(checkbox.id);
246 | });
247 | };
248 | searchResultsContainer.addEventListener('change', (e) => {
249 | // loop parent nodes from the target to the delegation node
250 | // eslint-disable-next-line prefer-destructuring
251 | for (let target = e.target; target && target !== this; target = target.parentNode) {
252 | if (target.classList.contains(elementSelector)) {
253 | handler.call(target, e);
254 | break;
255 | }
256 | }
257 | }, false);
258 |
259 | searchResultsContainer.querySelectorAll('.bookmark-url').forEach((elem) => {
260 | elem.addEventListener('click', (event) => {
261 | browserInstance.browser.tabs.update({
262 | url: event.target.href,
263 | });
264 | });
265 | });
266 | }
267 |
268 | function prepareCardTitleUrl(url = '') {
269 | const httpsRegex = /http(s)?:\/\//;
270 | return url.replace(httpsRegex, '');
271 | }
272 |
273 | function deleteDuplicatesSubmitHandler(event) {
274 | event.preventDefault();
275 |
276 | const form = this;
277 | const formData = new FormData(form);
278 | const selectedBookmarks = formData.getAll('duplicate');
279 |
280 | const dialog = new MDCDialog(document.querySelector('.mdc-dialog'));
281 | dialog.open();
282 | dialog.listen('MDCDialog:closed', (action) => {
283 | if (action.detail.action === 'confirm') {
284 | browserInstance.removeBookmarks(selectedBookmarks);
285 |
286 | // Hide removed bookmarks
287 | const selectedCheckboxes = form.querySelectorAll('input[type="checkbox"]:checked');
288 | selectedCheckboxes.forEach(async (checkbox) => {
289 | const parent = checkbox.closest('.duplicate-li');
290 | await hideElementWithAnimation(parent);
291 |
292 | // Hide li divider
293 | const prevSibling = parent.previousElementSibling;
294 | if (prevSibling && prevSibling.classList.contains('mdc-list-divider')) {
295 | prevSibling.style.display = 'none';
296 | }
297 | });
298 |
299 | // Hide empty cards
300 | document.querySelectorAll('.bookmark-card-content').forEach((card) => {
301 | const removedQty = card.querySelectorAll('.duplicate-li.removed').length;
302 | const allQty = card.querySelectorAll('.duplicate-li').length;
303 |
304 | if (removedQty === allQty) {
305 | hideElementWithAnimation(card);
306 | }
307 | });
308 |
309 | // Disable "Delete selected" button
310 | const deleteButton = form.querySelector('.mdc-button');
311 | deleteButton.disabled = true;
312 |
313 | // Hide alert
314 | const alert = document.querySelector('.alert-warning');
315 | if (alert) {
316 | hideElementWithAnimation(alert);
317 | }
318 |
319 | const snackbar = new MDCSnackbar(document.querySelector('.mdc-snackbar'));
320 | snackbar.open();
321 | }
322 | });
323 | }
324 |
325 | function hideElementWithAnimation(element) {
326 | element.classList.add('removed');
327 | element.addEventListener('transitionend', () => {
328 | // eslint-disable-next-line no-param-reassign
329 | element.style.display = 'none';
330 | });
331 |
332 | return Promise.resolve();
333 | }
334 |
--------------------------------------------------------------------------------
/src/css/styles.scss:
--------------------------------------------------------------------------------
1 | /* stylelint-disable selector-class-pattern */
2 |
3 | @import "generated_theme/tokens.css";
4 | @import "./loader.css";
5 |
6 | @mixin light-theme {
7 | --md-sys-color-background: var(--md-sys-color-background-light);
8 | --md-sys-color-surface-light: #F5F4E9;
9 | --mdc-theme-primary: var(--md-sys-color-primary-light);
10 | --mdc-theme-secondary: var(--md-sys-color-secondary-light);
11 | --mdc-theme-background: var(--md-sys-color-background-light);
12 | --mdc-theme-surface: var(--md-sys-color-surface-light);
13 | --mdc-theme-error: var(--md-sys-color-error-light);
14 | --mdc-theme-on-primary: var(--md-sys-color-on-primary-light);
15 | --mdc-theme-on-secondary: var(--md-sys-color-on-secondary-light);
16 | --mdc-theme-on-surface: var(--md-sys-color-on-surface-light);
17 | --mdc-theme-on-surface-variant: var(--md-sys-color-on-surface-variant-light);
18 | --mdc-theme-on-error: var(--md-sys-color-on-error-light);
19 | --md-sys-color-outline: var(--md-sys-color-outline-light);
20 | --md-sys-color-on-surface: var(--md-sys-color-on-surface-light);
21 | --md-sys-color-on-primary-container: var(--md-sys-color-on-primary-container-light);
22 | --md-sys-color-on-secondary-container: var(--md-sys-color-on-secondary-container-light);
23 | --md-sys-color-secondary-container: var(--md-sys-color-secondary-container-light);
24 |
25 | /* Override switch track */
26 | --mdc-switch-selected-track-color: var(--md-sys-color-primary-container-light);
27 | --mdc-switch-selected-hover-track-color: var(--md-sys-color-primary-container-light);
28 | --mdc-switch-selected-focus-track-color: var(--md-sys-color-primary-container-light);
29 | --mdc-switch-selected-pressed-track-color: var(--md-sys-color-primary-container-light);
30 |
31 | /* Override switch handle button */
32 | --mdc-switch-selected-handle-color: var(--mdc-theme-primary);
33 | --mdc-switch-selected-hover-handle-color: var(--mdc-theme-primary);
34 | --mdc-switch-selected-focus-handle-color: var(--mdc-theme-primary);
35 | --mdc-switch-selected-pressed-handle-color: var(--mdc-theme-primary);
36 |
37 | /* Alerts */
38 | --bs-alert-color: var(--md-sys-color-on-tertiary-container-light);
39 | --bs-alert-bg: var(--md-sys-color-tertiary-container-light);
40 | --bs-alert-border-color: var(--md-sys-color-tertiary-container-light);
41 |
42 |
43 | /* Override checkbox */
44 | --mdc-checkbox-checked-color: var(--md-sys-color-on-primary-container-light);
45 | --mdc-checkbox-unchecked-color: var(--md-sys-color-on-primary-container-light);
46 | --mdc-checkbox-ink-color: var(--md-sys-color-surface-light);
47 |
48 | /* Button override */
49 | --mdc-outlined-button-disabled-label-text-color: var(--md-sys-color-on-surface-light);
50 | --mdc-outlined-button-disabled-outline-color: var(--md-sys-color-on-surface-light);
51 | --mdc-outlined-button-outline-color: var(--md-sys-color-outline-light);
52 |
53 | --md-sys-color-outline: var(--md-sys-color-outline-light);
54 | --md-sys-color-on-surface-variant: var(--md-sys-color-on-surface-variant-light);
55 | }
56 |
57 | @mixin dark-theme {
58 | --md-sys-color-background: var(--md-sys-color-background-dark);
59 | --md-sys-color-surface-dark: #23261C;
60 | --mdc-theme-primary: var(--md-sys-color-primary-dark);
61 | --mdc-theme-secondary: var(--md-sys-color-secondary-dark);
62 |
63 | --mdc-theme-background: var(--md-sys-color-background-dark);
64 | --mdc-theme-surface: var(--md-sys-color-surface-dark);
65 | --mdc-theme-error: var(--md-sys-color-error-dark);
66 | --mdc-theme-on-primary: var(--md-sys-color-on-primary-dark);
67 | --mdc-theme-on-secondary: var(--md-sys-color-on-secondary-dark);
68 | --mdc-theme-on-surface: var(--md-sys-color-on-surface-dark);
69 | --mdc-theme-on-surface-variant: var(--md-sys-color-on-surface-variant-dark);
70 | --mdc-theme-on-error: var(--md-sys-color-on-error-dark);
71 | --mdc-theme-text-primary-on-background: var(--md-sys-color-on-surface-dark);
72 | --mdc-theme-text-secondary-on-background: var(--md-sys-color-inverse-surface-dark);
73 | --md-sys-color-outline: var(--md-sys-color-outline-dark);
74 | --md-sys-color-on-surface: var(--md-sys-color-on-surface-dark);
75 | --md-sys-color-on-primary-container: var(--md-sys-color-on-primary-container-dark);
76 | --md-sys-color-on-secondary-container: var(--md-sys-color-on-secondary-container-dark);
77 | --md-sys-color-secondary-container: var(--md-sys-color-secondary-container-dark);
78 |
79 | /* Override switch track */
80 | --mdc-switch-selected-track-color: var(--md-sys-color-primary-container-dark);
81 | --mdc-switch-selected-hover-track-color: var(--md-sys-color-primary-container-dark);
82 | --mdc-switch-selected-focus-track-color: var(--md-sys-color-primary-container-dark);
83 | --mdc-switch-selected-pressed-track-color: var(--md-sys-color-primary-container-dark);
84 |
85 | --mdc-switch-unselected-track-color: var(--md-sys-color-outline-dark);
86 | --mdc-switch-unselected-hover-track-color: var(--md-sys-color-outline-dark);
87 | --mdc-switch-unselected-focus-track-color: var(--md-sys-color-outline-dark);
88 | --mdc-switch-unselected-pressed-track-color: var(--md-sys-color-outline-dark);
89 |
90 | --mdc-switch-unselected-hover-handle-color: var(--md-sys-color-surface-variant-dark);
91 | /* --mdc-switch-unselected-focus-handle-color: var(--md-sys-color-surface-variant-dark);
92 | --mdc-switch-unselected-pressed-handle-color: var(--md-sys-color-surface-variant-dark); */
93 |
94 |
95 | --mdc-switch-unselected-icon-color: var(--md-sys-color-on-primary-container-dark);
96 | /* Override switch handle button */
97 | --mdc-switch-selected-handle-color: var(--mdc-theme-primary);
98 | --mdc-switch-selected-hover-handle-color: var(--mdc-theme-primary);
99 | --mdc-switch-selected-focus-handle-color: var(--mdc-theme-primary);
100 | --mdc-switch-selected-pressed-handle-color: var(--mdc-theme-primary);
101 |
102 | /* Alerts */
103 | --bs-alert-color: var(--md-sys-color-on-tertiary-container-dark);
104 | --bs-alert-bg: var(--md-sys-color-tertiary-container-dark);
105 | --bs-alert-border-color: var(--md-sys-color-tertiary-container-dark);
106 |
107 | /* Override checkbox */
108 | --mdc-checkbox-checked-color: var(--md-sys-color-on-primary-container-dark);
109 | --mdc-checkbox-unchecked-color: var(--md-sys-color-on-primary-container-dark);
110 | --mdc-checkbox-ink-color: var(--md-sys-color-surface-dark);
111 |
112 | /* Button override */
113 | --mdc-outlined-button-disabled-label-text-color: var(--md-sys-color-on-surface-dark);
114 | --mdc-outlined-button-disabled-outline-color: var(--md-sys-color-on-surface-dark);
115 | --mdc-outlined-button-outline-color: var(--md-sys-color-outline-dark);
116 |
117 | --md-sys-color-outline: var(--md-sys-color-outline-dark);
118 | --md-sys-color-on-surface-variant: var(--md-sys-color-on-surface-variant-dark);
119 | }
120 |
121 | // Theme: Light
122 | .light {
123 | @include light-theme;
124 | }
125 |
126 | // Theme: Dark
127 | .dark {
128 | @include dark-theme;
129 | }
130 |
131 | // Theme: OS Default
132 | :root:not(.light):not(.dark) {
133 | @media (prefers-color-scheme: light) {
134 | @include light-theme;
135 | }
136 |
137 | @media (prefers-color-scheme: dark) {
138 | @include dark-theme;
139 | }
140 | }
141 |
142 | /* Icons font */
143 | @font-face {
144 | font-family: icomoon;
145 | src: url("../fonts/icomoon.eot?46olml");
146 | src: url("../fonts/icomoon.eot?46olml#iefix") format("embedded-opentype"),
147 | url("../fonts/icomoon.ttf?46olml") format("truetype"),
148 | url("../fonts/icomoon.woff?46olml") format("woff"),
149 | url("../fonts/icomoon.svg?46olml#icomoon") format("svg");
150 | font-weight: normal;
151 | font-style: normal;
152 | font-display: block;
153 | }
154 |
155 | * {
156 | width: auto;
157 | }
158 |
159 | .icon-github,
160 | .app_icon-icon_bw {
161 | /* use !important to prevent issues with browser extensions that change fonts */
162 | /* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */
163 | font-family: icomoon !important;
164 | font-style: normal;
165 | font-weight: normal;
166 | font-variant: normal;
167 | font-size: xx-large;
168 | text-transform: none;
169 | line-height: 1;
170 | -moz-osx-font-smoothing: grayscale;
171 | }
172 |
173 | .app_icon-icon_bw::before {
174 | content: "\e901";
175 | }
176 |
177 | .icon-github::before {
178 | content: "\eab0";
179 | }
180 |
181 | /* Override Material styles */
182 | .mdc-top-app-bar {
183 | color: var(--mdc-theme-on-primary);
184 | }
185 |
186 | .mdc-text-field:not(.mdc-text-field--disabled) .mdc-floating-label {
187 | color: var(--md-sys-color-on-surface-variant);
188 | }
189 |
190 | .mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__input {
191 | color: var(--md-sys-color-on-surface);
192 | }
193 |
194 | .mdc-card {
195 | box-shadow: none;
196 | border-radius: 12px;
197 | }
198 |
199 | .mdc-list-divider {
200 | background-color: var(--md-sys-color-outline);
201 | }
202 |
203 | .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__leading,
204 | .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__notch,
205 | .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__trailing {
206 | border-color: var(--md-sys-color-on-surface-variant);
207 | }
208 |
209 | .mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__leading,
210 | .mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__notch,
211 | .mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__trailing {
212 | border-color: var(--md-sys-color-on-surface);
213 | }
214 |
215 | .mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,
216 | .mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above {
217 | color: var(--mdc-theme-primary);
218 | }
219 |
220 | .mdc-dialog .mdc-dialog__title {
221 | color: var(--mdc-theme-on-surface);
222 | }
223 |
224 | .mdc-dialog .mdc-dialog__content {
225 | color: var(--mdc-theme-on-surface-variant);
226 | }
227 |
228 | .mdc-dialog .mdc-dialog__close {
229 | color: #000;
230 | /* @alternate */
231 | color: var(--mdc-theme-on-surface, #000);
232 | }
233 |
234 | /* Custom styles */
235 | body {
236 | margin: 0;
237 | min-width: 560px;
238 | max-width: 700px;
239 | background-color: var(--mdc-theme-background);
240 | }
241 |
242 | main {
243 | margin: 0 8px;
244 | }
245 |
246 | .bookmarks-list-wrapper {
247 | overflow: hidden;
248 | }
249 |
250 | .bookmark-card__title {
251 | font-family: Roboto, sans-serif;
252 | -moz-osx-font-smoothing: grayscale;
253 | -webkit-font-smoothing: antialiased;
254 | font-size: 1.25rem;
255 | line-height: 2rem;
256 | color:var(--mdc-theme-on-surface);
257 | }
258 |
259 | .bookmark-card__subhead {
260 | font-family: Roboto, sans-serif;
261 | -moz-osx-font-smoothing: grayscale;
262 | -webkit-font-smoothing: antialiased;
263 | font-size: 0.875rem;
264 | line-height: 1.25rem;
265 | font-weight: 400;
266 | opacity: 0.6;
267 | color:var(--mdc-theme-on-surface);
268 | }
269 |
270 | .bookmark-card-content {
271 | padding: 8px 16px 0;
272 | margin: 8px 0;
273 | transition: opacity 0.3s;
274 | }
275 |
276 | .bookmark-card-content.removed {
277 | opacity: 0;
278 | }
279 |
280 | .bookmark-url {
281 | color: var(--md-sys-color-on-primary-container);
282 | }
283 |
284 | .duplicate-li {
285 | padding: 8px 0;
286 | transition: opacity 0.3s;
287 | /* overflow-x: auto; */
288 | }
289 |
290 | .duplicate-li.removed {
291 | opacity: 0;
292 | }
293 |
294 | .menu-li {
295 | height: 32px;
296 | width: auto;
297 | align-items: center;
298 | }
299 |
300 | .mdc-list-item,
301 | .mdc-list-item__secondary-text {
302 | cursor: default;
303 | }
304 | .mdc-list-item__primary-text label {
305 | cursor: pointer;
306 | }
307 |
308 | #find-bookmarks {
309 | margin: 8px 0;
310 | padding-top: 5px;
311 | }
312 |
313 | #find-bookmarks > * {
314 | margin: 4px;
315 | }
316 |
317 | #find-bookmarks .mdc-text-field__input {
318 | width: 300px;
319 | }
320 |
321 | .alert {
322 | --bs-alert-padding-x: 1rem;
323 | --bs-alert-padding-y: 1rem;
324 | --bs-alert-margin-bottom: 1rem;
325 | --bs-alert-color: inherit;
326 | --bs-alert-border-color: transparent;
327 | --bs-alert-border: 1px solid var(--bs-alert-border-color);
328 | --bs-alert-border-radius: 12px;
329 |
330 | font-family: Roboto, sans-serif;
331 | -moz-osx-font-smoothing: grayscale;
332 | -webkit-font-smoothing: antialiased;
333 | position: relative;
334 | padding: var(--bs-alert-padding-y) var(--bs-alert-padding-x);
335 | margin-bottom: var(--bs-alert-margin-bottom);
336 | color: var(--bs-alert-color);
337 | background-color: var(--bs-alert-bg);
338 | border: var(--bs-alert-border);
339 | border-radius: var(--bs-alert-border-radius);
340 | transition: opacity 0.3s;
341 | font-size: 1rem;
342 | }
343 |
344 | .alert.removed {
345 | opacity: 0;
346 | }
347 |
348 | .about-extension__main-container {
349 | color: var(--mdc-theme-on-surface);
350 | display: flex;
351 | flex-direction: row;
352 | justify-content: space-around;
353 | align-items: center;
354 | margin: 16px;
355 | }
356 |
357 | .about-extension__additional-container {
358 | display: flex;
359 | flex-direction: row;
360 | justify-content: space-around;
361 | align-items: center;
362 | padding: 16px;
363 | }
364 |
365 | .about-extension__source-img {
366 | width: 32px;
367 | height: auto;
368 | }
369 |
370 | .icon-link,
371 | .icon-link:visited {
372 | color: var(--mdc-theme-on-surface);
373 | text-decoration: none;
374 | }
375 |
376 | #app-bar-menu-btn {
377 | transition: transform .3s;
378 | }
379 |
380 | /* #app-bar-menu-btn.menu-opened {
381 | transform: rotate(90deg);
382 | } */
383 |
384 | #bookmarks-list {
385 | margin: 8px;
386 | }
387 |
388 | .mdc-list-item__text {
389 | width: 100%;
390 | }
391 |
392 | .setting-item {
393 | height: 64px;
394 | display: flex;
395 | flex-direction: row;
396 | justify-content: space-between;
397 | align-items: center;
398 | }
399 |
400 | .mdc-list-item__secondary-text::before {
401 | height: 0;
402 | }
403 |
404 | .mdc-chip {
405 | --mdc-ripple-hover-opacity: 0;
406 |
407 | border: 1px solid var(--md-sys-color-outline);
408 | border-radius: 8px;
409 | background-color: var(--mdc-theme-surface);
410 | }
411 |
412 | .mdc-chip .mdc-chip__ripple {
413 | border-radius: 8px;
414 | }
415 |
416 | .mdc-chip:hover {
417 | color: var(--mdc-theme-surface);
418 | }
419 |
420 | .mdc-chip__action {
421 | border: none;
422 | background: none;
423 | color: var(--mdc-theme-on-surface);
424 | padding: 1px 0;
425 | }
426 |
427 | .mdc-chip__icon--trailing {
428 | color: var(--mdc-theme-on-surface-variant);
429 | }
430 |
431 | .mdc-chip__icon--trailing:hover {
432 | color: var(--mdc-theme-on-surface);
433 | }
434 |
435 | .mdc-chip__icon--trailing:focus {
436 | color: var(--mdc-theme-on-surface-variant);
437 | }
438 |
439 | .mdc-button {
440 | --mdc-shape-small: 20px;
441 | --mdc-outlined-button-container-shape: 20px;
442 | --mdc-text-button-container-shape: 20px;
443 | --mdc-text-button-container-height: 40px;
444 | }
445 |
446 | .mdc-button--outlined:disabled {
447 | opacity: 0.12;
448 | }
449 |
450 | .list-item-chips .setting-item {
451 | flex-direction: column;
452 | align-items: flex-start;
453 | height: max-content;
454 | }
455 |
456 | .mdc-chip-set__chips {
457 | display: flex;
458 | width: 90vw;
459 | flex-direction: row;
460 | flex-wrap: wrap;
461 | }
462 |
463 | .mdc-menu {
464 | max-height: fit-content !important;
465 | top: 16px !important;
466 | width: max-content;
467 | }
468 |
469 | #reset-settings {
470 | margin: 1em;
471 | }
472 |
473 | .remove-ignored-url {
474 | cursor: pointer;
475 | }
476 |
477 | .settings-text-field {
478 | height: 45px;
479 | width: 320px;
480 | }
481 |
482 | .settings-button-add {
483 | height: 40px;
484 | }
485 |
486 | .settings-form {
487 | display: flex;
488 | align-items: center;
489 | justify-content: space-between;
490 | width: 100%;
491 | margin: 24px 0 5px;
492 | }
493 |
494 | // Settings Page - Segmented Buttons (for Theme)
495 | .mdc-segmented-buttons {
496 | color: var(--mdc-theme-on-surface);
497 | padding: 0;
498 | display: flex;
499 | }
500 |
501 | .mdc-button--segmented {
502 | border-radius: 0;
503 | border: 1px solid var(--md-sys-color-outline);
504 | border-left: none;
505 | padding-left: 12px;
506 | padding-right: 12px;
507 | }
508 |
509 | .mdc-button--segmented:first-child {
510 | border-left: 1px solid var(--md-sys-color-outline);
511 | border-radius: 24px 0 0 24px;
512 | }
513 |
514 | .mdc-button--segmented:last-child {
515 | border-radius: 0 24px 24px 0;
516 | }
517 |
518 | .mdc-button--segmented.selected {
519 | color: var(--md-sys-color-on-secondary-container);
520 | background-color: var(--md-sys-color-secondary-container);
521 | }
522 |
523 | .mdc-button--segmented .mdc-button__ripple {
524 | border-radius: 0;
525 | }
526 |
527 | .mdc-button--segmented:first-child .mdc-button__ripple {
528 | border-radius: 24px 0 0 24px;
529 | }
530 |
531 | .mdc-button--segmented:last-child .mdc-button__ripple {
532 | border-radius: 0 24px 24px 0;
533 | }
534 | // ---------------------------------------------
535 |
--------------------------------------------------------------------------------
/src/css/styles.css:
--------------------------------------------------------------------------------
1 | /* stylelint-disable selector-class-pattern */
2 | @import "generated_theme/tokens.css";
3 | @import "./loader.css";
4 | .light {
5 | --md-sys-color-background: var(--md-sys-color-background-light);
6 | --md-sys-color-surface-light: #F5F4E9;
7 | --mdc-theme-primary: var(--md-sys-color-primary-light);
8 | --mdc-theme-secondary: var(--md-sys-color-secondary-light);
9 | --mdc-theme-background: var(--md-sys-color-background-light);
10 | --mdc-theme-surface: var(--md-sys-color-surface-light);
11 | --mdc-theme-error: var(--md-sys-color-error-light);
12 | --mdc-theme-on-primary: var(--md-sys-color-on-primary-light);
13 | --mdc-theme-on-secondary: var(--md-sys-color-on-secondary-light);
14 | --mdc-theme-on-surface: var(--md-sys-color-on-surface-light);
15 | --mdc-theme-on-surface-variant: var(--md-sys-color-on-surface-variant-light);
16 | --mdc-theme-on-error: var(--md-sys-color-on-error-light);
17 | --md-sys-color-outline: var(--md-sys-color-outline-light);
18 | --md-sys-color-on-surface: var(--md-sys-color-on-surface-light);
19 | --md-sys-color-on-primary-container: var(--md-sys-color-on-primary-container-light);
20 | --md-sys-color-on-secondary-container: var(--md-sys-color-on-secondary-container-light);
21 | --md-sys-color-secondary-container: var(--md-sys-color-secondary-container-light);
22 | /* Override switch track */
23 | --mdc-switch-selected-track-color: var(--md-sys-color-primary-container-light);
24 | --mdc-switch-selected-hover-track-color: var(--md-sys-color-primary-container-light);
25 | --mdc-switch-selected-focus-track-color: var(--md-sys-color-primary-container-light);
26 | --mdc-switch-selected-pressed-track-color: var(--md-sys-color-primary-container-light);
27 | /* Override switch handle button */
28 | --mdc-switch-selected-handle-color: var(--mdc-theme-primary);
29 | --mdc-switch-selected-hover-handle-color: var(--mdc-theme-primary);
30 | --mdc-switch-selected-focus-handle-color: var(--mdc-theme-primary);
31 | --mdc-switch-selected-pressed-handle-color: var(--mdc-theme-primary);
32 | /* Alerts */
33 | --bs-alert-color: var(--md-sys-color-on-tertiary-container-light);
34 | --bs-alert-bg: var(--md-sys-color-tertiary-container-light);
35 | --bs-alert-border-color: var(--md-sys-color-tertiary-container-light);
36 | /* Override checkbox */
37 | --mdc-checkbox-checked-color: var(--md-sys-color-on-primary-container-light);
38 | --mdc-checkbox-unchecked-color: var(--md-sys-color-on-primary-container-light);
39 | --mdc-checkbox-ink-color: var(--md-sys-color-surface-light);
40 | /* Button override */
41 | --mdc-outlined-button-disabled-label-text-color: var(--md-sys-color-on-surface-light);
42 | --mdc-outlined-button-disabled-outline-color: var(--md-sys-color-on-surface-light);
43 | --mdc-outlined-button-outline-color: var(--md-sys-color-outline-light);
44 | --md-sys-color-outline: var(--md-sys-color-outline-light);
45 | --md-sys-color-on-surface-variant: var(--md-sys-color-on-surface-variant-light);
46 | }
47 |
48 | .dark {
49 | --md-sys-color-background: var(--md-sys-color-background-dark);
50 | --md-sys-color-surface-dark: #23261C;
51 | --mdc-theme-primary: var(--md-sys-color-primary-dark);
52 | --mdc-theme-secondary: var(--md-sys-color-secondary-dark);
53 | --mdc-theme-background: var(--md-sys-color-background-dark);
54 | --mdc-theme-surface: var(--md-sys-color-surface-dark);
55 | --mdc-theme-error: var(--md-sys-color-error-dark);
56 | --mdc-theme-on-primary: var(--md-sys-color-on-primary-dark);
57 | --mdc-theme-on-secondary: var(--md-sys-color-on-secondary-dark);
58 | --mdc-theme-on-surface: var(--md-sys-color-on-surface-dark);
59 | --mdc-theme-on-surface-variant: var(--md-sys-color-on-surface-variant-dark);
60 | --mdc-theme-on-error: var(--md-sys-color-on-error-dark);
61 | --mdc-theme-text-primary-on-background: var(--md-sys-color-on-surface-dark);
62 | --mdc-theme-text-secondary-on-background: var(--md-sys-color-inverse-surface-dark);
63 | --md-sys-color-outline: var(--md-sys-color-outline-dark);
64 | --md-sys-color-on-surface: var(--md-sys-color-on-surface-dark);
65 | --md-sys-color-on-primary-container: var(--md-sys-color-on-primary-container-dark);
66 | --md-sys-color-on-secondary-container: var(--md-sys-color-on-secondary-container-dark);
67 | --md-sys-color-secondary-container: var(--md-sys-color-secondary-container-dark);
68 | /* Override switch track */
69 | --mdc-switch-selected-track-color: var(--md-sys-color-primary-container-dark);
70 | --mdc-switch-selected-hover-track-color: var(--md-sys-color-primary-container-dark);
71 | --mdc-switch-selected-focus-track-color: var(--md-sys-color-primary-container-dark);
72 | --mdc-switch-selected-pressed-track-color: var(--md-sys-color-primary-container-dark);
73 | --mdc-switch-unselected-track-color: var(--md-sys-color-outline-dark);
74 | --mdc-switch-unselected-hover-track-color: var(--md-sys-color-outline-dark);
75 | --mdc-switch-unselected-focus-track-color: var(--md-sys-color-outline-dark);
76 | --mdc-switch-unselected-pressed-track-color: var(--md-sys-color-outline-dark);
77 | --mdc-switch-unselected-hover-handle-color: var(--md-sys-color-surface-variant-dark);
78 | /* --mdc-switch-unselected-focus-handle-color: var(--md-sys-color-surface-variant-dark);
79 | --mdc-switch-unselected-pressed-handle-color: var(--md-sys-color-surface-variant-dark); */
80 | --mdc-switch-unselected-icon-color: var(--md-sys-color-on-primary-container-dark);
81 | /* Override switch handle button */
82 | --mdc-switch-selected-handle-color: var(--mdc-theme-primary);
83 | --mdc-switch-selected-hover-handle-color: var(--mdc-theme-primary);
84 | --mdc-switch-selected-focus-handle-color: var(--mdc-theme-primary);
85 | --mdc-switch-selected-pressed-handle-color: var(--mdc-theme-primary);
86 | /* Alerts */
87 | --bs-alert-color: var(--md-sys-color-on-tertiary-container-dark);
88 | --bs-alert-bg: var(--md-sys-color-tertiary-container-dark);
89 | --bs-alert-border-color: var(--md-sys-color-tertiary-container-dark);
90 | /* Override checkbox */
91 | --mdc-checkbox-checked-color: var(--md-sys-color-on-primary-container-dark);
92 | --mdc-checkbox-unchecked-color: var(--md-sys-color-on-primary-container-dark);
93 | --mdc-checkbox-ink-color: var(--md-sys-color-surface-dark);
94 | /* Button override */
95 | --mdc-outlined-button-disabled-label-text-color: var(--md-sys-color-on-surface-dark);
96 | --mdc-outlined-button-disabled-outline-color: var(--md-sys-color-on-surface-dark);
97 | --mdc-outlined-button-outline-color: var(--md-sys-color-outline-dark);
98 | --md-sys-color-outline: var(--md-sys-color-outline-dark);
99 | --md-sys-color-on-surface-variant: var(--md-sys-color-on-surface-variant-dark);
100 | }
101 |
102 | @media (prefers-color-scheme: light) {
103 | :root:not(.light):not(.dark) {
104 | --md-sys-color-background: var(--md-sys-color-background-light);
105 | --md-sys-color-surface-light: #F5F4E9;
106 | --mdc-theme-primary: var(--md-sys-color-primary-light);
107 | --mdc-theme-secondary: var(--md-sys-color-secondary-light);
108 | --mdc-theme-background: var(--md-sys-color-background-light);
109 | --mdc-theme-surface: var(--md-sys-color-surface-light);
110 | --mdc-theme-error: var(--md-sys-color-error-light);
111 | --mdc-theme-on-primary: var(--md-sys-color-on-primary-light);
112 | --mdc-theme-on-secondary: var(--md-sys-color-on-secondary-light);
113 | --mdc-theme-on-surface: var(--md-sys-color-on-surface-light);
114 | --mdc-theme-on-surface-variant: var(--md-sys-color-on-surface-variant-light);
115 | --mdc-theme-on-error: var(--md-sys-color-on-error-light);
116 | --md-sys-color-outline: var(--md-sys-color-outline-light);
117 | --md-sys-color-on-surface: var(--md-sys-color-on-surface-light);
118 | --md-sys-color-on-primary-container: var(--md-sys-color-on-primary-container-light);
119 | --md-sys-color-on-secondary-container: var(--md-sys-color-on-secondary-container-light);
120 | --md-sys-color-secondary-container: var(--md-sys-color-secondary-container-light);
121 | /* Override switch track */
122 | --mdc-switch-selected-track-color: var(--md-sys-color-primary-container-light);
123 | --mdc-switch-selected-hover-track-color: var(--md-sys-color-primary-container-light);
124 | --mdc-switch-selected-focus-track-color: var(--md-sys-color-primary-container-light);
125 | --mdc-switch-selected-pressed-track-color: var(--md-sys-color-primary-container-light);
126 | /* Override switch handle button */
127 | --mdc-switch-selected-handle-color: var(--mdc-theme-primary);
128 | --mdc-switch-selected-hover-handle-color: var(--mdc-theme-primary);
129 | --mdc-switch-selected-focus-handle-color: var(--mdc-theme-primary);
130 | --mdc-switch-selected-pressed-handle-color: var(--mdc-theme-primary);
131 | /* Alerts */
132 | --bs-alert-color: var(--md-sys-color-on-tertiary-container-light);
133 | --bs-alert-bg: var(--md-sys-color-tertiary-container-light);
134 | --bs-alert-border-color: var(--md-sys-color-tertiary-container-light);
135 | /* Override checkbox */
136 | --mdc-checkbox-checked-color: var(--md-sys-color-on-primary-container-light);
137 | --mdc-checkbox-unchecked-color: var(--md-sys-color-on-primary-container-light);
138 | --mdc-checkbox-ink-color: var(--md-sys-color-surface-light);
139 | /* Button override */
140 | --mdc-outlined-button-disabled-label-text-color: var(--md-sys-color-on-surface-light);
141 | --mdc-outlined-button-disabled-outline-color: var(--md-sys-color-on-surface-light);
142 | --mdc-outlined-button-outline-color: var(--md-sys-color-outline-light);
143 | --md-sys-color-outline: var(--md-sys-color-outline-light);
144 | --md-sys-color-on-surface-variant: var(--md-sys-color-on-surface-variant-light);
145 | }
146 | }
147 | @media (prefers-color-scheme: dark) {
148 | :root:not(.light):not(.dark) {
149 | --md-sys-color-background: var(--md-sys-color-background-dark);
150 | --md-sys-color-surface-dark: #23261C;
151 | --mdc-theme-primary: var(--md-sys-color-primary-dark);
152 | --mdc-theme-secondary: var(--md-sys-color-secondary-dark);
153 | --mdc-theme-background: var(--md-sys-color-background-dark);
154 | --mdc-theme-surface: var(--md-sys-color-surface-dark);
155 | --mdc-theme-error: var(--md-sys-color-error-dark);
156 | --mdc-theme-on-primary: var(--md-sys-color-on-primary-dark);
157 | --mdc-theme-on-secondary: var(--md-sys-color-on-secondary-dark);
158 | --mdc-theme-on-surface: var(--md-sys-color-on-surface-dark);
159 | --mdc-theme-on-surface-variant: var(--md-sys-color-on-surface-variant-dark);
160 | --mdc-theme-on-error: var(--md-sys-color-on-error-dark);
161 | --mdc-theme-text-primary-on-background: var(--md-sys-color-on-surface-dark);
162 | --mdc-theme-text-secondary-on-background: var(--md-sys-color-inverse-surface-dark);
163 | --md-sys-color-outline: var(--md-sys-color-outline-dark);
164 | --md-sys-color-on-surface: var(--md-sys-color-on-surface-dark);
165 | --md-sys-color-on-primary-container: var(--md-sys-color-on-primary-container-dark);
166 | --md-sys-color-on-secondary-container: var(--md-sys-color-on-secondary-container-dark);
167 | --md-sys-color-secondary-container: var(--md-sys-color-secondary-container-dark);
168 | /* Override switch track */
169 | --mdc-switch-selected-track-color: var(--md-sys-color-primary-container-dark);
170 | --mdc-switch-selected-hover-track-color: var(--md-sys-color-primary-container-dark);
171 | --mdc-switch-selected-focus-track-color: var(--md-sys-color-primary-container-dark);
172 | --mdc-switch-selected-pressed-track-color: var(--md-sys-color-primary-container-dark);
173 | --mdc-switch-unselected-track-color: var(--md-sys-color-outline-dark);
174 | --mdc-switch-unselected-hover-track-color: var(--md-sys-color-outline-dark);
175 | --mdc-switch-unselected-focus-track-color: var(--md-sys-color-outline-dark);
176 | --mdc-switch-unselected-pressed-track-color: var(--md-sys-color-outline-dark);
177 | --mdc-switch-unselected-hover-handle-color: var(--md-sys-color-surface-variant-dark);
178 | /* --mdc-switch-unselected-focus-handle-color: var(--md-sys-color-surface-variant-dark);
179 | --mdc-switch-unselected-pressed-handle-color: var(--md-sys-color-surface-variant-dark); */
180 | --mdc-switch-unselected-icon-color: var(--md-sys-color-on-primary-container-dark);
181 | /* Override switch handle button */
182 | --mdc-switch-selected-handle-color: var(--mdc-theme-primary);
183 | --mdc-switch-selected-hover-handle-color: var(--mdc-theme-primary);
184 | --mdc-switch-selected-focus-handle-color: var(--mdc-theme-primary);
185 | --mdc-switch-selected-pressed-handle-color: var(--mdc-theme-primary);
186 | /* Alerts */
187 | --bs-alert-color: var(--md-sys-color-on-tertiary-container-dark);
188 | --bs-alert-bg: var(--md-sys-color-tertiary-container-dark);
189 | --bs-alert-border-color: var(--md-sys-color-tertiary-container-dark);
190 | /* Override checkbox */
191 | --mdc-checkbox-checked-color: var(--md-sys-color-on-primary-container-dark);
192 | --mdc-checkbox-unchecked-color: var(--md-sys-color-on-primary-container-dark);
193 | --mdc-checkbox-ink-color: var(--md-sys-color-surface-dark);
194 | /* Button override */
195 | --mdc-outlined-button-disabled-label-text-color: var(--md-sys-color-on-surface-dark);
196 | --mdc-outlined-button-disabled-outline-color: var(--md-sys-color-on-surface-dark);
197 | --mdc-outlined-button-outline-color: var(--md-sys-color-outline-dark);
198 | --md-sys-color-outline: var(--md-sys-color-outline-dark);
199 | --md-sys-color-on-surface-variant: var(--md-sys-color-on-surface-variant-dark);
200 | }
201 | }
202 |
203 | /* Icons font */
204 | @font-face {
205 | font-family: icomoon;
206 | src: url("../fonts/icomoon.eot?46olml");
207 | src: url("../fonts/icomoon.eot?46olml#iefix") format("embedded-opentype"), url("../fonts/icomoon.ttf?46olml") format("truetype"), url("../fonts/icomoon.woff?46olml") format("woff"), url("../fonts/icomoon.svg?46olml#icomoon") format("svg");
208 | font-weight: normal;
209 | font-style: normal;
210 | font-display: block;
211 | }
212 | * {
213 | width: auto;
214 | }
215 |
216 | .icon-github,
217 | .app_icon-icon_bw {
218 | /* use !important to prevent issues with browser extensions that change fonts */
219 | /* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */
220 | font-family: icomoon !important;
221 | font-style: normal;
222 | font-weight: normal;
223 | font-variant: normal;
224 | font-size: xx-large;
225 | text-transform: none;
226 | line-height: 1;
227 | -moz-osx-font-smoothing: grayscale;
228 | }
229 |
230 | .app_icon-icon_bw::before {
231 | content: "\e901";
232 | }
233 |
234 | .icon-github::before {
235 | content: "\eab0";
236 | }
237 |
238 | /* Override Material styles */
239 | .mdc-top-app-bar {
240 | color: var(--mdc-theme-on-primary);
241 | }
242 |
243 | .mdc-text-field:not(.mdc-text-field--disabled) .mdc-floating-label {
244 | color: var(--md-sys-color-on-surface-variant);
245 | }
246 |
247 | .mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__input {
248 | color: var(--md-sys-color-on-surface);
249 | }
250 |
251 | .mdc-card {
252 | box-shadow: none;
253 | border-radius: 12px;
254 | }
255 |
256 | .mdc-list-divider {
257 | background-color: var(--md-sys-color-outline);
258 | }
259 |
260 | .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__leading,
261 | .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__notch,
262 | .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__trailing {
263 | border-color: var(--md-sys-color-on-surface-variant);
264 | }
265 |
266 | .mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__leading,
267 | .mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__notch,
268 | .mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__trailing {
269 | border-color: var(--md-sys-color-on-surface);
270 | }
271 |
272 | .mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,
273 | .mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above {
274 | color: var(--mdc-theme-primary);
275 | }
276 |
277 | .mdc-dialog .mdc-dialog__title {
278 | color: var(--mdc-theme-on-surface);
279 | }
280 |
281 | .mdc-dialog .mdc-dialog__content {
282 | color: var(--mdc-theme-on-surface-variant);
283 | }
284 |
285 | .mdc-dialog .mdc-dialog__close {
286 | color: #000;
287 | /* @alternate */
288 | color: var(--mdc-theme-on-surface, #000);
289 | }
290 |
291 | /* Custom styles */
292 | body {
293 | margin: 0;
294 | min-width: 560px;
295 | max-width: 700px;
296 | background-color: var(--mdc-theme-background);
297 | }
298 |
299 | main {
300 | margin: 0 8px;
301 | }
302 |
303 | .bookmarks-list-wrapper {
304 | overflow: hidden;
305 | }
306 |
307 | .bookmark-card__title {
308 | font-family: Roboto, sans-serif;
309 | -moz-osx-font-smoothing: grayscale;
310 | -webkit-font-smoothing: antialiased;
311 | font-size: 1.25rem;
312 | line-height: 2rem;
313 | color: var(--mdc-theme-on-surface);
314 | }
315 |
316 | .bookmark-card__subhead {
317 | font-family: Roboto, sans-serif;
318 | -moz-osx-font-smoothing: grayscale;
319 | -webkit-font-smoothing: antialiased;
320 | font-size: 0.875rem;
321 | line-height: 1.25rem;
322 | font-weight: 400;
323 | opacity: 0.6;
324 | color: var(--mdc-theme-on-surface);
325 | }
326 |
327 | .bookmark-card-content {
328 | padding: 8px 16px 0;
329 | margin: 8px 0;
330 | transition: opacity 0.3s;
331 | }
332 |
333 | .bookmark-card-content.removed {
334 | opacity: 0;
335 | }
336 |
337 | .bookmark-url {
338 | color: var(--md-sys-color-on-primary-container);
339 | }
340 |
341 | .duplicate-li {
342 | padding: 8px 0;
343 | transition: opacity 0.3s;
344 | /* overflow-x: auto; */
345 | }
346 |
347 | .duplicate-li.removed {
348 | opacity: 0;
349 | }
350 |
351 | .menu-li {
352 | height: 32px;
353 | width: auto;
354 | align-items: center;
355 | }
356 |
357 | .mdc-list-item,
358 | .mdc-list-item__secondary-text {
359 | cursor: default;
360 | }
361 |
362 | .mdc-list-item__primary-text label {
363 | cursor: pointer;
364 | }
365 |
366 | #find-bookmarks {
367 | margin: 8px 0;
368 | padding-top: 5px;
369 | }
370 |
371 | #find-bookmarks > * {
372 | margin: 4px;
373 | }
374 |
375 | #find-bookmarks .mdc-text-field__input {
376 | width: 300px;
377 | }
378 |
379 | .alert {
380 | --bs-alert-padding-x: 1rem;
381 | --bs-alert-padding-y: 1rem;
382 | --bs-alert-margin-bottom: 1rem;
383 | --bs-alert-color: inherit;
384 | --bs-alert-border-color: transparent;
385 | --bs-alert-border: 1px solid var(--bs-alert-border-color);
386 | --bs-alert-border-radius: 12px;
387 | font-family: Roboto, sans-serif;
388 | -moz-osx-font-smoothing: grayscale;
389 | -webkit-font-smoothing: antialiased;
390 | position: relative;
391 | padding: var(--bs-alert-padding-y) var(--bs-alert-padding-x);
392 | margin-bottom: var(--bs-alert-margin-bottom);
393 | color: var(--bs-alert-color);
394 | background-color: var(--bs-alert-bg);
395 | border: var(--bs-alert-border);
396 | border-radius: var(--bs-alert-border-radius);
397 | transition: opacity 0.3s;
398 | font-size: 1rem;
399 | }
400 |
401 | .alert.removed {
402 | opacity: 0;
403 | }
404 |
405 | .about-extension__main-container {
406 | color: var(--mdc-theme-on-surface);
407 | display: flex;
408 | flex-direction: row;
409 | justify-content: space-around;
410 | align-items: center;
411 | margin: 16px;
412 | }
413 |
414 | .about-extension__additional-container {
415 | display: flex;
416 | flex-direction: row;
417 | justify-content: space-around;
418 | align-items: center;
419 | padding: 16px;
420 | }
421 |
422 | .about-extension__source-img {
423 | width: 32px;
424 | height: auto;
425 | }
426 |
427 | .icon-link,
428 | .icon-link:visited {
429 | color: var(--mdc-theme-on-surface);
430 | text-decoration: none;
431 | }
432 |
433 | #app-bar-menu-btn {
434 | transition: transform 0.3s;
435 | }
436 |
437 | /* #app-bar-menu-btn.menu-opened {
438 | transform: rotate(90deg);
439 | } */
440 | #bookmarks-list {
441 | margin: 8px;
442 | }
443 |
444 | .mdc-list-item__text {
445 | width: 100%;
446 | }
447 |
448 | .setting-item {
449 | height: 64px;
450 | display: flex;
451 | flex-direction: row;
452 | justify-content: space-between;
453 | align-items: center;
454 | }
455 |
456 | .mdc-list-item__secondary-text::before {
457 | height: 0;
458 | }
459 |
460 | .mdc-chip {
461 | --mdc-ripple-hover-opacity: 0;
462 | border: 1px solid var(--md-sys-color-outline);
463 | border-radius: 8px;
464 | background-color: var(--mdc-theme-surface);
465 | }
466 |
467 | .mdc-chip .mdc-chip__ripple {
468 | border-radius: 8px;
469 | }
470 |
471 | .mdc-chip:hover {
472 | color: var(--mdc-theme-surface);
473 | }
474 |
475 | .mdc-chip__action {
476 | border: none;
477 | background: none;
478 | color: var(--mdc-theme-on-surface);
479 | padding: 1px 0;
480 | }
481 |
482 | .mdc-chip__icon--trailing {
483 | color: var(--mdc-theme-on-surface-variant);
484 | }
485 |
486 | .mdc-chip__icon--trailing:hover {
487 | color: var(--mdc-theme-on-surface);
488 | }
489 |
490 | .mdc-chip__icon--trailing:focus {
491 | color: var(--mdc-theme-on-surface-variant);
492 | }
493 |
494 | .mdc-button {
495 | --mdc-shape-small: 20px;
496 | --mdc-outlined-button-container-shape: 20px;
497 | --mdc-text-button-container-shape: 20px;
498 | --mdc-text-button-container-height: 40px;
499 | }
500 |
501 | .mdc-button--outlined:disabled {
502 | opacity: 0.12;
503 | }
504 |
505 | .list-item-chips .setting-item {
506 | flex-direction: column;
507 | align-items: flex-start;
508 | height: max-content;
509 | }
510 |
511 | .mdc-chip-set__chips {
512 | display: flex;
513 | width: 90vw;
514 | flex-direction: row;
515 | flex-wrap: wrap;
516 | }
517 |
518 | .mdc-menu {
519 | max-height: fit-content !important;
520 | top: 16px !important;
521 | width: max-content;
522 | }
523 |
524 | #reset-settings {
525 | margin: 1em;
526 | }
527 |
528 | .remove-ignored-url {
529 | cursor: pointer;
530 | }
531 |
532 | .settings-text-field {
533 | height: 45px;
534 | width: 320px;
535 | }
536 |
537 | .settings-button-add {
538 | height: 40px;
539 | }
540 |
541 | .settings-form {
542 | display: flex;
543 | align-items: center;
544 | justify-content: space-between;
545 | width: 100%;
546 | margin: 24px 0 5px;
547 | }
548 |
549 | .mdc-segmented-buttons {
550 | color: var(--mdc-theme-on-surface);
551 | padding: 0;
552 | display: flex;
553 | }
554 |
555 | .mdc-button--segmented {
556 | border-radius: 0;
557 | border: 1px solid var(--md-sys-color-outline);
558 | border-left: none;
559 | padding-left: 12px;
560 | padding-right: 12px;
561 | }
562 |
563 | .mdc-button--segmented:first-child {
564 | border-left: 1px solid var(--md-sys-color-outline);
565 | border-radius: 24px 0 0 24px;
566 | }
567 |
568 | .mdc-button--segmented:last-child {
569 | border-radius: 0 24px 24px 0;
570 | }
571 |
572 | .mdc-button--segmented.selected {
573 | color: var(--md-sys-color-on-secondary-container);
574 | background-color: var(--md-sys-color-secondary-container);
575 | }
576 |
577 | .mdc-button--segmented .mdc-button__ripple {
578 | border-radius: 0;
579 | }
580 |
581 | .mdc-button--segmented:first-child .mdc-button__ripple {
582 | border-radius: 24px 0 0 24px;
583 | }
584 |
585 | .mdc-button--segmented:last-child .mdc-button__ripple {
586 | border-radius: 0 24px 24px 0;
587 | }
588 |
589 | /*# sourceMappingURL=styles.css.map */
590 |
--------------------------------------------------------------------------------