├── .editorconfig
├── .eslintrc.json
├── .github
├── FUNDING.yml
├── pull_request_template.md
└── workflows
│ ├── ci.yml
│ ├── release.yml
│ ├── take-action.yml
│ ├── tests.yml
│ ├── update-oss-attribution.yml
│ └── updateInvidous.yml
├── .gitignore
├── .gitmodules
├── LICENSE
├── LICENSE-APPSTORE.txt
├── LICENSE-HISTORY.txt
├── README.md
├── ci
├── invidiousCI.ts
├── invidiouslist.json
└── prettify.ts
├── config.json.example
├── jest.config.js
├── manifest
├── beta-manifest-extra.json
├── chrome-manifest-extra.json
├── firefox-beta-manifest-extra.json
├── firefox-manifest-extra.json
├── manifest-v2-extra.json
├── manifest.json
└── safari-manifest-extra.json
├── oss-attribution
└── licenseInfos.json
├── package-lock.json
├── package.json
├── public
├── casual.css
├── content.css
├── help.css
├── help.html
├── icons
│ ├── add.svg
│ ├── close.png
│ ├── heart.svg
│ ├── help.svg
│ ├── logo-1024.png
│ ├── logo-128.png
│ ├── logo-16.png
│ ├── logo-256.png
│ ├── logo-2r.svg
│ ├── logo-32.png
│ ├── logo-512.png
│ ├── logo-64.png
│ ├── logo-casual.svg
│ ├── logo.svg
│ ├── newprofilepic.jpg
│ ├── not_visible.svg
│ ├── pause.svg
│ ├── refresh.svg
│ ├── remove.svg
│ ├── settings.svg
│ ├── thumbs_down_locked.svg
│ └── visible.svg
├── options
│ ├── options.css
│ └── options.html
├── oss-attribution
│ ├── .gitkeep
│ └── attribution.txt
├── payment.html
├── popup.css
├── popup.html
└── shared.css
├── src
├── background.ts
├── config
│ ├── channelOverrides.ts
│ ├── config.ts
│ └── stats.ts
├── content.ts
├── dataFetching.ts
├── document.ts
├── documentScriptInjector.ts
├── help
│ ├── HelpComponent.tsx
│ ├── PaymentComponent.tsx
│ ├── help.tsx
│ └── payment.tsx
├── license
│ ├── LicenseComponent.tsx
│ └── license.ts
├── options.ts
├── options
│ ├── CasualChoice.tsx
│ ├── CasualChoiceComponent.tsx
│ ├── ChannelOverrides.tsx
│ ├── ChannelOverridesComponent.tsx
│ ├── KeybindComponent.tsx
│ └── KeybindDialogComponent.tsx
├── popup
│ ├── FormattedTextComponent.tsx
│ ├── FormattingOptionsComponent.tsx
│ ├── PopupComponent.tsx
│ ├── SelectOptionComponent.tsx
│ ├── ToggleOptionComponent.tsx
│ ├── YourWorkComponent.tsx
│ └── popup.tsx
├── submission
│ ├── BrandingPreviewComponent.tsx
│ ├── CasualVoteComponent.tsx
│ ├── CasualVoteOnboardingComponent.tsx
│ ├── SubmissionChecklist.tsx
│ ├── SubmissionComponent.tsx
│ ├── ThumbnailComponent.tsx
│ ├── ThumbnailDrawerComponent.tsx
│ ├── ThumbnailSelectionComponent.tsx
│ ├── TitleComponent.tsx
│ ├── TitleDrawerComponent.tsx
│ ├── autoWarning.ts
│ ├── casualVote.const.ts
│ ├── casualVoteButton.tsx
│ ├── submitButton.tsx
│ └── titleButton.tsx
├── svgIcons
│ ├── addIcon.tsx
│ ├── checkIcon.tsx
│ ├── clipboardIcon.tsx
│ ├── cursorIcon.tsx
│ ├── downvoteIcon.tsx
│ ├── exclamationIcon.tsx
│ ├── fontIcon.tsx
│ ├── pencilIcon.tsx
│ ├── personIcon.tsx
│ ├── questionIcon.tsx
│ ├── resetIcon.tsx
│ └── upvoteIcon.tsx
├── thumbnails
│ ├── thumbnailData.ts
│ ├── thumbnailDataCache.ts
│ └── thumbnailRenderer.ts
├── titles
│ ├── pageTitleHandler.ts
│ ├── titleAntiTranslateData.ts
│ ├── titleData.ts
│ ├── titleFormatter.ts
│ ├── titleFormatterData.ts
│ └── titleRenderer.ts
├── types
│ └── messaging.ts
├── unactivatedWarning.ts
├── utils.ts
├── utils
│ ├── configUtils.ts
│ ├── cssInjector.ts
│ ├── extensionCompatibility.ts
│ ├── keybinds.ts
│ ├── logger.ts
│ ├── pageCleaner.ts
│ ├── requests.ts
│ ├── titleBar.ts
│ └── tooltip.tsx
├── video.ts
└── videoBranding
│ ├── mediaSessionHandler.ts
│ ├── notificationHandler.ts
│ ├── onboarding.tsx
│ ├── videoBranding.ts
│ └── watchPageBrandingHandler.ts
├── test
├── selenium.test.ts
└── titleFormatter.test.ts
├── tsconfig-production.json
├── tsconfig.json
└── webpack
├── webpack.common.js
├── webpack.dev.js
├── webpack.manifest.js
└── webpack.prod.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: https://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # Unix-style newlines with a newline ending every file
7 | [*]
8 | end_of_line = lf
9 | insert_final_newline = true
10 |
11 | [*.{js,json,ts,tsx}]
12 | charset = utf-8
13 | indent_style = space
14 | indent_size = 4
15 |
16 | [package.json]
17 | indent_style = space
18 | indent_size = 2
19 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es2021": true,
5 | "node": true,
6 | "jest": true
7 | },
8 | "extends": [
9 | "eslint:recommended",
10 | "plugin:react/recommended",
11 | "plugin:@typescript-eslint/recommended"
12 | ],
13 | "parser": "@typescript-eslint/parser",
14 | "parserOptions": {
15 | "ecmaFeatures": {
16 | "jsx": true
17 | },
18 | "ecmaVersion": 12,
19 | "sourceType": "module"
20 | },
21 | "plugins": ["react", "@typescript-eslint"],
22 | "rules": {
23 | "@typescript-eslint/no-unused-vars": "error",
24 | "no-self-assign": "off",
25 | "@typescript-eslint/no-empty-interface": "off",
26 | "react/prop-types": [2, { "ignore": ["children"] }],
27 | "@typescript-eslint/member-delimiter-style": "warn",
28 | "require-await": "warn",
29 | "@typescript-eslint/no-non-null-assertion": "off",
30 | "@typescript-eslint/no-this-alias": "off",
31 | "@typescript-eslint/ban-ts-comment": "off"
32 | },
33 | "settings": {
34 | "react": {
35 | "version": "detect"
36 | }
37 | },
38 | "overrides": [
39 | {
40 | "files": ["src/**/*.ts"],
41 |
42 | "parserOptions": {
43 | "project": ["./tsconfig.json"]
44 | },
45 |
46 | "rules": {
47 | "@typescript-eslint/no-misused-promises": "warn",
48 | "@typescript-eslint/no-floating-promises" : "warn"
49 | }
50 | }
51 | ]
52 | }
53 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: ajayyy-org
2 | patreon: ajayyy
3 | custom: [dearrow.ajay.app/donate, theajayyy.itch.io/dearrow]
4 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | - [ ] I agree to license my contribution under GPL-3.0 and agree to allow distribution on app stores as outlined in [LICENSE-APPSTORE](https://github.com/ajayyy/DeArrow/blob/master/LICENSE-APPSTORE.txt)
2 |
3 | To test this pull request, follow the [instructions in the wiki](https://github.com/ajayyy/SponsorBlock/wiki/Testing-a-Pull-Request).
4 |
5 | ***
6 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 |
7 | build:
8 | name: Create artifacts
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | # Initialization
13 | - uses: actions/checkout@v4
14 | with:
15 | submodules: recursive
16 | - uses: actions/setup-node@v4
17 | with:
18 | node-version: '18'
19 | - run: npm ci
20 | - name: Copy configuration
21 | run: cp config.json.example config.json
22 |
23 | # Run linter
24 | - name: Lint
25 | run: npm run lint
26 |
27 | # Create Chrome artifacts
28 | - name: Create Chrome artifacts
29 | run: npm run build:chrome
30 | - uses: actions/upload-artifact@v4
31 | with:
32 | name: ChromeExtension
33 | path: dist
34 | - run: mkdir ./builds
35 | - uses: montudor/action-zip@0852c26906e00f8a315c704958823928d8018b28
36 | with:
37 | args: zip -qq -r ./builds/ChromeExtension.zip ./dist
38 |
39 | # Create Firefox artifacts
40 | - name: Create Firefox artifacts
41 | run: npm run build:firefox
42 | - uses: actions/upload-artifact@v4
43 | with:
44 | name: FirefoxExtension
45 | path: dist
46 | - uses: montudor/action-zip@0852c26906e00f8a315c704958823928d8018b28
47 | with:
48 | args: zip -qq -r ./builds/FirefoxExtension.zip ./dist
49 |
50 | # Create Beta artifacts (Builds with the name changed to beta)
51 | - name: Create Chrome Beta artifacts
52 | run: npm run build:chrome -- --env stream=beta
53 | - uses: actions/upload-artifact@v4
54 | with:
55 | name: ChromeExtensionBeta
56 | path: dist
57 | - uses: montudor/action-zip@0852c26906e00f8a315c704958823928d8018b28
58 | with:
59 | args: zip -qq -r ./builds/ChromeExtensionBeta.zip ./dist
60 |
61 | - name: Create Firefox Beta artifacts
62 | run: npm run build:firefox -- --env stream=beta
63 | - uses: actions/upload-artifact@v4
64 | with:
65 | name: FirefoxExtensionBeta
66 | path: dist
67 | - uses: montudor/action-zip@0852c26906e00f8a315c704958823928d8018b28
68 | with:
69 | args: zip -qq -r ./builds/FirefoxExtensionBeta.zip ./dist
70 |
71 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Upload Release Build
2 |
3 | on:
4 | release:
5 | types: [published]
6 |
7 | jobs:
8 |
9 | build:
10 | name: Upload Release
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | # Initialization
15 | - uses: actions/checkout@v4
16 | with:
17 | submodules: recursive
18 | - uses: actions/setup-node@v4
19 | with:
20 | node-version: '18'
21 | - name: Copy configuration
22 | run: cp config.json.example config.json
23 |
24 | # Create source artifact with submodule
25 | - name: Create directory
26 | run: cd ..; mkdir ./builds
27 | - name: Zip Source code
28 | run: zip -r ../builds/SourceCodeUseThisOne.zip *
29 | - name: Upload Source to release
30 | uses: Shopify/upload-to-release@07611424e04f1475ddf550e1c0dd650b867d5467
31 | with:
32 | args: ../builds/SourceCodeUseThisOne.zip
33 | name: SourceCodeUseThisOne.zip
34 | path: ../builds/SourceCodeUseThisOne.zip
35 | repo-token: ${{ secrets.GITHUB_TOKEN }}
36 |
37 | - run: npm ci
38 |
39 | # Create Chrome artifacts
40 | - name: Create Chrome artifacts
41 | run: npm run build:chrome
42 | - run: mkdir ./builds
43 | - name: Zip Artifacts
44 | run: cd ./dist ; zip -r ../builds/ChromeExtension.zip *
45 | - name: Upload ChromeExtension to release
46 | uses: Shopify/upload-to-release@07611424e04f1475ddf550e1c0dd650b867d5467
47 | with:
48 | args: builds/ChromeExtension.zip
49 | name: ChromeExtension.zip
50 | path: ./builds/ChromeExtension.zip
51 | repo-token: ${{ secrets.GITHUB_TOKEN }}
52 |
53 | # Create Firefox artifacts
54 | - name: Create Firefox artifacts
55 | run: npm run build:firefox
56 | - name: Zip Artifacts
57 | run: cd ./dist ; zip -r ../builds/FirefoxExtension.zip *
58 | - name: Upload FirefoxExtension to release
59 | uses: Shopify/upload-to-release@07611424e04f1475ddf550e1c0dd650b867d5467
60 | with:
61 | args: builds/FirefoxExtension.zip
62 | name: FirefoxExtension.zip
63 | path: ./builds/FirefoxExtension.zip
64 | repo-token: ${{ secrets.GITHUB_TOKEN }}
65 |
66 | # Create Beta artifacts (Builds with the name changed to beta)
67 | - name: Create Chrome Beta artifacts
68 | run: npm run build:chrome -- --env stream=beta
69 | - name: Zip Artifacts
70 | run: cd ./dist ; zip -r ../builds/ChromeExtensionBeta.zip *
71 | - name: Upload ChromeExtensionBeta to release
72 | uses: Shopify/upload-to-release@07611424e04f1475ddf550e1c0dd650b867d5467
73 | with:
74 | args: builds/ChromeExtensionBeta.zip
75 | name: ChromeExtensionBeta.zip
76 | path: ./builds/ChromeExtensionBeta.zip
77 | repo-token: ${{ secrets.GITHUB_TOKEN }}
78 |
79 |
80 | # Create Safari artifacts
81 | - name: Create Safari artifacts
82 | run: npm run build:safari
83 | - name: Zip Artifacts
84 | run: cd ./dist ; zip -r ../builds/SafariExtension.zip *
85 | - name: Upload SafariExtension to release
86 | uses: Shopify/upload-to-release@07611424e04f1475ddf550e1c0dd650b867d5467
87 | with:
88 | args: builds/SafariExtension.zip
89 | name: SafariExtension.zip
90 | path: ./builds/SafariExtension.zip
91 | repo-token: ${{ secrets.GITHUB_TOKEN }}
92 |
93 | # Create Edge artifacts
94 | - name: Clear dist for Edge
95 | run: rm -rf ./dist
96 | - name: Create Edge artifacts
97 | run: npm run build:edge
98 | - name: Zip Artifacts
99 | run: cd ./dist ; zip -r ../builds/EdgeExtension.zip *
100 | - name: Upload EdgeExtension to release
101 | uses: Shopify/upload-to-release@07611424e04f1475ddf550e1c0dd650b867d5467
102 | with:
103 | args: builds/EdgeExtension.zip
104 | name: EdgeExtension.zip
105 | path: ./builds/EdgeExtension.zip
106 | repo-token: ${{ secrets.GITHUB_TOKEN }}
107 |
108 | # Firefox Beta
109 | - name: Create Firefox Beta artifacts
110 | run: npm run build:firefox -- --env stream=beta
111 | - uses: actions/upload-artifact@v4
112 | with:
113 | name: FirefoxExtensionBeta
114 | path: dist
115 | - name: Zip Artifacts
116 | run: cd ./dist ; zip -r ../builds/FirefoxExtensionBeta.zip *
117 |
118 | # Create Firefox Signed Beta version
119 | - name: Create Firefox Signed Beta artifacts
120 | run: npm run web-sign
121 | env:
122 | WEB_EXT_API_KEY: ${{ secrets.WEB_EXT_API_KEY }}
123 | WEB_EXT_API_SECRET: ${{ secrets.WEB_EXT_API_SECRET }}
124 | - name: Install rename
125 | run: sudo apt-get install rename
126 | - name: Rename signed file
127 | run: cd ./web-ext-artifacts ; rename 's/.*/FirefoxSignedInstaller.xpi/' *
128 | - uses: actions/upload-artifact@v4
129 | with:
130 | name: FirefoxExtensionSigned.xpi
131 | path: ./web-ext-artifacts/FirefoxSignedInstaller.xpi
132 |
133 | - name: Upload FirefoxSignedInstaller.xpi to release
134 | uses: Shopify/upload-to-release@07611424e04f1475ddf550e1c0dd650b867d5467
135 | with:
136 | args: web-ext-artifacts/FirefoxSignedInstaller.xpi
137 | name: FirefoxSignedInstaller.xpi
138 | path: ./web-ext-artifacts/FirefoxSignedInstaller.xpi
139 | repo-token: ${{ secrets.GITHUB_TOKEN }}
140 |
141 |
--------------------------------------------------------------------------------
/.github/workflows/take-action.yml:
--------------------------------------------------------------------------------
1 | # .github/workflows/take.yml
2 | name: Assign issue to contributor
3 | on:
4 | issue_comment:
5 |
6 | jobs:
7 | assign:
8 | name: Take an issue
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: take the issue
12 | uses: bdougie/take-action@28b86cd8d25593f037406ecbf96082db2836e928
13 | env:
14 | GITHUB_TOKEN: ${{ github.token }}
15 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | test:
7 | name: Run tests
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | # Initialization
12 | - uses: actions/checkout@v4
13 | with:
14 | submodules: recursive
15 | - uses: actions/setup-node@v4
16 | with:
17 | node-version: '18'
18 | - run: npm ci
19 |
20 | - uses: browser-actions/setup-chrome@c785b87e244131f27c9f19c1a33e2ead956ab7ce
21 | with:
22 | chrome-version: 135
23 | install-dependencies: true
24 | install-chromedriver: true
25 |
26 | - name: Copy configuration
27 | run: "cp config.json.example config.json; sed -i 's/^}/,\"freeAccess\": true}/' config.json"
28 |
29 | - name: Set up WireGuard Connection
30 | uses: niklaskeerl/easy-wireguard-action@50341d5f4b8245ff3a90e278aca67b2d283c78d0
31 | with:
32 | WG_CONFIG_FILE: ${{ secrets.WG_CONFIG_FILE }}
33 |
34 | - name: Run tests
35 | run: npm run test
36 |
37 | - name: Upload results on fail
38 | if: ${{ failure() }}
39 | uses: actions/upload-artifact@v4
40 | with:
41 | name: Test Results
42 | path: ./test-results
--------------------------------------------------------------------------------
/.github/workflows/update-oss-attribution.yml:
--------------------------------------------------------------------------------
1 | name: update oss attributions
2 | on:
3 | push:
4 | branches:
5 | - master
6 | paths:
7 | - 'package.json'
8 | - 'package-lock.json'
9 | workflow_dispatch:
10 |
11 | jobs:
12 | update-oss:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v4
16 | with:
17 | submodules: recursive
18 | - uses: actions/setup-node@v4
19 | with:
20 | node-version: '18'
21 | - name: Install and generate attribution
22 | run: |
23 | npm ci
24 | npm i -g oss-attribution-generator
25 | generate-attribution
26 | mv ./oss-attribution/attribution.txt ./public/oss-attribution/attribution.txt
27 | - name: Prettify attributions
28 | run: |
29 | cd ci && npx ts-node prettify.ts
30 |
31 | - name: Create pull request to update list
32 | uses: peter-evans/create-pull-request@v7
33 | # v4.2.3
34 | with:
35 | commit-message: Update OSS Attribution
36 | author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
37 | branch: ci/oss_attribution
38 | title: Update OSS Attribution
39 | body: Automated OSS Attribution update
40 |
--------------------------------------------------------------------------------
/.github/workflows/updateInvidous.yml:
--------------------------------------------------------------------------------
1 | name: update invidious
2 | on:
3 | workflow_dispatch:
4 | schedule:
5 | - cron: '0 0 1 * *' # check every month
6 |
7 | jobs:
8 | check-list:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v4
12 | with:
13 | submodules: recursive
14 | - name: Download instance list
15 | run: |
16 | wget https://api.invidious.io/instances.json -O ci/data.json
17 | - name: Install dependencies
18 | run: npm ci
19 | - name: "Run CI"
20 | run: npm run ci:invidious
21 |
22 | - name: Create pull request to update list
23 | uses: peter-evans/create-pull-request@v7
24 | # v4.2.3
25 | with:
26 | commit-message: Update Invidious List
27 | author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
28 | branch: ci/update_invidious_list
29 | title: Update Invidious List
30 | body: Automated Invidious list update
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | config.json
2 | ignored
3 | .idea/
4 | node_modules
5 | web-ext-artifacts
6 | .vscode/
7 | dist/
8 | tmp/
9 | .DS_Store
10 | ci/data.json
11 | test-results
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "public/_locales"]
2 | path = public/_locales
3 | url = https://github.com/ajayyy/ExtensionTranslations
4 | [submodule "maze-utils"]
5 | path = maze-utils
6 | url = https://github.com/ajayyy/maze-utils
7 |
--------------------------------------------------------------------------------
/LICENSE-APPSTORE.txt:
--------------------------------------------------------------------------------
1 | The developers are aware that the terms of service that
2 | apply to apps distributed via Apple's App Store services and similar app stores may conflict
3 | with rights granted under the DeArrow license, the GNU General
4 | Public License, version 3. The copyright holders of the DeArrow project
5 | do not wish this conflict to prevent the otherwise-compliant distribution
6 | of derived apps via the App Store and similar app stores.
7 | Therefore, we have committed not to pursue any license
8 | violation that results solely from the conflict between the GNU GPLv3
9 | and the Apple App Store terms of service or similar app stores. In
10 | other words, as long as you comply with the GPL in all other respects,
11 | including its requirements to provide users with source code and the
12 | text of the license, we will not object to your distribution of the
13 | DeArrow project through the App Store.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | DeArrow
6 |
7 |
8 | Download:
9 | Chrome/Chromium |
10 | Firefox |
11 | Safari for MacOS and iOS |
12 | Android |
13 | Buy |
14 | Website |
15 | Stats
16 |
17 |
18 | DeArrow is a browser extension for crowdsourcing better titles and thumbnails on YouTube.
19 |
20 | The goal of DeArrow is to make titles accurate and reduce sensationalism.
21 |
22 | Titles can be any arbitrary text. Thumbnails are screenshots from specific timestamps in the video. These are user submitted and voted on.
23 |
24 | By default, if there are no submissions, it will format the original title to the user-specified format, and set a screenshot from a random timestamp as the thumbnail. This can be configured in the options to disable formatting, or show the original thumbnail by default.
25 |
26 | If the original thumbnail is actually good, you can still vote for it in the submission menu, and then it will act like a submission.
27 |
28 | The extension is currently in beta, and there are some issues to work out, but it should be fully usable.
29 |
30 | 
31 | 
32 |
33 | ### How it works
34 |
35 | The browser extension first fetches data from the [backend](https://github.com/ajayyy/SponsorBlockServer) about submitted titles and thumbnails. If one is found, it replaces the branding locally.
36 |
37 | All thumbnails are just timestamps in a video, so they need to be generated. There are two options to generate them. One is to use the [thumbnail generation service](https://github.com/ajayyy/DeArrowThumbnailCache), and another is to generate it locally. It tries both and uses the fastest one. The thumbnail generation service will cache thumbnails for future requests, making it return instantly for the next user. Local thumbnail generation is done by taking a screenshot of an HTML video element using and drawing that to a canvas.
38 |
39 | If no thumbnails or titles are submitted, it switches to the configurable fallback options. Titles will be formatted according to user preference (title or sentence cases). Thumbnails, by default, are generated at a random timestamp that is not in a [SponsorBlock](https://github.com/ajayyy/SponsorBlock) segment.
40 |
41 | Lastly, it adds a "show original" button if anything was changed, allowing you to peek at the original title and thumbnail when you want.
42 |
43 | ### Related Repositories
44 |
45 | | Name | URL |
46 | | --- | --- |
47 | | Extension | https://github.com/ajayyy/DeArrow |
48 | | Shared Library With SponsorBlock | https://github.com/ajayyy/maze-utils |
49 | | Translations | https://github.com/ajayyy/ExtensionTranslations |
50 | | Safari | https://github.com/ajayyy/DeArrowSafari |
51 | | Backend | https://github.com/ajayyy/SponsorBlockServer|
52 | | Backend Kubernetes Manifests | https://github.com/ajayyy/SponsorBlockKubernetes |
53 | | Thumbnail Cache Backend | https://github.com/ajayyy/DeArrowThumbnailCache |
54 | | Thumbnail Cache Kubernetes Manifests | https://github.com/ajayyy/k8s-thumbnail-cache |
55 |
56 |
57 | ### Group Policy Options
58 |
59 | See the [Firefox Managed Storage](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/managed), [Chrome Admin Settings](https://www.chromium.org/administrators/configuring-policy-for-extensions/) and [Edge ExtensionSettings](https://learn.microsoft.com/en-us/deployedge/microsoft-edge-manage-extensions-ref-guide) pages for more info. This [uBlock Origin wiki page](https://github.com/gorhill/uBlock/wiki/Deploying-uBlock-Origin) might also help.
60 |
61 | It is possible to inject a license key using group policy/managed storage to be able to have the extension auto-activated even when you reset the settings on each install.
62 |
63 | ```json
64 | {
65 | "licenseKey": "your license key here"
66 | }
67 | ```
68 |
69 | ### Building
70 |
71 | You must have [Node.js 16](https://nodejs.org/) and npm installed.
72 |
73 | 1. Clone with submodules
74 |
75 | ```bash
76 | git clone https://github.com/ajayyy/DeArrow --recurse-submodules=yes
77 | ```
78 |
79 | Or if you already cloned it, pull submodules with
80 |
81 | ```bash
82 | git submodule update --init --recursive
83 | ```
84 |
85 | 2. Copy the file `config.json.example` to `config.json` and adjust configuration as desired.
86 |
87 | - You will need to repeat this step in the future if you get build errors related to `CompileConfig`.
88 |
89 | 3. Run `npm ci` in the repository to install dependencies.
90 |
91 | 4. Run `npm run build:dev` (for Chrome) or `npm run build:dev:firefox` (for Firefox) to generate a development version of the extension with source maps.
92 |
93 | - You can also run `npm run build` (for Chrome) or `npm run build:firefox` (for Firefox) to generate a production build.
94 |
95 | 5. The built extension is now in `dist/`. You can load this folder directly in Chrome as an [unpacked extension](https://developer.chrome.com/docs/extensions/mv3/getstarted/#manifest), or convert it to a zip file to load it as a [temporary extension](https://developer.mozilla.org/en-US/docs/Tools/about:debugging#loading_a_temporary_extension) in Firefox. You may need to edit package.json and add the parameters directly there.
96 |
97 | ### Credit
98 |
99 | Built on the base of [SponsorBlock](https://github.com/ajayyy/SponsorBlock) licensed under GPL 3.0.
100 |
101 | Logo based on Twemoji licensed under CC-BY 4.0.
102 |
--------------------------------------------------------------------------------
/ci/invidiousCI.ts:
--------------------------------------------------------------------------------
1 | /*
2 | This file is only ran by GitHub Actions in order to populate the Invidious instances list
3 |
4 | This file should not be shipped with the extension
5 | */
6 |
7 | import { writeFile, existsSync } from 'fs';
8 | import { join } from 'path';
9 |
10 | // import file from https://api.invidious.io/instances.json
11 | if (!existsSync(join(__dirname, "data.json"))) {
12 | process.exit(1);
13 | }
14 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment
15 | // @ts-ignore
16 | import * as data from "../ci/data.json";
17 |
18 | type instanceMap = {
19 | name: string;
20 | url: string;
21 | dailyRatios: {ratio: string; label: string }[];
22 | thirtyDayUptime: string;
23 | }[]
24 |
25 | // only https servers
26 | const mapped: instanceMap = data
27 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
28 | .filter((i: any) => i[1]?.type === 'https')
29 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
30 | .map((instance: any) => {
31 | return {
32 | name: instance[0],
33 | url: instance[1].uri,
34 | dailyRatios: instance[1].monitor.dailyRatios,
35 | thirtyDayUptime: instance[1]?.monitor['30dRatio'].ratio,
36 | }
37 | })
38 |
39 | // reliability and sanity checks
40 | const reliableCheck = mapped
41 | .filter((instance) => {
42 | // 30d uptime >= 90%
43 | const thirtyDayUptime = Number(instance.thirtyDayUptime) >= 90
44 | // available for at least 80/90 days
45 | const dailyRatioCheck = instance.dailyRatios.filter(status => status.label !== "black")
46 | return (thirtyDayUptime && dailyRatioCheck.length >= 80)
47 | })
48 | // url includes name
49 | .filter(instance => instance.url.includes(instance.name))
50 |
51 | // finally map to array
52 | const result: string[] = reliableCheck.map(instance => instance.name).sort()
53 | writeFile(join(__dirname, "./invidiouslist.json"), JSON.stringify(result), (err) => {
54 | if (err) return console.log(err);
55 | })
--------------------------------------------------------------------------------
/ci/invidiouslist.json:
--------------------------------------------------------------------------------
1 | ["inv.bp.projectsegfau.lt","inv.odyssey346.dev","inv.riverside.rocks","inv.vern.cc","invidious.baczek.me","invidious.epicsite.xyz","invidious.esmailelbob.xyz","invidious.flokinet.to","invidious.lidarshield.cloud","invidious.nerdvpn.de","invidious.privacydev.net","invidious.snopyta.org","invidious.tiekoetter.com","invidious.weblibre.org","iv.melmac.space","vid.puffyan.us","watch.thekitty.zone","y.com.sb","yewtu.be","yt.artemislena.eu","yt.funami.tech","yt.oelrichsgarcia.de"]
--------------------------------------------------------------------------------
/ci/prettify.ts:
--------------------------------------------------------------------------------
1 | import { writeFile } from 'fs';
2 |
3 | import * as license from "../oss-attribution/licenseInfos.json";
4 |
5 | const result = JSON.stringify(license, null, 2);
6 | writeFile("../oss-attribution/licenseInfos.json", result, err => { if (err) return console.log(err) } );
--------------------------------------------------------------------------------
/config.json.example:
--------------------------------------------------------------------------------
1 | {
2 | "serverAddress": "https://sponsor.ajay.app",
3 | "thumbnailServerAddress": "https://dearrow-thumb.ajay.app",
4 | "testingServerAddress": "https://sponsor.ajay.app/test",
5 | "debug": false,
6 | "extensionImportList": {
7 | "chromium": [
8 | "mnjggcdmjocbbbhaepdhchncahnbgone",
9 | "mbmgnelfcpoecdepckhlhegpcehmpmji"
10 | ],
11 | "firefox": [
12 | "sponsorBlocker@ajay.app",
13 | "sponsorBlockerBETA@ajay.app"
14 | ],
15 | "safari": [
16 | "app.ajay.sponsor.macos.extension"
17 | ]
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "roots": [
3 | "test"
4 | ],
5 | "transform": {
6 | "^.+\\.ts$": "ts-jest"
7 | },
8 | "reporters": ["default", "github-actions"],
9 | "globals": {
10 | "LOAD_CLD": false
11 | }
12 | };
13 |
--------------------------------------------------------------------------------
/manifest/beta-manifest-extra.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "BETA - DeArrow"
3 | }
4 |
--------------------------------------------------------------------------------
/manifest/chrome-manifest-extra.json:
--------------------------------------------------------------------------------
1 | {
2 | "permissions": [
3 | "scripting"
4 | ],
5 | "content_scripts": [
6 | {
7 | "world": "MAIN",
8 | "js": [
9 | "./js/document.js"
10 | ],
11 | "matches": [
12 | "https://*.youtube.com/*",
13 | "https://www.youtube-nocookie.com/embed/*"
14 | ],
15 | "all_frames": true,
16 | "run_at": "document_start"
17 | }
18 | ],
19 | "web_accessible_resources": [{
20 | "resources": [
21 | "icons/refresh.svg",
22 | "icons/logo.svg",
23 | "js/document.js",
24 | "js/options.js",
25 | "js/popup.js",
26 | "popup.css",
27 | "shared.css",
28 | "help.html",
29 | "help.css",
30 | "icons/logo-16.png",
31 | "icons/logo-32.png",
32 | "icons/logo-64.png",
33 | "icons/logo-128.png",
34 | "icons/logo-256.png",
35 | "icons/logo-2r.svg",
36 | "icons/logo-casual.svg",
37 | "icons/close.png",
38 | "icons/add.svg",
39 | "icons/remove.svg"
40 | ],
41 | "matches": [""]
42 | }],
43 | "host_permissions": [
44 | "https://sponsor.ajay.app/*",
45 | "https://dearrow-thumb.ajay.app/*",
46 | "https://dearrow.ajay.app/*",
47 | "https://*.youtube.com/*",
48 | "https://www.youtube-nocookie.com/embed/*"
49 | ],
50 | "action": {
51 | "default_title": "DeArrow",
52 | "default_popup": "popup.html",
53 | "default_icon": {
54 | "16": "icons/logo-16.png",
55 | "32": "icons/logo-32.png",
56 | "64": "icons/logo-64.png",
57 | "128": "icons/logo-128.png"
58 | },
59 | "theme_icons": [
60 | {
61 | "light": "icons/logo-16.png",
62 | "dark": "icons/logo-16.png",
63 | "size": 16
64 | },
65 | {
66 | "light": "icons/logo-32.png",
67 | "dark": "icons/logo-32.png",
68 | "size": 32
69 | },
70 | {
71 | "light": "icons/logo-64.png",
72 | "dark": "icons/logo-64.png",
73 | "size": 64
74 | },
75 | {
76 | "light": "icons/logo-128.png",
77 | "dark": "icons/logo-128.png",
78 | "size": 128
79 | },
80 | {
81 | "light": "icons/logo-256.png",
82 | "dark": "icons/logo-256.png",
83 | "size": 256
84 | },
85 | {
86 | "light": "icons/logo-512.png",
87 | "dark": "icons/logo-512.png",
88 | "size": 512
89 | },
90 | {
91 | "light": "icons/logo-1024.png",
92 | "dark": "icons/logo-1024.png",
93 | "size": 1024
94 | }
95 | ]
96 | },
97 | "background": {
98 | "service_worker": "./js/background.js"
99 | },
100 | "manifest_version": 3
101 | }
--------------------------------------------------------------------------------
/manifest/firefox-beta-manifest-extra.json:
--------------------------------------------------------------------------------
1 | {
2 | "browser_specific_settings": {
3 | "gecko": {
4 | "id": "deArrowBETA@ajay.app"
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/manifest/firefox-manifest-extra.json:
--------------------------------------------------------------------------------
1 | {
2 | "browser_specific_settings": {
3 | "gecko": {
4 | "id": "deArrow@ajay.app",
5 | "strict_min_version": "102.0"
6 | },
7 | "gecko_android": {
8 | "strict_min_version": "113.0"
9 | }
10 | },
11 | "permissions": [
12 | "scripting"
13 | ],
14 | "content_scripts": [{
15 | "run_at": "document_start",
16 | "matches": [
17 | "https://*.youtube.com/*",
18 | "https://www.youtube-nocookie.com/embed/*"
19 | ],
20 | "all_frames": true,
21 | "js": [
22 | "./js/documentScriptInjector.js"
23 | ],
24 | "css": [
25 | "content.css",
26 | "shared.css"
27 | ]
28 | }],
29 | "background": {
30 | "persistent": false
31 | },
32 | "browser_action": {
33 | "default_area": "navbar"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/manifest/manifest-v2-extra.json:
--------------------------------------------------------------------------------
1 | {
2 | "web_accessible_resources": [
3 | "icons/refresh.svg",
4 | "icons/logo.svg",
5 | "js/document.js",
6 | "js/options.js",
7 | "js/popup.js",
8 | "popup.css",
9 | "shared.css",
10 | "help.html",
11 | "help.css",
12 | "icons/logo-16.png",
13 | "icons/logo-32.png",
14 | "icons/logo-64.png",
15 | "icons/logo-128.png",
16 | "icons/logo-256.png",
17 | "icons/logo-2r.svg",
18 | "icons/logo-casual.svg",
19 | "icons/close.png",
20 | "icons/add.svg",
21 | "icons/remove.svg"
22 | ],
23 | "permissions": [
24 | "https://sponsor.ajay.app/*",
25 | "https://dearrow-thumb.ajay.app/*",
26 | "https://*.googlevideo.com/*",
27 | "https://*.youtube.com/*",
28 | "https://www.youtube-nocookie.com/embed/*"
29 | ],
30 | "optional_permissions": [
31 | "*://*/*"
32 | ],
33 | "browser_action": {
34 | "default_title": "DeArrow",
35 | "default_popup": "popup.html",
36 | "default_icon": {
37 | "16": "icons/logo-16.png",
38 | "32": "icons/logo-32.png",
39 | "64": "icons/logo-64.png",
40 | "128": "icons/logo-128.png"
41 | },
42 | "theme_icons": [
43 | {
44 | "light": "icons/logo-16.png",
45 | "dark": "icons/logo-16.png",
46 | "size": 16
47 | },
48 | {
49 | "light": "icons/logo-32.png",
50 | "dark": "icons/logo-32.png",
51 | "size": 32
52 | },
53 | {
54 | "light": "icons/logo-64.png",
55 | "dark": "icons/logo-64.png",
56 | "size": 64
57 | },
58 | {
59 | "light": "icons/logo-128.png",
60 | "dark": "icons/logo-128.png",
61 | "size": 128
62 | },
63 | {
64 | "light": "icons/logo-256.png",
65 | "dark": "icons/logo-256.png",
66 | "size": 256
67 | },
68 | {
69 | "light": "icons/logo-512.png",
70 | "dark": "icons/logo-512.png",
71 | "size": 512
72 | },
73 | {
74 | "light": "icons/logo-1024.png",
75 | "dark": "icons/logo-1024.png",
76 | "size": 1024
77 | }
78 | ]
79 | },
80 | "background": {
81 | "scripts":[
82 | "./js/background.js"
83 | ]
84 | },
85 | "manifest_version": 2
86 | }
--------------------------------------------------------------------------------
/manifest/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "__MSG_deArrowFullName__",
3 | "short_name": "DeArrow",
4 | "version": "2.1",
5 | "default_locale": "en",
6 | "description": "__MSG_deArrowDescription__",
7 | "homepage_url": "https://dearrow.ajay.app",
8 | "permissions": [
9 | "storage",
10 | "unlimitedStorage",
11 | "alarms"
12 | ],
13 | "icons": {
14 | "16": "icons/logo-16.png",
15 | "32": "icons/logo-32.png",
16 | "64": "icons/logo-64.png",
17 | "128": "icons/logo-128.png",
18 | "256": "icons/logo-256.png",
19 | "512": "icons/logo-512.png",
20 | "1024": "icons/logo-1024.png"
21 | },
22 | "options_ui": {
23 | "page": "options/options.html",
24 | "open_in_tab": true
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/manifest/safari-manifest-extra.json:
--------------------------------------------------------------------------------
1 | {
2 | "background": {
3 | "persistent": false
4 | },
5 | "content_scripts": [{
6 | "run_at": "document_start",
7 | "matches": [
8 | "https://*.youtube.com/*",
9 | "https://www.youtube-nocookie.com/embed/*"
10 | ],
11 | "all_frames": true,
12 | "js": [
13 | "./js/content.js"
14 | ],
15 | "css": [
16 | "content.css",
17 | "shared.css"
18 | ]
19 | }]
20 | }
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dearrow",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "background.js",
6 | "dependencies": {
7 | "cld3-asm": "^3.1.1",
8 | "react": "^18.2.0",
9 | "react-dom": "^18.2.0",
10 | "seedrandom": "^3.0.5"
11 | },
12 | "devDependencies": {
13 | "@types/chrome": "^0.0.243",
14 | "@types/firefox-webext-browser": "^111.0.1",
15 | "@types/jest": "^29.1.2",
16 | "@types/react": "^18.0.21",
17 | "@types/react-dom": "^18.0.6",
18 | "@types/seedrandom": "^3.0.5",
19 | "@types/selenium-webdriver": "^4.1.5",
20 | "@types/wicg-mediasession": "^1.1.4",
21 | "@typescript-eslint/eslint-plugin": "^5.39.0",
22 | "@typescript-eslint/parser": "^5.39.0",
23 | "chromedriver": "^135.0.1",
24 | "concurrently": "^7.4.0",
25 | "copy-webpack-plugin": "^11.0.0",
26 | "eslint": "^8.24.0",
27 | "eslint-plugin-react": "^7.31.8",
28 | "fork-ts-checker-webpack-plugin": "^7.2.13",
29 | "jest": "^29.1.2",
30 | "jest-environment-jsdom": "^29.1.2",
31 | "rimraf": "^3.0.2",
32 | "schema-utils": "^4.0.0",
33 | "selenium-webdriver": "^4.5.0",
34 | "speed-measure-webpack-plugin": "^1.5.0",
35 | "ts-jest": "^29.0.3",
36 | "ts-loader": "^9.4.1",
37 | "ts-node": "^10.9.1",
38 | "typescript": "4.8",
39 | "web-ext": "^8.2.0",
40 | "webpack": "^5.94.0",
41 | "webpack-cli": "^4.10.0",
42 | "webpack-merge": "^5.8.0"
43 | },
44 | "scripts": {
45 | "web-run": "npm run web-run:chrome",
46 | "web-sign": "web-ext sign --channel unlisted -s dist",
47 | "web-run:firefox": "cd dist && web-ext run --start-url https://addons.mozilla.org/firefox/addon/ublock-origin/",
48 | "web-run:firefox-android": "cd dist && web-ext run -t firefox-android --firefox-apk org.mozilla.fenix",
49 | "web-run:chrome": "cd dist && web-ext run --start-url https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm -t chromium",
50 | "build": "npm run build:chrome",
51 | "build:chrome": "webpack --env browser=chrome --config webpack/webpack.prod.js",
52 | "build:firefox": "webpack --env browser=firefox --config webpack/webpack.prod.js",
53 | "build:safari": "webpack --env browser=safari --config webpack/webpack.prod.js",
54 | "build:edge": "webpack --env browser=edge --config webpack/webpack.prod.js",
55 | "build:dev": "npm run build:dev:chrome",
56 | "build:dev:chrome": "webpack --env browser=chrome --config webpack/webpack.dev.js",
57 | "build:dev:firefox": "webpack --env browser=firefox --config webpack/webpack.dev.js",
58 | "build:watch": "npm run build:watch:chrome",
59 | "build:watch:chrome": "webpack --env browser=chrome --config webpack/webpack.dev.js --watch",
60 | "build:watch:firefox": "webpack --env browser=firefox --config webpack/webpack.dev.js --watch",
61 | "ci:invidious": "ts-node ci/invidiousCI.ts",
62 | "dev": "npm run build:dev && concurrently \"npm run web-run\" \"npm run build:watch\"",
63 | "dev:firefox": "npm run build:dev:firefox && concurrently \"npm run web-run:firefox\" \"npm run build:watch:firefox\"",
64 | "dev:firefox-android": "npm run build:dev:firefox && concurrently \"npm run web-run:firefox-android\" \"npm run build:watch:firefox\"",
65 | "clean": "rimraf dist",
66 | "test": "npm run build:chrome && npm run test:no-build",
67 | "test-without-building": "npm run test:no-build",
68 | "test:no-build": "npx jest --passWithNoTests",
69 | "lint": "eslint src",
70 | "lint:fix": "eslint src --fix"
71 | },
72 | "engines": {
73 | "node": ">=16"
74 | },
75 | "funding": [
76 | {
77 | "type": "individual",
78 | "url": "https://sponsor.ajay.app/donate"
79 | },
80 | {
81 | "type": "github",
82 | "url": "https://github.com/sponsors/ajayyy-org"
83 | },
84 | {
85 | "type": "patreon",
86 | "url": "https://www.patreon.com/ajayyy"
87 | },
88 | {
89 | "type": "individual",
90 | "url": "https://paypal.me/ajayyy"
91 | }
92 | ],
93 | "repository": {
94 | "type": "git",
95 | "url": "git+https://github.com/ajayyy/DeArrow.git"
96 | },
97 | "author": "Ajay Ramachandran",
98 | "license": "GPL-3.0",
99 | "private": true
100 | }
101 |
--------------------------------------------------------------------------------
/public/casual.css:
--------------------------------------------------------------------------------
1 | .embed [data-type="react-CasualChoiceComponent"] {
2 | display: none;
3 | }
4 |
5 | .casualChoiceContainer {
6 | display: flex;
7 | flex-direction: column;
8 | align-items: center;
9 | padding: 20px;
10 | font-size: 14px;
11 | --container-gap: 20px;
12 | gap: var(--container-gap);
13 | color: #dfdfdf;
14 |
15 | height: auto;
16 |
17 | border-radius: 10px;
18 | border-width: 10px;
19 | border: solid;
20 |
21 | width: 300px;
22 | cursor: pointer;
23 |
24 | transition: border-color ease-in-out 0.2s, background-color ease-in-out 0.2s;
25 | }
26 |
27 | .casualChoiceContainer.casualMode {
28 | border-color: rgba(121, 215, 152, 0.5);
29 | }
30 | .casualChoiceContainer.casualMode.selected {
31 | border-color: rgba(121, 215, 152, 1);
32 | background-color: rgba(0, 118, 31, 0.2);
33 | }
34 |
35 | .casualChoiceContainer.classicMode {
36 | border-color: rgba(136, 201, 249, 0.5);
37 | }
38 | .casualChoiceContainer.classicMode.selected {
39 | border-color: rgba(136, 201, 249, 1);
40 | background-color: rgba(18, 19, 189, 0.2);
41 | }
42 |
43 | .casualChoiceTitle {
44 | font-size: 24px;
45 | font-weight: bold;
46 | }
47 |
48 | .casualChoiceLogo {
49 | padding-left: 35%;
50 | padding-right: 35%;
51 |
52 | box-sizing: border-box;
53 | }
54 |
55 | .casualChoiceDescription {
56 | font-size: 14px;
57 | }
58 |
59 | .casualChoiceDescription div {
60 | margin-top: 10px;
61 | margin-bottom: 10px;
62 | line-height: 1.3;
63 | }
64 | .casualChoiceDescription div:first-child {
65 | margin-top: 0px;
66 | }
67 | .casualChoiceDescription div:last-child {
68 | margin-bottom: 0px;
69 | }
70 |
71 | .casualChoicesContainer {
72 | display: flex;
73 | justify-content: center;
74 | gap: 10%;
75 | max-width: 800px;
76 | }
77 |
78 | .casualChoiceCategories {
79 | display: flex;
80 | flex-direction: column;
81 | justify-content: center;
82 | align-items: center;
83 | --container-gap: 20px;
84 | gap: var(--container-gap);
85 | width: 100%;
86 | }
87 |
88 | .casualChoiceContainer:not(.selected) .casualChoiceCategories {
89 | display: none;
90 | }
91 |
92 | .casualCategoryPill {
93 | display: flex;
94 | flex-direction: row;
95 | justify-content: space-between;
96 | align-items: center;
97 |
98 | cursor: default;
99 | width: 80%;
100 | border-radius: 25px;
101 | background-color: rgba(121, 215, 152, 0.5);
102 |
103 | position: relative;
104 | }
105 |
106 | .casualCategoryPillContent {
107 | display: flex;
108 | flex-direction: column;
109 | justify-content: center;
110 | align-items: center;
111 | gap: 5px;
112 |
113 | width: 100%;
114 |
115 | padding: 5px;
116 | font-size: 16px;
117 | }
118 |
119 | .addButton {
120 | width: 25px;
121 | height: 25px;
122 | font-size: 20px;
123 | background-color: rgba(121, 215, 152, 0.5);
124 | cursor: pointer;
125 | }
126 |
127 | .minimumVotes {
128 | display: flex;
129 | flex-direction: row;
130 | justify-content: center;
131 | align-items: center;
132 | gap: 5px;
133 | }
134 |
135 | .minimumVotesText {
136 | font-size: 11px;
137 | opacity: 0.8;
138 | }
139 |
140 | .minimumVotesButton {
141 | border: solid 1px;
142 | cursor: pointer;
143 |
144 | --font-size: 14px;
145 |
146 | font-size: var(--font-size);
147 | width: var(--font-size);
148 | height: var(--font-size);
149 |
150 | display: flex;
151 | justify-content: center;
152 | align-items: center;
153 | line-height: 1;
154 |
155 | border-radius: 100%;
156 | }
157 |
158 | .casualCategoryPill .closeButton {
159 | cursor: pointer;
160 | position: absolute;
161 | right: 5px;
162 |
163 | padding: 5px;
164 | }
165 |
166 | .casualCategorySelectionAnchor {
167 | position: relative;
168 | width: 100%;
169 |
170 | /* Undo gap */
171 | margin-top: calc(var(--container-gap) * -1);
172 | }
173 |
174 | .casualCategorySelectionParent {
175 | position: absolute;
176 | background-color: rgba(28, 28, 28, 0.9);
177 | padding: 5px;
178 | border-radius: 25px;
179 |
180 | top: 5px;
181 | left: 0;
182 | right: 0;
183 | width: fit-content;
184 | margin: auto;
185 | }
186 |
187 | .casualCategorySelection {
188 | font-size: 14px;
189 | padding: 5px;
190 | margin: 10px;
191 |
192 | border-radius: 25px;
193 | text-align: center;
194 | }
195 |
196 | [data-theme="light"] .casualCategorySelection {
197 | color: white;
198 | }
199 |
200 | .casualCategorySelection:hover {
201 | background-color: rgba(235, 235, 235, 0.2);
202 | }
203 |
204 | .casualChoicesContainer input:checked + .sb-slider {
205 | background-color: rgba(121, 215, 152, 0.5);
206 | }
207 |
208 | .casualChoicesContainer .sb-switch-container {
209 | width: 100%;
210 | display: flex;
211 |
212 | --sb-slider-color: #495c49;
213 | }
214 |
215 | .casualChoicesContainer .sb-switch-container .sb-switch-label {
216 | text-align: center;
217 | }
--------------------------------------------------------------------------------
/public/help.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --color-scheme: dark;
3 | --background: #333333;
4 | --header-color: #212121;
5 | --dialog-background: #181818;
6 | --dialog-border: white;
7 | --text: #c4c4c4;
8 | --title: #dad8d8;
9 | --disabled: #080052;
10 | --black: black;
11 | --white: white;
12 | }
13 |
14 | [data-theme="light"] {
15 | --color-scheme: light;
16 | --background: #f9f9f9;
17 | --header-color: white;
18 | --dialog-background: #f9f9f9;
19 | --dialog-border: #282828;
20 | --text: #262626;
21 | --title: #707070;
22 | --disabled: #ffcaca;
23 | --black: white;
24 | --white: black;
25 | }
26 |
27 | html {
28 | color-scheme: var(--color-scheme);
29 | }
30 |
31 | .bigText {
32 | font-size: 50px;
33 | }
34 |
35 | .bigText > * {
36 | font-size: 50px;
37 | }
38 |
39 | body {
40 | background-color: var(--background);
41 | font-family: sans-serif;
42 | }
43 |
44 | .center {
45 | text-align: center;
46 | }
47 |
48 | .inline {
49 | display: inline-block;
50 | }
51 |
52 | .container {
53 | max-width: 900px;
54 | margin: auto;
55 | }
56 |
57 | .projectPreview {
58 | position: relative;
59 | }
60 |
61 | .projectPreviewImage {
62 | position: absolute;
63 | left: -90px;
64 | width: 80px;
65 | top: 50%;
66 | transform: translateY(-50%);
67 | }
68 |
69 | .projectPreviewImageLarge {
70 | position: absolute;
71 | left: -210px;
72 | width: 200px;
73 | top: 50%;
74 | transform: translateY(-20%);
75 | }
76 |
77 | .createdBy {
78 | font-size: 14px;
79 | text-align: center;
80 | padding-top: 0px;
81 | padding-bottom: 0px;
82 | }
83 |
84 | #title {
85 | background-color: #636363;
86 |
87 | text-align: center;
88 | vertical-align: middle;
89 |
90 | font-size: 50px;
91 | color: var(--header-color);
92 |
93 | padding: 20px;
94 |
95 | text-decoration: none;
96 |
97 | border-radius: 15px;
98 |
99 | transition: font-size 1s;
100 | }
101 |
102 | #titleText {
103 | padding-left: 5px;
104 | }
105 |
106 | .subtitle {
107 | font-size: 40px;
108 | color: #dad8d8;
109 |
110 | padding-top: 10px;
111 |
112 | transition: font-size 0.4s;
113 | }
114 |
115 | .subtitle:hover {
116 | font-size: 45px;
117 |
118 | transition: font-size 0.4s;
119 | }
120 |
121 | .profilepic {
122 | background-color: #636363 !important;
123 | vertical-align: middle;
124 | }
125 |
126 | .profilepiccircle {
127 | vertical-align: middle;
128 | overflow: hidden;
129 | border-radius: 50%;
130 | }
131 |
132 | a {
133 | text-decoration: underline;
134 | color: inherit;
135 | }
136 |
137 | .link {
138 | padding: 20px;
139 |
140 | height: 80px;
141 |
142 | transition: height 0.2s;
143 | }
144 |
145 | .link:hover {
146 | height: 95px;
147 |
148 | transition: height 0.2s;
149 | }
150 |
151 | #contact,
152 | .smalllink {
153 | font-size: 25px;
154 | color: #e8e8e8;
155 |
156 | text-align: center;
157 |
158 | padding: 10px;
159 | }
160 |
161 | #contact {
162 | text-decoration: none;
163 | }
164 |
165 | p,
166 | li {
167 | font-size: 16px;
168 | }
169 |
170 | p,
171 | li,
172 | a,
173 | span,
174 | div:not(.casualChoicesContainer *) {
175 | color: var(--text);
176 | }
177 |
178 | p,
179 | li,
180 | code,
181 | a {
182 | text-align: left;
183 | overflow-wrap: break-word;
184 | }
185 |
186 | .optionsFrame {
187 | width: 100%;
188 | height: 500px;
189 | }
190 |
191 | .previewImage {
192 | max-height: 200px;
193 | }
194 |
195 | img {
196 | max-width: 100%;
197 |
198 | text-align: center;
199 | }
200 |
201 | #recentPostTitle {
202 | font-size: 30px;
203 | color: #dad8d8;
204 | }
205 |
206 | #recentPostDate {
207 | font-size: 15px;
208 | color: #dad8d8;
209 | }
210 |
211 | h1,
212 | h2,
213 | h3,
214 | h4,
215 | h5,
216 | h6 {
217 | color: var(--title);
218 | text-align: center;
219 | }
220 |
221 | svg {
222 | text-decoration: none;
223 | }
224 |
225 | #sbDonate {
226 | font-size: 10px;
227 | }
228 |
229 | @media screen and (orientation:portrait) {
230 | .projectPreviewImage {
231 | position: unset;
232 | width: 50%;
233 | display: block;
234 | margin: auto;
235 | transform: none;
236 | }
237 |
238 | .projectPreviewImageLarge {
239 | position: unset;
240 | left: 0;
241 | width: 50%;
242 | display: block;
243 | margin: auto;
244 | transform: unset;
245 | }
246 |
247 | .container {
248 | max-width: 100%;
249 | margin: 5px;
250 | text-align: center;
251 | }
252 |
253 | p,
254 | li,
255 | code,
256 | a {
257 | text-align: center;
258 | }
259 | }
260 |
261 | /* keybind dialog */
262 | .key {
263 | border-width: 1px;
264 | border-style: solid;
265 | border-radius: 5px;
266 | display: inline-block;
267 | min-width: 33px;
268 | text-align: center;
269 | font-weight: bold;
270 | border-color: var(--white);
271 | box-sizing: border-box;
272 | }
273 |
274 | .unbound,
275 | .key {
276 | padding: 8px;
277 | }
278 |
279 | #keybind-dialog .dialog {
280 | position: fixed;
281 | border-width: 3px;
282 | border-style: solid;
283 | border-radius: 15px;
284 | max-height: 100vh;
285 | width: 400px;
286 | overflow-x: auto;
287 | z-index: 100;
288 | padding: 15px;
289 | left: 50%;
290 | top: 50%;
291 | transform: translate(-50%, -50%);
292 | font-size: 14px;
293 | background-color: var(--dialog-background);
294 | border-color: var(--dialog-border);
295 | }
296 |
297 | #change-keybind-buttons {
298 | float: right;
299 | }
300 |
301 | #change-keybind-buttons>.option-button {
302 | margin: 0 2px;
303 | }
304 |
305 | #change-keybind-settings {
306 | margin: 15px 15px 30px;
307 | }
308 |
309 | #change-keybind-settings .key {
310 | vertical-align: top;
311 | margin: 15px 0 0 40px;
312 | height: 34px;
313 | }
314 |
315 | #change-keybind-error {
316 | margin-bottom: 15px;
317 | color: red;
318 | font-weight: bold;
319 | }
320 |
321 | .blocker {
322 | position: fixed;
323 | left: 0;
324 | right: 0;
325 | top: 0;
326 | bottom: 0;
327 | z-index: 90;
328 | background-color: #00000080;
329 | }
330 |
331 | .option-button {
332 | cursor: pointer;
333 |
334 | background-color: #0e79ca;
335 | padding: 10px;
336 | color: white;
337 | border-radius: 5px;
338 | font-size: 14px;
339 |
340 | width: max-content;
341 | }
342 |
343 | .option-button:hover:not(.disabled) {
344 | background-color: #0e79ca;
345 | }
346 |
347 | .option-button.disabled {
348 | cursor: default;
349 | background-color: var(--disabled);
350 | color: grey;
351 | }
352 |
353 | .selfpromo-text {
354 | color: #8a8a8a;
355 | cursor: pointer;
356 | }
357 |
358 | .hidden {
359 | display: none;
360 | }
361 |
362 | .payment-announcement-container {
363 | padding: 15px;
364 | border-radius: 20px;
365 | background-color: #171717;
366 | }
367 |
368 | .payment-announcement-container * {
369 | color: white;
370 | }
371 |
372 | .payment-announcement > * {
373 | font-size: 25px;
374 | margin-top: 0;
375 | text-align: center;
376 | }
377 |
378 | .center-button {
379 | margin-left: auto;
380 | margin-right: auto;
381 | }
382 |
383 | .option-text-box {
384 | width: 300px;
385 | font-size: 13px;
386 |
387 | margin: 10px
388 | }
389 |
390 | .option-link {
391 | text-decoration: none;
392 | }
393 |
394 | .option-link.side-by-side {
395 | margin: 50px;
396 | }
397 |
398 | .redeem-box {
399 | margin-bottom: 10px;
400 | }
401 |
402 | .casualChoicesContainer {
403 | margin-top: 10px;
404 | margin-bottom: 10px;
405 | }
--------------------------------------------------------------------------------
/public/help.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/public/icons/add.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/icons/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajayyy/DeArrow/f755fa2c2d0975688a5853afb11d08ea0cf98737/public/icons/close.png
--------------------------------------------------------------------------------
/public/icons/heart.svg:
--------------------------------------------------------------------------------
1 |
2 |
44 |
--------------------------------------------------------------------------------
/public/icons/help.svg:
--------------------------------------------------------------------------------
1 |
2 |
59 |
--------------------------------------------------------------------------------
/public/icons/logo-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajayyy/DeArrow/f755fa2c2d0975688a5853afb11d08ea0cf98737/public/icons/logo-1024.png
--------------------------------------------------------------------------------
/public/icons/logo-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajayyy/DeArrow/f755fa2c2d0975688a5853afb11d08ea0cf98737/public/icons/logo-128.png
--------------------------------------------------------------------------------
/public/icons/logo-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajayyy/DeArrow/f755fa2c2d0975688a5853afb11d08ea0cf98737/public/icons/logo-16.png
--------------------------------------------------------------------------------
/public/icons/logo-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajayyy/DeArrow/f755fa2c2d0975688a5853afb11d08ea0cf98737/public/icons/logo-256.png
--------------------------------------------------------------------------------
/public/icons/logo-2r.svg:
--------------------------------------------------------------------------------
1 |
2 |
31 |
--------------------------------------------------------------------------------
/public/icons/logo-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajayyy/DeArrow/f755fa2c2d0975688a5853afb11d08ea0cf98737/public/icons/logo-32.png
--------------------------------------------------------------------------------
/public/icons/logo-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajayyy/DeArrow/f755fa2c2d0975688a5853afb11d08ea0cf98737/public/icons/logo-512.png
--------------------------------------------------------------------------------
/public/icons/logo-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajayyy/DeArrow/f755fa2c2d0975688a5853afb11d08ea0cf98737/public/icons/logo-64.png
--------------------------------------------------------------------------------
/public/icons/logo-casual.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/icons/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
55 |
--------------------------------------------------------------------------------
/public/icons/newprofilepic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajayyy/DeArrow/f755fa2c2d0975688a5853afb11d08ea0cf98737/public/icons/newprofilepic.jpg
--------------------------------------------------------------------------------
/public/icons/not_visible.svg:
--------------------------------------------------------------------------------
1 |
2 |
40 |
--------------------------------------------------------------------------------
/public/icons/pause.svg:
--------------------------------------------------------------------------------
1 |
2 |
60 |
--------------------------------------------------------------------------------
/public/icons/refresh.svg:
--------------------------------------------------------------------------------
1 |
2 |
45 |
--------------------------------------------------------------------------------
/public/icons/remove.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/icons/settings.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/icons/thumbs_down_locked.svg:
--------------------------------------------------------------------------------
1 |
2 |
59 |
--------------------------------------------------------------------------------
/public/icons/visible.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/oss-attribution/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajayyy/DeArrow/f755fa2c2d0975688a5853afb11d08ea0cf98737/public/oss-attribution/.gitkeep
--------------------------------------------------------------------------------
/public/payment.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Payment
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/public/popup.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --sb-main-font-family: "Source Sans Pro", sans-serif;
3 | --sb-main-bg-color: #222;
4 | --sb-main-fg-color: #fff;
5 | --sb-grey-bg-color: #333;
6 | --sb-grey-fg-color: #999;
7 | --sb-red-bg-color: #cc1717;
8 | --cb-switch-color: #0e79ca;
9 |
10 | /* Options */
11 | --color-scheme: dark;
12 | --background: #333333;
13 | --menu-background: #181818;
14 | --menu-foreground: white;
15 | --dialog-background: #181818;
16 | --dialog-border: white;
17 | --tab-color: #242424;
18 | --tab-button-hover: #07375d;
19 | --tab-hover: white;
20 | --description: #dfdfdf;
21 | --disabled: #080052;
22 | --title: #dad8d8;
23 | --border-color: #484848;
24 | --black: black;
25 | --white: white;
26 | }
27 |
28 | /*
29 | * Main containers
30 | */
31 | #sponsorBlockPopupHTML {
32 | color-scheme: dark;
33 | max-height: 600px;
34 | overflow-y: auto;
35 | }
36 |
37 | #sponsorBlockPopupBody {
38 | margin: 0;
39 | width: 374px;
40 | max-width: 100%;
41 | /* NOTE: Ensures content doesn't exceed restricted popup widths in Firefox */
42 | font-size: 14px;
43 | font-family: var(--sb-main-font-family);
44 | background-color: var(--sb-main-bg-color);
45 | color: var(--sb-main-fg-color);
46 | color-scheme: dark;
47 | text-align: center;
48 | }
49 |
50 | #sponsorBlockPopupBody a,
51 | #sponsorBlockPopupBody button {
52 | cursor: pointer;
53 | }
54 |
55 | /*
56 | * Header logo
57 | */
58 | .sbPopupLogo {
59 | display: flex;
60 | align-items: center;
61 | justify-content: center;
62 | font-weight: bold;
63 | user-select: none;
64 | padding: 10px 0px 0px;
65 | font-size: 32px;
66 | }
67 |
68 | .sbPopupLogo img {
69 | margin: 8px;
70 | }
71 |
72 | /*
73 | * Main controls menu
74 | */
75 | .sbControlsMenu {
76 | margin: 16px;
77 | margin-top: 6px;
78 | border-radius: 8px;
79 | background-color: var(--sb-grey-bg-color);
80 | justify-content: space-evenly;
81 | overflow: hidden;
82 | display: flex;
83 | }
84 |
85 | .sbControlsMenu-item {
86 | display: flex;
87 | align-items: center;
88 | flex-direction: column;
89 | justify-content: center;
90 | background: transparent;
91 | user-select: none;
92 | cursor: pointer;
93 | border: none;
94 | flex: 1;
95 | padding: 10px 15px;
96 | transition: background-color 0.2s ease-in-out;
97 | }
98 |
99 | .sbControlsMenu-item:hover {
100 | background-color: #444;
101 | }
102 |
103 | .sbControlsMenu-itemIcon {
104 | margin-bottom: 6px;
105 | }
106 |
107 | /*
108 | * "Extension is enabled" toggle
109 | */
110 | .toggleSwitchContainer {
111 | display: flex;
112 | align-items: center;
113 | flex-direction: column;
114 | }
115 |
116 | .toggleSwitchContainer-switch {
117 | display: flex;
118 | margin-bottom: 6px;
119 | }
120 |
121 | .switchBg {
122 | width: 50px;
123 | height: 23px;
124 | display: block;
125 | border-radius: 18.5px;
126 | }
127 |
128 | .switchBg.shadow {
129 | box-shadow: 0.75px 0.75px 10px 0px rgba(50, 50, 50, 0.5);
130 | opacity: 1;
131 | }
132 |
133 | .switchBg.white {
134 | opacity: 1;
135 | position: absolute;
136 | background-color: #ccc;
137 | }
138 |
139 | .switchBg.blue {
140 | opacity: 0;
141 | position: absolute;
142 | background-color: var(--cb-switch-color);
143 | transition: opacity 0.2s ease-out;
144 | }
145 |
146 | .switchDot {
147 | width: 15px;
148 | margin: 4px;
149 | height: 15px;
150 | border-radius: 50%;
151 | position: absolute;
152 | transition: transform 0.2s ease-out;
153 | background-color: var(--sb-main-fg-color);
154 | box-shadow: 0.75px 0.75px 3.8px 0px rgba(50, 50, 50, 0.45);
155 | }
156 |
157 | #toggleSwitch:checked~.switchDot {
158 | transform: translateX(27px);
159 | }
160 |
161 | #toggleSwitch:checked~.switchBg.blue {
162 | opacity: 1;
163 | }
164 |
165 | #toggleSwitch:checked~.switchBg.white {
166 | transition: opacity 0.2s step-end;
167 | opacity: 0;
168 | }
169 |
170 |
171 |
172 | /*
173 | * Footer
174 | */
175 | #sbFooter {
176 | padding: 8px 0;
177 | }
178 |
179 | #sbFooter a {
180 | transition: background 0.3s ease !important;
181 | color: var(--sb-main-fg-color);
182 | display: inline-block;
183 | text-decoration: none;
184 | border-radius: 4px;
185 | background-color: #333;
186 | padding: 4px 8px;
187 | font-weight: 500;
188 | margin: 2px 1px;
189 | }
190 |
191 | #sbFooter a:hover {
192 | background-color: #444;
193 | }
194 |
195 | #sponsorTimesDonateContainer a {
196 | color: var(--sb-main-fg-color);
197 | text-decoration: none;
198 | }
199 |
200 | .activation-needed {
201 | background-color: #171717;
202 | border-radius: 15px;
203 |
204 | padding: 20px;
205 | margin: 20px;
206 |
207 | font-size: 20px;
208 | }
209 |
210 | .option-button {
211 | cursor: pointer;
212 |
213 | background-color: #0e79ca;
214 | padding: 10px;
215 | color: white;
216 | border-radius: 5px;
217 | font-size: 14px;
218 |
219 | width: max-content;
220 |
221 | margin: auto;
222 | text-align: center;
223 | }
224 |
225 | .option-button:hover:not(.disabled) {
226 | background-color: #0e79ca;
227 | }
--------------------------------------------------------------------------------
/public/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/public/shared.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --sb-slider-color: #707070;
3 | }
4 |
5 | /*
6 | * Generic utilities
7 | */
8 | .cb-grey-text {
9 | color: var(--sb-grey-fg-color);
10 | }
11 |
12 | .cb-white-text {
13 | color: var(--sb-main-fg-color);
14 | }
15 |
16 | .sbHeader {
17 | font-size: 20px;
18 | font-weight: bold;
19 | text-align: left;
20 | margin: 0;
21 | }
22 |
23 | #sponsorBlockPopupBody .u-mZ, .sbYourWorkBox .u-mZ {
24 | margin: 0 !important;
25 | position: relative;
26 | }
27 |
28 | #sponsorBlockPopupBody .hidden, .sbYourWorkBox .hidden {
29 | display: none !important;
30 | }
31 |
32 |
33 | /*
34 | * Your Work box
35 | */
36 | .sbYourWorkBox {
37 | margin: 16px;
38 | margin-bottom: 8px;
39 | border-radius: 8px;
40 | border: 2px solid var(--sb-grey-bg-color);
41 | }
42 |
43 | .sbYourWorkBox a,
44 | .sbYourWorkBox button {
45 | cursor: pointer;
46 | }
47 |
48 | .sbYourWorkCols {
49 | display: flex;
50 | border-top: 2px solid var(--sb-grey-bg-color);
51 | }
52 |
53 | .sbStatsSentence {
54 | padding: 6px 14px;
55 | border-top: 2px solid var(--sb-grey-bg-color);
56 | }
57 |
58 | /*
59 | * Increase font size of username input and display
60 | */
61 | #usernameValue,
62 | #usernameInput {
63 | font-size: 16px;
64 | flex: 1 0;
65 | }
66 |
67 | #sponsorTimesContributionsDisplay {
68 | font-size: 16px;
69 | }
70 |
71 | /*
72 | *