Compare commits

..

136 Commits
7.2.1 ... main

Author SHA1 Message Date
886d31bdea feat(browser-extension): add background script tests 2024-11-20 13:26:48 +01:00
edf5eda7dc feat(browser-extension): use plasmo framework 2024-11-19 12:57:32 +01:00
6434dd6c1c fix(api): rate limit bug 2024-11-19 12:19:21 +01:00
d16f86cd32 feat: enough github 2024-11-19 01:39:57 +01:00
12a5d68e3c feat(web): add new mobile release 2024-11-11 20:37:24 +01:00
59b4c4334e refactor(browser-extension): drop scripting permission 2024-11-11 20:35:40 +01:00
781511be3c feat(web): add new desktop release 2024-11-11 20:22:25 +01:00
657e6955a5 Merge pull request '8.0.5' (#228) from v8.0.5 into main
Reviewed-on: #228
2024-11-11 19:12:17 +00:00
c360ea2d70 Merge branch 'main' into v8.0.5 2024-11-11 20:10:41 +01:00
5fa4b9566c fix(browser-extension): issue #229 2024-11-11 20:00:59 +01:00
debc9d3b05 feat(web): add new mobile release 2024-11-11 14:33:24 +01:00
9559fc761b refactor(web): improve releases structure 2024-11-11 14:33:03 +01:00
9bece711a2 refactor(browser-extension): rename locale folder jp to ja 2024-11-11 14:20:56 +01:00
56929d0bcd feat(web): add new mobile release 2024-11-11 14:19:14 +01:00
fd242502ab feat(web): update texts 2024-11-11 14:07:02 +01:00
cc42141293 feat(web): add new desktop release 2024-11-11 14:06:40 +01:00
4dd405f0d2 Merge pull request '8.0.4' (#226) from v8.0.4 into main
Reviewed-on: #226
2024-11-11 12:44:06 +00:00
31dc8389f0 fix(browser-extension): tab creation not closing extension menu 2024-11-11 13:43:26 +01:00
b1f09ce11f feat(browser-extension): add loader now that we have more queries 2024-11-11 13:34:48 +01:00
3e539e9e1c fix(browser-extension): issue #166 2024-11-11 12:35:54 +01:00
adc8ccc391 feat(api): add version endpoint 2024-11-10 23:13:32 +01:00
2390960d21 fix(browser-extension): report context menu item sometimes disabled 2024-11-10 22:45:57 +01:00
effe22da79 fix(browser-extension): issue #199 2024-11-10 22:38:50 +01:00
40b29fdd87 feat(browser-extension): add new translations ar, hi, id, jp, ko and tr 2024-11-10 22:29:13 +01:00
f8578b0940 feat(browser-extension): add new popup_bannerSupport message 2024-11-10 22:28:50 +01:00
dc44a6f8da feat(browser-extension): improve ux/ui 2024-11-10 22:28:18 +01:00
62b6a21b95 fix(browser-extension): issue #197 2024-11-10 22:28:03 +01:00
5dc35344a1 feat(web): add new mobile release 2024-10-28 14:01:52 +01:00
f6aaf9f600 feat(web): add new desktop release 2024-10-28 13:32:04 +01:00
d781f756f6 Merge pull request '8.0.3' (#162) from v8.0.3 into main
Reviewed-on: #162
2024-10-28 12:20:34 +00:00
0c99d86b96 fix(database): issue #102 2024-10-28 12:30:49 +01:00
031f7d0b34 fix(browser-extension): issue #166 2024-10-28 12:21:50 +01:00
9293bd84ac fix(browser-extension): some broken styles in firefox 2024-10-28 12:21:38 +01:00
5c82a0e8f9 fix(database): issue #109 2024-10-25 10:37:44 +02:00
d4430a72dc fix(database): issue #108 2024-10-25 10:28:54 +02:00
9e4384a08d fix(database): issue #107 2024-10-25 10:27:12 +02:00
ff2b18256a fix(database): issue #106 2024-10-25 10:25:35 +02:00
e7ff26f1f7 fix(database): issue #103 2024-10-25 10:19:03 +02:00
85f052c3a5 fix(database): issue #100 2024-10-25 10:11:16 +02:00
8dd3f4d3c9 fix(database): issue #99 2024-10-25 10:08:50 +02:00
7a42f91532 fix(database): issue #95 2024-10-25 10:03:44 +02:00
7e2b4b3e08 fix(database): issue #94 2024-10-25 10:02:08 +02:00
1f37fa0607 fix(database): issue #93 2024-10-25 09:58:49 +02:00
fa8b3833d8 fix(database): issue #91 2024-10-23 12:10:29 +02:00
a6e187e350 fix(database): issue #90 2024-10-23 12:08:51 +02:00
49baa47eaa fix(database): issue #86 2024-10-23 12:05:06 +02:00
92d5d5507f fix(database): issue #83 2024-10-23 11:58:06 +02:00
04edca2ec5 fix(database): issue #82 2024-10-23 11:53:10 +02:00
9aaf27868a fix(database): issue #80 2024-10-23 11:46:47 +02:00
1862793a9c fix(database): issue #78 2024-10-23 11:39:57 +02:00
0a4442aa19 fix(database): issue #76 2024-10-23 11:35:48 +02:00
fc3db66cfd fix(database): issue #74 2024-10-23 11:33:07 +02:00
c4f428591e fix(database): issue #68 2024-10-23 11:23:48 +02:00
7800ed86b9 fix(database): issue #67 2024-10-21 11:15:39 +02:00
c118551dc7 fix(database): issue #66 2024-10-21 11:10:33 +02:00
8fed2f0444 fix(database): issue #60 2024-10-21 11:06:27 +02:00
cf56b67eb7 fix(database): issue #62 2024-10-21 11:04:01 +02:00
95e6cfbe84 fix(database): issue #60 2024-10-21 11:01:19 +02:00
b88a81e347 fix(database): issue #53 2024-10-21 10:51:51 +02:00
1873c4c426 fix(database): issue #49 2024-10-21 10:50:03 +02:00
8510d50ab9 fix(database): issue #47 2024-10-21 10:39:59 +02:00
362da4a7ef fix(database): issue #46 2024-10-21 10:35:17 +02:00
daa774a3da fix(database): issue #44 2024-10-21 10:31:19 +02:00
fd19ba3242 refactor(browser-extension): drop unnecessary line break 2024-10-19 18:29:03 +02:00
6b788abe28 refactor(browser-extension): drop old github references 2024-10-19 18:23:47 +02:00
1ac4812ebf chore(browser-extension): bump extension version 2024-10-19 18:23:25 +02:00
b0e56c87eb refactor(web): drop old references to GitHub, also improve alt for images 2024-10-19 18:19:24 +02:00
f8f2e64aef fix(database): issue #118 2024-10-19 11:48:24 +02:00
59229f7eeb fix(database): issue #116 2024-10-19 11:26:20 +02:00
4394695d15 fix(database): issue #117 2024-10-19 11:24:45 +02:00
a6f72cd90d fix(database): issue #118 2024-10-19 11:23:29 +02:00
35f3622e00 fix(database): issue #119 2024-10-19 11:21:25 +02:00
b7f04383e8 fix(database): issue #120 2024-10-19 11:18:23 +02:00
dfb220ce90 fix(database): issue #124 2024-10-19 11:08:38 +02:00
6827951d6d fix(database): issue #128 2024-10-19 10:55:21 +02:00
0460306fbf fix(database): issue #129 2024-10-19 10:53:03 +02:00
21112ffcbc fix(database): issue #133 2024-10-19 10:48:54 +02:00
42d5dd6885 fix(database): issue #135 2024-10-19 10:40:26 +02:00
08efbe3e0f fix(database): issue #141 2024-10-19 10:30:51 +02:00
c99a18d1a5 fix(database): issue #140 2024-10-19 10:29:02 +02:00
32bb4ce5e6 fix(database): issue #142 2024-10-19 10:27:23 +02:00
3046799d10 fix(database): issue #145 2024-10-19 10:25:32 +02:00
7b14777d1c fix(database): issue #146 2024-10-19 10:22:56 +02:00
d015c4a993 fix(database): issue #147 2024-10-19 10:21:14 +02:00
c2a80cd97c fix(database): issue #148 2024-10-19 10:19:04 +02:00
a7b267c0eb fix(database): issue #153 2024-10-19 10:14:15 +02:00
105a73b17c fix(database): issue #156 2024-10-19 10:12:05 +02:00
51d86c36f2 fix(database): issue #158 2024-10-19 10:08:05 +02:00
0dd47949d2 feat(web): add new mobile release 2024-10-19 10:02:27 +02:00
c0afca20fe feat(web): add new desktop release 2024-10-19 10:01:02 +02:00
a38d77d46d Merge pull request '8.0.2' (#155) from v8.0.2 into main
Reviewed-on: #155
2024-10-19 07:44:05 +00:00
7d8d3957ee feat(browser-extension): adapt code to database exclusions and fix minor bugs 2024-10-18 16:44:09 +02:00
a5766071fd chore(browser-extension): drop exclude_matches from content_scripts 2024-10-18 16:31:43 +02:00
33a2e096a9 chore(browser-extension): bump extension version 2024-10-18 16:30:45 +02:00
daa805f4f4 refactor(database): improve naming to add exclude_matches to it 2024-10-18 16:18:52 +02:00
accc5efda8 Merge pull request '8.0.1' (#144) from v8.0.1 into main
Reviewed-on: #144
2024-10-15 16:13:34 +00:00
f972832fe4 feat(web): add new mobile release 2024-10-15 18:11:46 +02:00
30786010c1 feat(web): add new desktop release 2024-10-15 18:03:43 +02:00
4f74213a44 fix(browser-extension): minify 2024-10-15 17:54:34 +02:00
fe9a60f357 Merge pull request '8.0.0' (#3) from v8.0.0 into main
Reviewed-on: #3
2024-10-15 15:01:18 +00:00
f620f68cec refactor: wiki home urls 2024-10-15 16:45:20 +02:00
027020cb80 chore: drop rules from commitlint 2024-10-15 16:45:20 +02:00
c82f39df6b fix(browser-extension): issues fetch every popup open 2024-10-15 16:45:20 +02:00
d89b1c6c08 feat(browser-extension): max attempts fetching data, issues 2024-10-15 16:45:20 +02:00
e4b0cfd817 refactor(browser-extension): request manager comment and naming 2024-10-15 16:45:20 +02:00
8b527e135e feat(browser-extension): add extension update banner 2024-10-15 16:45:20 +02:00
0cb264d796 fix(browser-extension): regexp to match domains 2024-10-15 16:45:20 +02:00
a75518b0a2 feat(browser-extension): migrate to git.wanhose.dev 2024-10-15 16:45:20 +02:00
274f282fbc fix(browser-extension): possible undefined 2024-10-15 16:45:20 +02:00
ceb00de0fa fix(browser-extension): report cancel button clickable area 2024-10-15 16:45:20 +02:00
ebf68ee0cc feat(web): add new issue tracking section 2024-10-15 16:45:20 +02:00
e9758d9e2d fix(browser-extension): media queries for mobile version 2024-10-15 16:45:20 +02:00
1c96a2e262 feat(web): update options and popup images 2024-10-15 16:45:20 +02:00
74b479380b fix(browser-extension): how options and popup look on mobile 2024-10-15 16:45:20 +02:00
2b8d596006 chore(browser-extension): bump extension version 2024-10-15 16:45:20 +02:00
317298521e feat(browser-extension): minor improvement to new error extra text english translation 2024-10-15 16:45:20 +02:00
95f0131f56 feat(browser-extension): enable report only on supported pages 2024-10-15 16:45:20 +02:00
74dec0877a feat(browser-extension): handle new error cases when reporting 2024-10-15 16:45:20 +02:00
b216746697 feat(browser-extension): bump api version to use 2024-10-15 16:45:20 +02:00
03efab9acc refactor(browser-extension): move report dialog to popup 2024-10-15 16:45:20 +02:00
ccc313618d fix(browser-extension): popup auto height issue 2024-10-15 16:45:20 +02:00
c7f0b31dd9 refactor: drop old dialog stuff 2024-10-15 16:45:20 +02:00
46aefd4688 feat(api): add issues and version endpoints 2024-10-15 16:45:20 +02:00
c1d2a3d93a feat(browser-extension): switch to warn mode after reporting an issue 2024-10-15 16:45:20 +02:00
c76192ffaf fix(browser-extension): off/on performance 2024-10-15 16:45:20 +02:00
79d6d84774 feat(browser-extension): add issue banner translations 2024-10-15 16:45:20 +02:00
1c4928d89e feat(browser-extension): add request batching, issue details fetching and minor refactors to scripts 2024-10-15 16:45:20 +02:00
07e03c8ff5 feat(browser-extension): add issue banner to popup giving user some information about what's wrong and where to find the issue easily 2024-10-15 16:45:20 +02:00
69831ab07d refactor(browser-extension): assets naming 2024-10-15 16:45:20 +02:00
f1255b9348 chore(browser-extension): upgrade manifest version 2024-10-15 16:45:20 +02:00
4b4ab1884d feat(api): add trailing slash to v5/issues/:hostname 2024-10-15 16:04:10 +02:00
abf972c658 fix(web): update GitLab to Git 2024-10-15 13:03:06 +02:00
fd1c57996b refactor: move licenses up 2024-10-15 11:51:07 +02:00
0a9e0fa3a9 docs: add resources to main readme 2024-10-15 11:46:14 +02:00
a66c4bedc7 feat(web): update links to wiki 2024-10-14 15:35:33 +02:00
d3e7c061f0 docs(browser-extension): move installation guides to wiki 2024-10-14 15:33:37 +02:00
193 changed files with 18225 additions and 9432 deletions

View File

@ -20,7 +20,6 @@
"database",
"docs",
"packages",
"rules",
"web"
]
]

4
.gitignore vendored
View File

@ -1,9 +1,7 @@
_metadata/
!.yarn/plugins
!.yarn/releases
.DS_Store
.env
.plasmo/
.pnp.*
.yarn/*
build/
node_modules/

View File

@ -1,4 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
yarn commitlint --edit "$1"
pnpm commitlint --edit "$1"

View File

@ -1,4 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
yarn lint-staged
pnpm lint-staged

View File

@ -1,3 +1,3 @@
{
"packages/**/*.{js,ts}": ["prettier --loglevel=silent --write", "bash -c 'yarn lint'"]
"packages/**/*.{js,ts}": ["prettier --loglevel=silent --write", "bash -c 'pnpm lint'"]
}

View File

@ -1,2 +0,0 @@
package.json
.yarnrc.yml

View File

@ -1,4 +1,12 @@
{
"overrides": [
{
"files": ["*.jsonc", ".eslintrc", "tsconfig*.json"],
"options": {
"trailingComma": "none"
}
}
],
"printWidth": 100,
"semi": true,
"singleQuote": true,

View File

@ -1,4 +1,7 @@
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "always"
},
"files.associations": {
".commitlintrc": "json",
".lintstagedrc": "json"

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,9 +0,0 @@
enableGlobalCache: true
nodeLinker: node-modules
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-outdated.cjs
spec: "https://mskelton.dev/yarn-outdated/v2"
yarnPath: .yarn/releases/yarn-4.2.1.cjs

View File

@ -1,19 +1,26 @@
> AN IMPORTANT UPDATE
> <br><br>
> On the morning of October 11, 2024, GitHub unexpectedly hid our repository without any prior notification. This sudden action immediately disrupted all of our API services, as they relied on files from the repository that were no longer accessible. Our team reached out to GitHub support to understand the situation, but we have not yet received any response. Given the critical impact on our services and the lack of communication from GitHub, we decided to migrate the entire repository to an alternative platform to ensure continuity and reliability.
> <br><br>
> We initially migrated the repository, releases, and open issues (excluding discussions) to GitLab. However, during the migration of issues, GitLab's own spam detection mechanism mistakenly identified its bot activity as spam, leading to our issues being hidden. Despite reaching out to GitLab support, we have not yet received a resolution for this incident. Faced with these ongoing platform limitations and communication delays, we have opted to move forward with self-hosting our own Git system using Gitea to ensure full control over our repository and services.
> <br><br>
> We will no longer support platforms that overlook the contributions of our users and the significant work invested over the last five years. Once our GitHub account is reinstated, we will set up a redirect to guide users to our new, self-hosted repository.
> <br><br>
> Thank you to our community for your patience. Your contributions remain vital to this project, and we are committed to ensuring its stability and growth in a more secure environment.
# Cookie Dialog Monster
Cookie Dialog Monster is a browser extension that hides cookie consent dialogs without changing user preferences. By default, we do NOT accept cookies (except in [a few cases](https://git.wanhose.dev/wanhose/cookie-dialog-monster/src/branch/main/database.json#L248) where the pages do not function without accepting them). You can report broken sites with a single click, which will create an issue in this repository to be fixed promptly.
Cookie Dialog Monster is a browser extension that hides cookie consent dialogs without changing user preferences. By default, we do NOT accept cookies (except in [a few cases](https://git.wanhose.dev/wanhose/cookie-dialog-monster/src/branch/main/database.json) where the pages do not function without accepting them). You can report broken sites with a single click, which will create an issue in this repository to be fixed promptly.
## Repositories
- [API](/wanhose/cookie-dialog-monster/src/branch/main/packages/api)
- [Browser extension](/wanhose/cookie-dialog-monster/src/branch/main/packages/browser-extension)
- [Web](/wanhose/cookie-dialog-monster/src/branch/main/packages/web)
## Resources
- [FAQs](https://git.wanhose.dev/wanhose/cookie-dialog-monster/wiki/Help-or-issues%3F#faqs)
- [Guides](https://git.wanhose.dev/wanhose/cookie-dialog-monster/wiki/Help-or-issues%3F#guides)
- [Issues](https://git.wanhose.dev/wanhose/cookie-dialog-monster/issues)
- [Pull requests](https://git.wanhose.dev/wanhose/cookie-dialog-monster/pulls)
- [Releases](https://git.wanhose.dev/wanhose/cookie-dialog-monster/releases)
- [Wiki](https://git.wanhose.dev/wanhose/cookie-dialog-monster/wiki/Help-or-issues%3F)
## Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
## License
[MIT](https://choosealicense.com/licenses/mit/)

File diff suppressed because it is too large Load Diff

View File

@ -3,8 +3,9 @@
"private": true,
"version": "1.0.0",
"scripts": {
"build": "yarn workspaces foreach --all -p run build",
"lint": "yarn workspaces foreach --all -p run lint",
"build": "pnpm -r run build",
"lint": "pnpm -r run lint",
"preinstall": "npx only-allow pnpm",
"prepare": "husky"
},
"devDependencies": {
@ -14,11 +15,8 @@
"lint-staged": "^15.2.2",
"prettier": "^3.2.5"
},
"workspaces": [
"packages/*"
],
"engines": {
"node": "20.x"
},
"packageManager": "yarn@4.2.1"
"license": "MIT"
}

View File

@ -2,34 +2,26 @@
## Installation
Make sure you have [Node.js](https://nodejs.org/) (version 20.x) and [Yarn](https://yarnpkg.com/) installed.
Make sure you have [Node.js](https://nodejs.org/) (version 20.x) and [pnpm](https://pnpm.io/) installed.
```bash
yarn install
pnpm i
```
## Scripts
### `yarn build`
### `pnpm build`
Removes the build directory and compiles TypeScript files.
### `yarn dev`
### `pnpm dev`
Starts the server in development mode with nodemon.
### `yarn lint`
### `pnpm lint`
Lints the codebase using ESLint.
### `yarn start`
### `pnpm start`
Starts the API server instance in production mode.
## Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
## License
[MIT](https://choosealicense.com/licenses/mit/)

View File

@ -33,7 +33,5 @@
},
"engines": {
"node": "20.x"
},
"packageManager": "yarn@4.1.0",
"license": "MIT"
}
}

View File

@ -12,7 +12,12 @@ import v4ReportRoutes from 'routes/v4/report';
import v5DataRoutes from 'routes/v5/data';
import v5IssuesRoutes from 'routes/v5/issues';
import v5ReportRoutes from 'routes/v5/report';
import v6DataRoutes from 'routes/v6/data';
import v6IssuesRoutes from 'routes/v6/issues';
import v6ReportRoutes from 'routes/v6/report';
import v6VersionRoutes from 'routes/v6/version';
import environment from 'services/environment';
import { keyGenerator } from 'services/rateLimit';
const server = fastify({ logger: true });
@ -26,6 +31,7 @@ server.register(cors, {
server.register(rateLimit, {
global: false,
keyGenerator,
});
server.register(v1EntriesRoutes, { prefix: '/rest/v1' });
@ -39,6 +45,10 @@ server.register(v4ReportRoutes, { prefix: '/rest/v4' });
server.register(v5DataRoutes, { prefix: '/rest/v5' });
server.register(v5IssuesRoutes, { prefix: '/rest/v5' });
server.register(v5ReportRoutes, { prefix: '/rest/v5' });
server.register(v6DataRoutes, { prefix: '/rest/v6' });
server.register(v6IssuesRoutes, { prefix: '/rest/v6' });
server.register(v6ReportRoutes, { prefix: '/rest/v6' });
server.register(v6VersionRoutes, { prefix: '/rest/v6' });
server.listen({ host: '0.0.0.0', port: environment.port }, (error, address) => {
if (error) {

View File

@ -1,6 +1,6 @@
import { FastifyInstance, RouteShorthandOptions } from 'fastify';
import fetch from 'node-fetch';
import { parseNewFix } from 'services/compatibility';
import { parseAction } from 'services/compatibility';
import environment from 'services/environment';
import { RATE_LIMIT_10_PER_MIN } from 'services/rateLimit';
@ -14,17 +14,18 @@ export default (server: FastifyInstance, _options: RouteShorthandOptions, done:
},
async (_request, reply) => {
try {
const url = `${environment.gitea.raw}/database.json`;
const result = await (await fetch(url)).json();
const database = `${environment.gitea.raw}/database.json`;
const response = await fetch(database);
const { actions, exclusions, keywords, tokens } = await response.json();
reply.send({
data: {
classes: result.tokens.classes,
commonWords: result.commonWords,
elements: result.tokens.selectors,
fixes: result.fixes.map(parseNewFix),
skips: result.skips.domains,
tags: result.skips.tags,
classes: tokens.classes,
commonWords: keywords,
elements: tokens.selectors,
fixes: actions.map(parseAction),
skips: exclusions.overflows,
tags: exclusions.tags,
},
success: true,
});

View File

@ -1,5 +1,5 @@
import { FastifyInstance, RouteShorthandOptions } from 'fastify';
import { formatMessage } from 'services/format';
import { formatMessage, formatDomainFromURL } from 'services/format';
import { createIssue, createIssueComment, getIssue, updateIssue } from 'services/git';
import { RATE_LIMIT_1_PER_MIN } from 'services/rateLimit';
import { validatorCompiler } from 'services/validation';
@ -34,8 +34,8 @@ export default (server: FastifyInstance, _options: RouteShorthandOptions, done:
async (request, reply) => {
try {
const { reason, url, userAgent, version } = request.body;
const hostname = new URL(url).hostname.split('.').slice(-3).join('.').replace('www.', '');
const issue = await getIssue({ title: hostname });
const domain = formatDomainFromURL(new URL(url));
const issue = await getIssue({ title: domain });
const ua = new UAParser(userAgent ?? '').getResult();
if (issue) {
@ -61,7 +61,7 @@ export default (server: FastifyInstance, _options: RouteShorthandOptions, done:
await createIssue({
description: formatMessage({ reason, ua, url, version }),
labels: ['bug'],
title: hostname,
title: domain,
});
reply.send({

View File

@ -1,6 +1,6 @@
import { FastifyInstance, RouteShorthandOptions } from 'fastify';
import fetch from 'node-fetch';
import { parseNewFix } from 'services/compatibility';
import { parseAction } from 'services/compatibility';
import environment from 'services/environment';
import { RATE_LIMIT_3_PER_MIN } from 'services/rateLimit';
@ -14,13 +14,16 @@ export default (server: FastifyInstance, _options: RouteShorthandOptions, done:
},
async (_request, reply) => {
try {
const url = `${environment.gitea.raw}/database.json`;
const result = await (await fetch(url)).json();
const database = `${environment.gitea.raw}/database.json`;
const response = await fetch(database);
const { actions, exclusions, keywords, ...rest } = await response.json();
reply.send({
data: {
...result,
fixes: result.fixes.map(parseNewFix),
...rest,
commonWords: keywords,
fixes: actions.map(parseAction),
skips: { domains: exclusions.overflows, tags: exclusions.tags },
},
success: true,
});

View File

@ -1,5 +1,5 @@
import { FastifyInstance, RouteShorthandOptions } from 'fastify';
import { formatMessage } from 'services/format';
import { formatMessage, formatDomainFromURL } from 'services/format';
import { createIssue, createIssueComment, getIssue, updateIssue } from 'services/git';
import { RATE_LIMIT_1_PER_MIN } from 'services/rateLimit';
import { validatorCompiler } from 'services/validation';
@ -34,8 +34,8 @@ export default (server: FastifyInstance, _options: RouteShorthandOptions, done:
async (request, reply) => {
try {
const { reason, url, userAgent, version } = request.body;
const hostname = new URL(url).hostname.split('.').slice(-3).join('.').replace('www.', '');
const issue = await getIssue({ title: hostname });
const domain = formatDomainFromURL(new URL(url));
const issue = await getIssue({ title: domain });
const ua = new UAParser(userAgent ?? '').getResult();
if (issue) {
@ -62,7 +62,7 @@ export default (server: FastifyInstance, _options: RouteShorthandOptions, done:
const newIssue = await createIssue({
description: formatMessage({ reason, ua, url, version }),
labels: ['bug'],
title: hostname,
title: domain,
});
reply.send({

View File

@ -1,5 +1,6 @@
import { FastifyInstance, RouteShorthandOptions } from 'fastify';
import fetch from 'node-fetch';
import { parseActionName, toDeclarativeNetRequestRule } from 'services/compatibility';
import environment from 'services/environment';
import { RATE_LIMIT_3_PER_MIN } from 'services/rateLimit';
@ -13,14 +14,18 @@ export default (server: FastifyInstance, _options: RouteShorthandOptions, done:
},
async (_request, reply) => {
try {
const database = `${environment.gitea.raw}/database.json`;
const options = { headers: { 'Cache-Control': 'no-cache' } };
const url = `${environment.gitea.raw}/database.json`;
const { rules, ...result } = await (await fetch(url, options)).json();
const response = await fetch(database, options);
const { actions, exclusions, keywords, rules, ...rest } = await response.json();
reply.send({
data: {
...result,
...rest,
actions: actions.map(parseActionName),
commonWords: keywords,
rules: rules.map(toDeclarativeNetRequestRule),
skips: { domains: exclusions.overflows, tags: exclusions.tags },
},
success: true,
});
@ -35,17 +40,3 @@ export default (server: FastifyInstance, _options: RouteShorthandOptions, done:
done();
};
function toDeclarativeNetRequestRule(urlFilter: string, index: number) {
return {
action: {
type: 'block',
},
condition: {
resourceTypes: ['font', 'image', 'media', 'object', 'script', 'stylesheet', 'xmlhttprequest'],
urlFilter,
},
id: index + 1,
priority: 1,
};
}

View File

@ -5,14 +5,14 @@ import { validatorCompiler } from 'services/validation';
import * as yup from 'yup';
const GetIssuesParamsSchema = yup.object().shape({
hostname: yup.string().required(),
domain: yup.string().required(),
});
type GetIssuesParams = yup.InferType<typeof GetIssuesParamsSchema>;
export default (server: FastifyInstance, _options: RouteShorthandOptions, done: () => void) => {
server.get<{ Params: GetIssuesParams }>(
'/issues/:hostname',
'/issues/:domain/',
{
config: {
rateLimit: RATE_LIMIT_10_PER_MIN,
@ -24,8 +24,8 @@ export default (server: FastifyInstance, _options: RouteShorthandOptions, done:
},
async (request, reply) => {
try {
const { hostname } = request.params;
const issue = await getIssue({ title: hostname });
const { domain } = request.params;
const issue = await getIssue({ title: domain });
if (
issue &&

View File

@ -1,5 +1,5 @@
import { FastifyInstance, RouteShorthandOptions } from 'fastify';
import { formatMessage } from 'services/format';
import { formatMessage, formatDomainFromURL } from 'services/format';
import { createIssue, getIssue, updateIssue } from 'services/git';
import { RATE_LIMIT_1_PER_MIN } from 'services/rateLimit';
import { validatorCompiler } from 'services/validation';
@ -34,8 +34,8 @@ export default (server: FastifyInstance, _options: RouteShorthandOptions, done:
async (request, reply) => {
try {
const { reason, url, userAgent, version } = request.body;
const hostname = new URL(url).hostname.split('.').slice(-3).join('.').replace('www.', '');
const issue = await getIssue({ title: hostname });
const domain = formatDomainFromURL(new URL(url));
const issue = await getIssue({ title: domain });
const ua = new UAParser(userAgent ?? '').getResult();
if (issue) {
@ -75,7 +75,7 @@ export default (server: FastifyInstance, _options: RouteShorthandOptions, done:
const newIssue = await createIssue({
description: formatMessage({ reason, ua, url, version }),
labels: ['bug'],
title: hostname,
title: domain,
});
reply.send({

View File

@ -0,0 +1,39 @@
import { FastifyInstance, RouteShorthandOptions } from 'fastify';
import fetch from 'node-fetch';
import { toDeclarativeNetRequestRule } from 'services/compatibility';
import environment from 'services/environment';
import { RATE_LIMIT_3_PER_MIN } from 'services/rateLimit';
export default (server: FastifyInstance, _options: RouteShorthandOptions, done: () => void) => {
server.get(
'/data/',
{
config: {
rateLimit: RATE_LIMIT_3_PER_MIN,
},
},
async (_request, reply) => {
try {
const database = `${environment.gitea.raw}/database.json`;
const options = { headers: { 'Cache-Control': 'no-cache' } };
const response = await fetch(database, options);
const { rules, ...rest } = await response.json();
reply.send({
data: {
...rest,
rules: rules.map(toDeclarativeNetRequestRule),
},
success: true,
});
} catch (error) {
reply.send({
errors: [error.message],
success: false,
});
}
}
);
done();
};

View File

@ -0,0 +1 @@
export { default as default } from '../v5/issues';

View File

@ -0,0 +1 @@
export { default as default } from '../v5/report';

View File

@ -0,0 +1,35 @@
import { FastifyInstance, RouteShorthandOptions } from 'fastify';
import fetch from 'node-fetch';
import environment from 'services/environment';
import { RATE_LIMIT_10_PER_MIN } from 'services/rateLimit';
export default (server: FastifyInstance, _options: RouteShorthandOptions, done: () => void) => {
server.get(
'/version/',
{
config: {
rateLimit: RATE_LIMIT_10_PER_MIN,
},
},
async (_request, reply) => {
try {
const manifest = `${environment.gitea.raw}/packages/browser-extension/src/manifest.json`;
const options = { headers: { 'Cache-Control': 'no-cache' } };
const response = await fetch(manifest, options);
const { version } = await response.json();
reply.send({
data: version,
success: true,
});
} catch (error) {
reply.send({
errors: [error.message],
success: false,
});
}
}
);
done();
};

View File

@ -1,13 +1,32 @@
/**
* Parse the new fix object into the old string format used by older versions of the extension
*/
export function parseNewFix(fix: Fix): string {
return `${fix.domain}##${fix.selector}##${fix.action}${fix.property ? `##${fix.property}` : ''}`;
export function parseAction(action: Action): string {
return `${action.domain}##${action.selector}##${action.name}${action.property ? `##${action.property}` : ''}`;
}
export interface Fix {
readonly action: string;
export function parseActionName(
action: Action
): Omit<Action, 'name'> & { readonly action: string } {
const { name, ...rest } = action;
return { action: name, ...rest };
}
export function toDeclarativeNetRequestRule(urlFilter: string, index: number) {
return {
action: {
type: 'block',
},
condition: {
resourceTypes: ['font', 'image', 'media', 'object', 'script', 'stylesheet', 'xmlhttprequest'],
urlFilter,
},
id: index + 1,
priority: 1,
};
}
export interface Action {
readonly domain: string;
readonly name: string;
readonly property?: string;
readonly selector: string;
}

View File

@ -20,6 +20,16 @@ export function formatMessage(params: FormatMessageParams): string {
].join('\n');
}
export function formatDomainFromURL(value: URL): string {
let result: string = value.hostname;
if (result.startsWith('www.')) {
result = result.replace('www.', '');
}
return result;
}
export interface FormatMessageParams {
readonly reason?: string;
readonly ua: UAParserResult;

View File

@ -1,3 +1,5 @@
import type { FastifyRequest } from 'fastify';
export const RATE_LIMIT_1_PER_HOUR = {
max: 1,
timeWindow: '1 hour',
@ -17,3 +19,9 @@ export const RATE_LIMIT_3_PER_MIN = {
max: 3,
timeWindow: '1 minute',
};
export function keyGenerator(req: FastifyRequest): string {
const userIdentifier = req.headers['x-forwarded-for'] || req.headers['x-real-ip'] || req.ip;
return `${userIdentifier}:${req.routerPath}`;
}

View File

@ -1,9 +1,41 @@
{
"env": {
"browser": true,
"node": true,
"es6": true
},
"extends": ["plugin:prettier/recommended"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:jest-dom/recommended",
"plugin:jsx-a11y/recommended",
"plugin:prettier/recommended",
"plugin:react/jsx-runtime",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:testing-library/dom"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest"
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": ["import", "simple-import-sort"],
"rules": {
"@typescript-eslint/no-explicit-any": "off",
"import/first": "error",
"import/newline-after-import": "error",
"import/no-duplicates": "error",
"react/react-in-jsx-scope": "off",
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error"
},
"settings": {
"react": {
"version": "detect"
}
}
}

View File

@ -1,21 +0,0 @@
MIT License Copyright (c) 2022 wanhose
Permission is hereby
granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice
(including the next paragraph) shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,14 +1,5 @@
# Cookie Dialog Monster Browser Extension
## Downloads
- [Chrome Web Store](https://chrome.google.com/webstore/detail/djcbfpkdhdkaflcigibkbpboflaplabg)
- [Edge Add-ons](https://microsoftedge.microsoft.com/addons/detail/hbogodfciblakeneadpcolhmfckmjcii)
- [Mozilla Firefox (.xpi)](https://www.cookie-dialog-monster.com/releases/latest.xpi)
- Advanced users or non-listed browsers ¹
¹ Scroll down to [this section](#installation-for-advanced-users-and-non-listed-browser-users) and follow the instructions to install the latest version of this extension in your browser.
## Compatibility
- All browsers based on Chromium 127+ (Blisk, Brave, Colibri, Epic Browser, Iron Browser, Vivaldi and many more)
@ -17,67 +8,13 @@
- Mozilla Firefox 126+
- Mozilla Firefox Mobile 126+
## Installation (for Mozilla Firefox users)
## Downloads
1. Start by downloading the latest `.xpi` file [from here](https://www.cookie-dialog-monster.com/releases/latest.xpi).
2. Open your Firefox browser. Depending on your browser settings, Firefox may prompt you to install the add-on automatically upon download completion. If the installation prompt appears, you can proceed with the installation directly from the prompt by clicking **Add** and skip to step 8.
3. If the automatic prompt does not appear, navigate to [about:addons](about:addons) to open the Add-ons Manager.
4. Click on the gear icon (⚙️) to open the menu.
5. Select **Install Add-on From File...** from the menu.
6. A file dialog will open. Navigate to and select the downloaded `.xpi` file.
7. A confirmation dialog will appear asking if you wish to install the add-on. Click **Add** to proceed.
8. Congratulations! You've successfully installed the latest build of this extension in Firefox.
- [Chrome Web Store](https://chrome.google.com/webstore/detail/djcbfpkdhdkaflcigibkbpboflaplabg)
- [Edge Add-ons](https://microsoftedge.microsoft.com/addons/detail/hbogodfciblakeneadpcolhmfckmjcii)
- [Mozilla Firefox](https://www.cookie-dialog-monster.com/releases/mozilla/latest.xpi)
- [Mozilla Firefox Mobile](https://www.cookie-dialog-monster.com/releases/mozilla-mobile/latest.xpi)
Optionally, if you wish to allow the extension to run in private browsing mode, find the extension in the list, click on the **Details** button, and then enable the **Allow in Private Browsing** checkbox.
## Installation
## Installation (for Mozilla Firefox Mobile users)
1. Start by downloading the latest `.xpi` file [from here](https://www.cookie-dialog-monster.com/releases/latest-mozilla-mobile.xpi).
2. Open your Firefox browser, then tap the three-dot menu icon in the bottom-right corner of the browser and click on the **Settings** option.
3. If you haven't already enabled debug mode, go to the About Firefox option and tap the Firefox logo 5 times. If you have already enabled it, skip to step 4.
4. Go back and you will see a new option **Install extension from file**, click on it.
5. A file dialog will open. Navigate to and select the downloaded `.xpi` file.
6. A confirmation dialog will appear asking if you wish to install the add-on. Click **Add** to proceed.
7. Congratulations! You've successfully installed the latest build of this extension in Firefox.
Optionally, if you wish to allow the extension to run in private browsing mode, find the extension in the list, click on the **Details** button, and then enable the **Allow in Private Browsing** checkbox.
## Installation (for advanced and non-listed browser users)
1. Start by downloading the latest zip file [from here](https://www.cookie-dialog-monster.com/releases/latest.zip).
2. Extract the downloaded zip file to a location on your local machine.
3. Open your browser's extensions page:
- For Chrome, navigate to [chrome://extensions](chrome://extensions)
- For Firefox, navigate to [about:addons](about:addons)
- For Edge, navigate to [edge://extensions](edge://extensions)
(Replace with the correct URL based on your browser)
4. Enable **Developer mode** (usually a toggle switch in the top right corner of the extensions page).
5. Click on the **Load Temporary Add-on…** button in Firefox or **Load unpacked** in other browsers.
6. A file dialog will open. Navigate to and select the extracted folder (or `manifest.json` file inside this folder if needed).
7. Congratulations! You've successfully loaded the latest build of this extension.
## Installation (only for developers)
1. Start by cloning the repository to your local machine using the command `git clone <repository-url>` or downloading it from the assets section of a release.
2. Navigate to the cloned repository's directory with `cd <repository-name>`.
3. Open your browser's extensions page:
- For Chrome, navigate to [chrome://extensions](chrome://extensions)
- For Firefox, navigate to [about:addons](about:addons)
- For Edge, navigate to [edge://extensions](edge://extensions)
(Replace with the correct URL based on your browser)
4. Enable **Developer mode** (usually a toggle switch in the top right corner of the extensions page).
5. Click on the **Load unpacked** button.
6. A file dialog will open. Navigate to and select the `packages/browser-extension/src` folder from the cloned repository.
7. Congratulations! You've successfully loaded the development build of this extension.
To ensure you're using the latest version of the extension, it's important to regularly pull the latest changes from the repository. You can do this by navigating to the repository's directory in your terminal and running the command `git pull`. This will fetch and download the latest changes from the remote repository and merge them into your local repository.
In some cases, after pulling the latest changes and rebuilding the extension, your browser may not immediately reflect the updates. This is because browsers typically cache extensions for performance reasons. To manually update the extension, navigate to your browser's extensions page. Look for a button or option to "Update" or "Reload" the extension. This forces the browser to reload the extension, ensuring it's running the latest version. Remember, this process may vary slightly depending on the browser you're using. Always refer to your browser's specific instructions for managing extensions.
## Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
## License
[MIT](https://choosealicense.com/licenses/mit/)
To install the extension, choose [the appropriate step-by-step guide](https://git.wanhose.dev/wanhose/cookie-dialog-monster/wiki/Help-or-issues%3F#guides) for your needs.

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,25 @@
import { createRequire } from 'module';
import { pathsToModuleNameMapper } from 'ts-jest';
const require = createRequire(import.meta.url);
const tsconfig = require('./tsconfig.json');
/**
* @type {import('@jest/types').Config.InitialOptions}
*/
const config = {
extensionsToTreatAsEsm: ['.ts', '.tsx'],
moduleNameMapper: {
...pathsToModuleNameMapper(tsconfig.compilerOptions.paths, { prefix: '<rootDir>/' }),
'^url:~assets/(.+).png$': '<rootDir>/mocks/assets/$1.mock.ts',
},
preset: 'ts-jest/presets/default-esm',
setupFiles: ['./jest.setup.ts'],
testEnvironment: 'jsdom',
testRegex: ['^.+\\.test.tsx?$'],
transform: {
'^.+.tsx?$': ['ts-jest', { isolatedModules: true, useESM: true }],
},
};
export default config;

View File

@ -0,0 +1,5 @@
import 'jest-webextension-mock';
import './mocks/chrome.mock';
import './mocks/plasmo.mock';
import './mocks/utils/domain.mock';
import './mocks/utils/storage.mock';

View File

@ -0,0 +1,98 @@
{
"appDesc": {
"message": "هل ذكر أحد حوارات موافقة ملفات تعريف الارتباط؟ 😋"
},
"contextMenu_issueOption": {
"message": "متابعة هذه المشكلة"
},
"contextMenu_reportOption": {
"message": "الإبلاغ عن هذا الموقع"
},
"contextMenu_settingsOption": {
"message": "إدارة قائمة الاستبعاد"
},
"options_addButton": {
"message": "إضافة استبعاد"
},
"options_addPrompt": {
"message": "أدخل النطاق"
},
"options_clearButton": {
"message": "مسح القائمة"
},
"options_empty": {
"message": "لم يتم العثور على استثناءات"
},
"options_exclusionListTitle": {
"message": "قائمة الاستبعاد"
},
"options_exportButton": {
"message": "تصدير القائمة"
},
"options_filterPlaceholder": {
"message": "اضغط ENTER للتصفية بعد الكتابة"
},
"options_importButton": {
"message": "استيراد القائمة"
},
"popup_bannerIssueOpen": {
"message": "تم الإبلاغ عن مشكلة في هذه الصفحة. قد لا تعمل الإضافة بشكل متوقع مؤقتًا. تابع المشكلة أو أضف المزيد من المعلومات"
},
"popup_bannerIssueWontFix": {
"message": "تم الإبلاغ عن مشكلة في هذه الصفحة ولكن لن يتم إصلاحها. يُرجى العلم أن الإضافة قد لا تعمل كما هو متوقع في هذه الصفحة. نوصي بشدة بإيقاف تشغيل الإضافة باستخدام الزر أدناه"
},
"popup_bannerSupport": {
"message": "لم يعد هذا الموقع مدعومًا. لمزيد من المعلومات، يرجى المتابعة هنا"
},
"popup_bannerUpdateAvailable": {
"message": "التحديث متاح. يرجى تثبيت أحدث إصدار لحل المشكلات المعروفة قبل الإبلاغ عن مشكلات جديدة"
},
"popup_contributeOption": {
"message": "ساهم في هذا المشروع"
},
"popup_databaseVersion": {
"message": "إصدار قاعدة البيانات"
},
"popup_extensionVersion": {
"message": "إصدار الإضافة"
},
"popup_helpOption": {
"message": "المساعدة أو المشاكل؟"
},
"popup_rateOption": {
"message": "قيّم هذه الإضافة"
},
"report_bodyText": {
"message": "يرجى عدم مشاركة أي معلومات شخصية في هذا التقرير. إذا كنت تريد إضافة المزيد من التفاصيل، انتقل إلى المشكلة في الخطوة التالية وأضف تعليقًا."
},
"report_cancelButtonText": {
"message": "إلغاء"
},
"report_reasonInputError": {
"message": "يرجى إدخال سبب يتكون من 10 أحرف على الأقل ولا يزيد عن 1000 حرف"
},
"report_reasonInputLabel": {
"message": "السبب"
},
"report_reasonInputPlaceholder": {
"message": "ظهر إشعار"
},
"report_submitErrorExtraText": {
"message": "يبدو أن هناك مشكلة مفتوحة بالفعل أو تم تحديدها بأنها لن يتم إصلاحها. يرجى الدخول إلى المشكلة إذا كنت ترغب في إضافة المزيد من المعلومات باستخدام الزر التالي."
},
"report_submitErrorText": {
"message": "لم يتم إرسال التقرير؟"
},
"report_submitSuccessExtraText": {
"message": "بينما نعمل على هذه المسألة، يمكنك إضافة هذا الموقع إلى قائمة الاستبعاد في إعدادات الإضافة أو تعطيل الإضافة لهذا الموقع بالنقر فوق أيقونة الإضافة في شريط الأدوات الخاص بالمتصفح"
},
"report_submitSuccessText": {
"message": "تم إرسال التقرير!"
},
"report_urlInputError": {
"message": "يرجى إدخال عنوان URL صالح لا يزيد عن 1000 حرف"
},
"report_urlInputLabel": {
"message": "URL"
}
}

View File

@ -0,0 +1,98 @@
{
"appDesc": {
"message": "Hat jemand Cookie-Einwilligungsdialoge gesagt? 😋"
},
"contextMenu_issueOption": {
"message": "Folgen Sie diesem Problem"
},
"contextMenu_reportOption": {
"message": "Diese Website melden"
},
"contextMenu_settingsOption": {
"message": "Ausschlussliste verwalten"
},
"options_addButton": {
"message": "Ausschluss hinzufügen"
},
"options_addPrompt": {
"message": "Domain eingeben"
},
"options_clearButton": {
"message": "Liste leeren"
},
"options_empty": {
"message": "Keine Ausschlüsse gefunden"
},
"options_exclusionListTitle": {
"message": "Ausschlussliste"
},
"options_exportButton": {
"message": "Liste exportieren"
},
"options_filterPlaceholder": {
"message": "Drücken Sie ENTER, um nach dem Tippen zu filtern"
},
"options_importButton": {
"message": "Liste importieren"
},
"popup_bannerIssueOpen": {
"message": "Ein Problem wurde für diese Seite gemeldet. Die Erweiterung funktioniert möglicherweise vorübergehend nicht wie erwartet. Verfolgen Sie das Problem oder fügen Sie weitere Informationen hinzu."
},
"popup_bannerIssueWontFix": {
"message": "Ein Problem wurde für diese Seite gemeldet, wird jedoch nicht behoben. Bitte beachten Sie, dass die Erweiterung auf dieser Seite möglicherweise nicht wie erwartet funktioniert. Wir empfehlen dringend, die Erweiterung über die Schaltfläche unten zu deaktivieren."
},
"popup_bannerSupport": {
"message": "Diese Seite wird nicht mehr unterstützt. Für weitere Informationen lesen Sie bitte hier weiter"
},
"popup_bannerUpdateAvailable": {
"message": "Update verfügbar. Bitte installieren Sie die neueste Version, um bekannte Probleme zu beheben, bevor Sie neue melden"
},
"popup_contributeOption": {
"message": "Zu diesem Projekt beitragen"
},
"popup_databaseVersion": {
"message": "Datenbankversion"
},
"popup_extensionVersion": {
"message": "Erweiterungsversion"
},
"popup_helpOption": {
"message": "Hilfe oder Probleme?"
},
"popup_rateOption": {
"message": "Bewerten Sie diese Erweiterung"
},
"report_bodyText": {
"message": "Bitte teilen Sie in diesem Bericht keine persönlichen Informationen. Wenn Sie weitere Details hinzufügen möchten, gehen Sie im nächsten Schritt zum Problem und fügen Sie einen Kommentar hinzu."
},
"report_cancelButtonText": {
"message": "Abbrechen"
},
"report_reasonInputError": {
"message": "Bitte geben Sie einen Grund mit mindestens 10 und höchstens 1000 Zeichen ein"
},
"report_reasonInputLabel": {
"message": "Grund"
},
"report_reasonInputPlaceholder": {
"message": "Popup ist erschienen"
},
"report_submitErrorExtraText": {
"message": "Es scheint, dass bereits ein Problem geöffnet ist oder als 'wontfix' markiert wurde. Bitte öffnen Sie das Problem und fügen Sie gegebenenfalls weitere Informationen hinzu, indem Sie den folgenden Button verwenden."
},
"report_submitErrorText": {
"message": "Bericht nicht gesendet?"
},
"report_submitSuccessExtraText": {
"message": "Während wir daran arbeiten, können Sie diese Website zur Ausschlussliste in den Erweiterungseinstellungen hinzufügen oder einfach die Erweiterung für diese Website deaktivieren, indem Sie auf das Erweiterungssymbol in Ihrer Browser-Symbolleiste klicken."
},
"report_submitSuccessText": {
"message": "Bericht gesendet!"
},
"report_urlInputError": {
"message": "Bitte geben Sie eine gültige URL mit höchstens 1000 Zeichen ein"
},
"report_urlInputLabel": {
"message": "URL"
}
}

View File

@ -3,7 +3,7 @@
"message": "Did someone say cookie consent dialogs? 😋"
},
"contextMenu_issueOption": {
"message": "Follow this issue in GitHub"
"message": "Follow this issue"
},
"contextMenu_reportOption": {
"message": "Report this website"
@ -20,6 +20,9 @@
"options_clearButton": {
"message": "Clear list"
},
"options_empty": {
"message": "No exclusions found"
},
"options_exclusionListTitle": {
"message": "Exclusion list"
},
@ -32,6 +35,18 @@
"options_importButton": {
"message": "Import list"
},
"popup_bannerIssueOpen": {
"message": "An issue has been reported for this page. The extension may not work as expected temporarily. Follow the issue, or add more information"
},
"popup_bannerIssueWontFix": {
"message": "An issue has been reported for this page, but it won't be fixed. Please be aware that the extension may not function as expected on this page. We highly recommend turning off the extension using the button below"
},
"popup_bannerSupport": {
"message": "This site is no longer supported. For more information, please keep reading here"
},
"popup_bannerUpdateAvailable": {
"message": "Update available. Please install the latest version to address known issues before reporting new ones"
},
"popup_contributeOption": {
"message": "Contribute to this project"
},
@ -47,28 +62,37 @@
"popup_rateOption": {
"message": "Rate this extension"
},
"reportDialog_bodyText": {
"message": "Please, do not share any personal information in this report. If you want to add more details, open the GitHub issue in the next step and add a comment."
"report_bodyText": {
"message": "Please, do not share any personal information in this report. If you want to add more details, go to the issue in the next step and add a comment."
},
"reportDialog_reasonInputError": {
"report_cancelButtonText": {
"message": "Cancel"
},
"report_reasonInputError": {
"message": "Please enter a reason with at least 10 characters and no more than 1000 characters"
},
"reportDialog_reasonInputLabel": {
"report_reasonInputLabel": {
"message": "Reason"
},
"reportDialog_reasonInputPlaceholder": {
"report_reasonInputPlaceholder": {
"message": "Popup showed up"
},
"reportDialog_submitExtraText": {
"report_submitErrorExtraText": {
"message": "It seems that there's an issue open already, or it is marked as 'wontfix'. Please enter the issue if you want to add more information using the following button."
},
"report_submitErrorText": {
"message": "Report not sent?"
},
"report_submitSuccessExtraText": {
"message": "While we are working on this, you can add this website to the exclusion list in the extension settings or just disable the extension for this website clicking on the extension icon in your browser toolbar"
},
"reportDialog_submitText": {
"report_submitSuccessText": {
"message": "Report sent!"
},
"reportDialog_urlInputError": {
"report_urlInputError": {
"message": "Please enter a valid URL with no more than 1000 characters"
},
"reportDialog_urlInputLabel": {
"report_urlInputLabel": {
"message": "URL"
}
}

View File

@ -0,0 +1,98 @@
{
"appDesc": {
"message": "¿Alguien mencionó los diálogos de consentimiento de cookies? 😋"
},
"contextMenu_issueOption": {
"message": "Sigue este problema"
},
"contextMenu_reportOption": {
"message": "Reportar este sitio web"
},
"contextMenu_settingsOption": {
"message": "Gestionar lista de exclusión"
},
"options_addButton": {
"message": "Agregar exclusión"
},
"options_addPrompt": {
"message": "Ingrese dominio"
},
"options_clearButton": {
"message": "Borrar lista"
},
"options_empty": {
"message": "No se encontraron exclusiones"
},
"options_exclusionListTitle": {
"message": "Lista de exclusión"
},
"options_exportButton": {
"message": "Exportar lista"
},
"options_filterPlaceholder": {
"message": "Presiona ENTER para filtrar después de escribir"
},
"options_importButton": {
"message": "Importar lista"
},
"popup_bannerIssueOpen": {
"message": "Se ha informado de un problema en esta página. La extensión puede no funcionar como se espera temporalmente. Siga el problema o agregue más información."
},
"popup_bannerIssueWontFix": {
"message": "Se ha informado de un problema en esta página, pero no se solucionará. Tenga en cuenta que la extensión puede no funcionar como se espera en esta página. Recomendamos encarecidamente desactivar la extensión utilizando el botón a continuación."
},
"popup_bannerSupport": {
"message": "Este sitio ya no es compatible. Para más información, sigue leyendo aquí"
},
"popup_bannerUpdateAvailable": {
"message": "Actualización disponible. Por favor, instale la última versión para solucionar problemas conocidos antes de informar nuevos"
},
"popup_contributeOption": {
"message": "Contribuir a este proyecto"
},
"popup_databaseVersion": {
"message": "Versión de la base de datos"
},
"popup_extensionVersion": {
"message": "Versión de la extensión"
},
"popup_helpOption": {
"message": "¿Ayuda o problemas?"
},
"popup_rateOption": {
"message": "Califica esta extensión"
},
"report_bodyText": {
"message": "Por favor, no compartas información personal en este informe. Si deseas agregar más detalles, ve al problema en el siguiente paso y añade un comentario."
},
"report_cancelButtonText": {
"message": "Cancelar"
},
"report_reasonInputError": {
"message": "Por favor, ingrese una razón con al menos 10 caracteres y no más de 1000 caracteres"
},
"report_reasonInputLabel": {
"message": "Razón"
},
"report_reasonInputPlaceholder": {
"message": "Apareció un popup"
},
"report_submitErrorExtraText": {
"message": "Reporte no enviado?"
},
"report_submitErrorText": {
"message": "Parece que ya hay un problema abierto o está marcado como 'no se solucionará'. Por favor, ingrese al problema para agregar más información usando el siguiente botón."
},
"report_submitSuccessExtraText": {
"message": "Mientras trabajamos en esto, puedes agregar este sitio web a la lista de exclusiones en la configuración de la extensión o simplemente deshabilitar la extensión para este sitio web haciendo clic en el icono de la extensión en la barra de herramientas de tu navegador."
},
"report_submitSuccessText": {
"message": "Reporte enviado!"
},
"report_urlInputError": {
"message": "Por favor, ingrese una URL válida con no más de 1000 caracteres"
},
"report_urlInputLabel": {
"message": "URL"
}
}

View File

@ -3,7 +3,7 @@
"message": "Quelqu'un a parlé de boîtes de dialogue de consentement aux cookies ? 😋"
},
"contextMenu_issueOption": {
"message": "Suivre ce problème sur GitHub"
"message": "Suivre ce problème"
},
"contextMenu_reportOption": {
"message": "Signaler ce site web"
@ -20,6 +20,9 @@
"options_clearButton": {
"message": "Vider la liste"
},
"options_empty": {
"message": "Aucune exclusion trouvée"
},
"options_exclusionListTitle": {
"message": "Liste d'exclusion"
},
@ -32,6 +35,18 @@
"options_importButton": {
"message": "Importer la liste"
},
"popup_bannerIssueOpen": {
"message": "Un problème a été signalé pour cette page. L'extension peut ne pas fonctionner comme prévu temporairement. Suivez le problème ou ajoutez plus d'informations."
},
"popup_bannerIssueWontFix": {
"message": "Un problème a été signalé pour cette page, mais il ne sera pas résolu. Veuillez noter que l'extension pourrait ne pas fonctionner comme prévu sur cette page. Nous vous recommandons vivement de désactiver l'extension en utilisant le bouton ci-dessous."
},
"popup_bannerSupport": {
"message": "Ce site n'est plus pris en charge. Pour plus d'informations, veuillez continuer à lire ici"
},
"popup_bannerUpdateAvailable": {
"message": "Mise à jour disponible. Veuillez installer la dernière version pour résoudre les problèmes connus avant d'en signaler de nouveaux"
},
"popup_contributeOption": {
"message": "Contribuer à ce projet"
},
@ -47,28 +62,37 @@
"popup_rateOption": {
"message": "Évaluer cette extension"
},
"reportDialog_bodyText": {
"message": "Veuillez ne pas partager d'informations personnelles dans ce rapport. Si vous souhaitez ajouter plus de détails, ouvrez le problème GitHub à l'étape suivante et ajoutez un commentaire."
"report_bodyText": {
"message": "Veuillez ne partager aucune information personnelle dans ce rapport. Si vous souhaitez ajouter plus de détails, allez à l'issue à l'étape suivante et ajoutez un commentaire."
},
"reportDialog_reasonInputError": {
"report_cancelButtonText": {
"message": "Annuler"
},
"report_reasonInputError": {
"message": "Veuillez entrer une raison d'au moins 10 caractères et de pas plus de 1000 caractères"
},
"reportDialog_reasonInputLabel": {
"report_reasonInputLabel": {
"message": "Raison"
},
"reportDialog_reasonInputPlaceholder": {
"report_reasonInputPlaceholder": {
"message": "Le popup est apparu"
},
"reportDialog_submitExtraText": {
"report_submitErrorExtraText": {
"message": "Il semble qu'un problème soit déjà ouvert ou marqué comme 'ne sera pas corrigé'. Veuillez entrer dans le problème pour ajouter plus d'informations en utilisant le bouton suivant."
},
"report_submitErrorText": {
"message": "Rapport non envoyé?"
},
"report_submitSuccessExtraText": {
"message": "Pendant que nous travaillons sur ce point, vous pouvez ajouter ce site web à la liste d'exclusion dans les paramètres de l'extension ou simplement désactiver l'extension pour ce site web en cliquant sur l'icône de l'extension dans la barre d'outils de votre navigateur."
},
"reportDialog_submitText": {
"report_submitSuccessText": {
"message": "Rapport envoyé!"
},
"reportDialog_urlInputError": {
"report_urlInputError": {
"message": "Veuillez entrer une URL valide de pas plus de 1000 caractères"
},
"reportDialog_urlInputLabel": {
"report_urlInputLabel": {
"message": "URL"
}
}

View File

@ -0,0 +1,98 @@
{
"appDesc": {
"message": "क्या किसी ने कुकी सहमति संवाद का उल्लेख किया? 😋"
},
"contextMenu_issueOption": {
"message": "इस समस्या का पालन करें"
},
"contextMenu_reportOption": {
"message": "इस वेबसाइट की रिपोर्ट करें"
},
"contextMenu_settingsOption": {
"message": "बहिष्करण सूची प्रबंधित करें"
},
"options_addButton": {
"message": "बहिष्करण जोड़ें"
},
"options_addPrompt": {
"message": "डोमेन दर्ज करें"
},
"options_clearButton": {
"message": "सूची साफ़ करें"
},
"options_empty": {
"message": "कोई बहिष्कार नहीं मिला।"
},
"options_exclusionListTitle": {
"message": "बहिष्करण सूची"
},
"options_exportButton": {
"message": "सूची निर्यात करें"
},
"options_filterPlaceholder": {
"message": "टाइप करने के बाद ENTER दबाएं फ़िल्टर करने के लिए"
},
"options_importButton": {
"message": "सूची आयात करें"
},
"popup_bannerIssueOpen": {
"message": "इस पृष्ठ के लिए एक समस्या दर्ज की गई है। अस्थायी रूप से एक्सटेंशन ठीक से काम नहीं कर सकता है। समस्या का पालन करें या अधिक जानकारी जोड़ें"
},
"popup_bannerIssueWontFix": {
"message": "इस पृष्ठ के लिए एक समस्या दर्ज की गई है, लेकिन इसे ठीक नहीं किया जाएगा। कृपया ध्यान दें कि एक्सटेंशन इस पृष्ठ पर अपेक्षित रूप से कार्य नहीं कर सकता है। हम नीचे दिए गए बटन से एक्सटेंशन को बंद करने की सलाह देते हैं"
},
"popup_bannerSupport": {
"message": "इस साइट को अब समर्थन प्राप्त नहीं है। अधिक जानकारी के लिए कृपया यहाँ पढ़ें"
},
"popup_bannerUpdateAvailable": {
"message": "अपडेट उपलब्ध है। कृपया नई समस्याओं की रिपोर्ट करने से पहले ज्ञात मुद्दों को हल करने के लिए नवीनतम संस्करण इंस्टॉल करें"
},
"popup_contributeOption": {
"message": "इस प्रोजेक्ट में योगदान करें"
},
"popup_databaseVersion": {
"message": "डेटाबेस संस्करण"
},
"popup_extensionVersion": {
"message": "एक्सटेंशन संस्करण"
},
"popup_helpOption": {
"message": "मदद या समस्याएँ?"
},
"popup_rateOption": {
"message": "इस एक्सटेंशन को रेट करें"
},
"report_bodyText": {
"message": "कृपया इस रिपोर्ट में कोई भी व्यक्तिगत जानकारी साझा न करें। यदि आप अधिक विवरण जोड़ना चाहते हैं, तो अगले चरण में समस्या पर जाएं और एक टिप्पणी जोड़ें।"
},
"report_cancelButtonText": {
"message": "रद्द करें"
},
"report_reasonInputError": {
"message": "कृपया कम से कम 10 अक्षरों और 1000 से अधिक नहीं अक्षरों के साथ एक कारण दर्ज करें"
},
"report_reasonInputLabel": {
"message": "कारण"
},
"report_reasonInputPlaceholder": {
"message": "पॉपअप दिखाई दिया"
},
"report_submitErrorExtraText": {
"message": "ऐसा लगता है कि पहले से ही एक मुद्दा खुला है, या इसे 'फिक्स नहीं होगा' के रूप में चिह्नित किया गया है। कृपया अधिक जानकारी जोड़ने के लिए अगले बटन का उपयोग करके समस्या में जाएं।"
},
"report_submitErrorText": {
"message": "रिपोर्ट नहीं भेजी गई?"
},
"report_submitSuccessExtraText": {
"message": "जब तक हम इस पर काम कर रहे हैं, तब तक आप इस वेबसाइट को एक्सटेंशन सेटिंग में बहिष्करण सूची में जोड़ सकते हैं या अपने ब्राउज़र टूलबार में एक्सटेंशन आइकन पर क्लिक करके इस वेबसाइट के लिए एक्सटेंशन को अक्षम कर सकते हैं"
},
"report_submitSuccessText": {
"message": "रिपोर्ट भेज दी गई!"
},
"report_urlInputError": {
"message": "कृपया अधिकतम 1000 अक्षरों के साथ एक मान्य URL दर्ज करें"
},
"report_urlInputLabel": {
"message": "URL"
}
}

View File

@ -0,0 +1,98 @@
{
"appDesc": {
"message": "Ada yang bilang dialog persetujuan cookie? 😋"
},
"contextMenu_issueOption": {
"message": "Ikuti masalah ini"
},
"contextMenu_reportOption": {
"message": "Laporkan situs ini"
},
"contextMenu_settingsOption": {
"message": "Kelola daftar pengecualian"
},
"options_addButton": {
"message": "Tambahkan pengecualian"
},
"options_addPrompt": {
"message": "Masukkan domain"
},
"options_clearButton": {
"message": "Bersihkan daftar"
},
"options_empty": {
"message": "Tidak ada pengecualian ditemukan"
},
"options_exclusionListTitle": {
"message": "Daftar pengecualian"
},
"options_exportButton": {
"message": "Ekspor daftar"
},
"options_filterPlaceholder": {
"message": "Tekan ENTER untuk memfilter setelah mengetik"
},
"options_importButton": {
"message": "Impor daftar"
},
"popup_bannerIssueOpen": {
"message": "Ada masalah yang dilaporkan untuk halaman ini. Ekstensi mungkin tidak berfungsi seperti yang diharapkan untuk sementara. Ikuti masalahnya atau tambahkan informasi lainnya"
},
"popup_bannerIssueWontFix": {
"message": "Ada masalah yang dilaporkan untuk halaman ini, tetapi tidak akan diperbaiki. Harap diperhatikan bahwa ekstensi mungkin tidak berfungsi seperti yang diharapkan di halaman ini. Kami sangat menyarankan untuk mematikan ekstensi menggunakan tombol di bawah ini"
},
"popup_bannerSupport": {
"message": "Situs ini tidak lagi didukung. Untuk informasi lebih lanjut, silakan baca di sini"
},
"popup_bannerUpdateAvailable": {
"message": "Pembaruan tersedia. Harap instal versi terbaru untuk menyelesaikan masalah yang diketahui sebelum melaporkan masalah baru"
},
"popup_contributeOption": {
"message": "Berkontribusi pada proyek ini"
},
"popup_databaseVersion": {
"message": "Versi basis data"
},
"popup_extensionVersion": {
"message": "Versi ekstensi"
},
"popup_helpOption": {
"message": "Butuh bantuan atau ada masalah?"
},
"popup_rateOption": {
"message": "Beri peringkat pada ekstensi ini"
},
"report_bodyText": {
"message": "Jangan membagikan informasi pribadi apa pun dalam laporan ini. Jika Anda ingin menambahkan lebih banyak detail, buka masalah pada langkah berikutnya dan tambahkan komentar."
},
"report_cancelButtonText": {
"message": "Batal"
},
"report_reasonInputError": {
"message": "Harap masukkan alasan yang terdiri dari minimal 10 karakter dan maksimal 1000 karakter"
},
"report_reasonInputLabel": {
"message": "Alasan"
},
"report_reasonInputPlaceholder": {
"message": "Muncul pop-up"
},
"report_submitErrorExtraText": {
"message": "Tampaknya sudah ada masalah yang terbuka atau ditandai sebagai 'tidak akan diperbaiki'. Silakan buka masalah menggunakan tombol di bawah ini untuk menambahkan lebih banyak informasi."
},
"report_submitErrorText": {
"message": "Laporan tidak terkirim?"
},
"report_submitSuccessExtraText": {
"message": "Sambil kami menangani masalah ini, Anda dapat menambahkan situs web ini ke daftar pengecualian di pengaturan ekstensi atau menonaktifkan ekstensi untuk situs ini dengan mengklik ikon ekstensi di bilah alat peramban Anda"
},
"report_submitSuccessText": {
"message": "Laporan terkirim!"
},
"report_urlInputError": {
"message": "Masukkan URL yang valid dengan maksimal 1000 karakter"
},
"report_urlInputLabel": {
"message": "URL"
}
}

View File

@ -3,7 +3,7 @@
"message": "Qualcuno ha parlato di dialoghi di consenso sui cookie? 😋"
},
"contextMenu_issueOption": {
"message": "Segui questo problema su GitHub"
"message": "Segui questo problema"
},
"contextMenu_reportOption": {
"message": "Segnala questo sito web"
@ -20,6 +20,9 @@
"options_clearButton": {
"message": "Cancella lista"
},
"options_empty": {
"message": "Nessuna esclusione trovata"
},
"options_exclusionListTitle": {
"message": "Lista di esclusione"
},
@ -32,6 +35,18 @@
"options_importButton": {
"message": "Importa lista"
},
"popup_bannerIssueOpen": {
"message": "È stato segnalato un problema per questa pagina. L'estensione potrebbe non funzionare temporaneamente come previsto. Segui il problema o aggiungi ulteriori informazioni."
},
"popup_bannerIssueWontFix": {
"message": "È stato segnalato un problema per questa pagina, ma non verrà risolto. Si prega di notare che l'estensione potrebbe non funzionare come previsto su questa pagina. Si consiglia vivamente di disattivare l'estensione utilizzando il pulsante qui sotto."
},
"popup_bannerSupport": {
"message": "Questo sito non è più supportato. Per ulteriori informazioni, continua a leggere qui"
},
"popup_bannerUpdateAvailable": {
"message": "Aggiornamento disponibile. Si prega di installare l'ultima versione per risolvere i problemi noti prima di segnalarne di nuovi"
},
"popup_contributeOption": {
"message": "Contribuisci a questo progetto"
},
@ -47,28 +62,37 @@
"popup_rateOption": {
"message": "Valuta questa estensione"
},
"reportDialog_bodyText": {
"message": "Per favore, non condividere informazioni personali in questo rapporto. Se vuoi aggiungere più dettagli, apri il problema su GitHub nel passaggio successivo e aggiungi un commento."
"report_bodyText": {
"message": "Per favore, non condividere informazioni personali in questo rapporto. Se desideri aggiungere ulteriori dettagli, vai al problema nel prossimo passaggio e aggiungi un commento."
},
"reportDialog_reasonInputError": {
"report_cancelButtonText": {
"message": "Annulla"
},
"report_reasonInputError": {
"message": "Inserisci una motivazione con almeno 10 caratteri e non più di 1000 caratteri"
},
"reportDialog_reasonInputLabel": {
"report_reasonInputLabel": {
"message": "Motivo"
},
"reportDialog_reasonInputPlaceholder": {
"report_reasonInputPlaceholder": {
"message": "Popup apparso"
},
"reportDialog_submitExtraText": {
"report_submitErrorExtraText": {
"message": "Sembra che ci sia già un problema aperto o contrassegnato come 'non risolvibile'. Per favore, entra nel problema per aggiungere ulteriori informazioni usando il pulsante seguente."
},
"report_submitErrorText": {
"message": "Segnalazione non inviata?"
},
"report_submitSuccessExtraText": {
"message": "Mentre lavoriamo su questo, puoi aggiungere questo sito web alla lista di esclusione nelle impostazioni dell'estensione o semplicemente disattivare l'estensione per questo sito web cliccando sull'icona dell'estensione nella barra degli strumenti del browser."
},
"reportDialog_submitText": {
"report_submitSuccessText": {
"message": "Rapporto inviato!"
},
"reportDialog_urlInputError": {
"report_urlInputError": {
"message": "Inserisci un URL valido con non più di 1000 caratteri"
},
"reportDialog_urlInputLabel": {
"report_urlInputLabel": {
"message": "URL"
}
}

View File

@ -0,0 +1,98 @@
{
"appDesc": {
"message": "クッキー同意ダイアログの話をしている? 😋"
},
"contextMenu_issueOption": {
"message": "この問題をフォローする"
},
"contextMenu_reportOption": {
"message": "このウェブサイトを報告する"
},
"contextMenu_settingsOption": {
"message": "除外リストを管理する"
},
"options_addButton": {
"message": "除外を追加"
},
"options_addPrompt": {
"message": "ドメインを入力してください"
},
"options_clearButton": {
"message": "リストをクリア"
},
"options_empty": {
"message": "除外項目が見つかりませんでした"
},
"options_exclusionListTitle": {
"message": "除外リスト"
},
"options_exportButton": {
"message": "リストをエクスポート"
},
"options_filterPlaceholder": {
"message": "入力後にENTERキーを押してフィルタリング"
},
"options_importButton": {
"message": "リストをインポート"
},
"popup_bannerIssueOpen": {
"message": "このページで問題が報告されました。拡張機能が一時的に正常に動作しない可能性があります。問題をフォローするか、追加情報を提供してください"
},
"popup_bannerIssueWontFix": {
"message": "このページで問題が報告されましたが、修正されません。このページで拡張機能が期待通りに機能しない可能性があります。以下のボタンで拡張機能をオフにすることをお勧めします"
},
"popup_bannerSupport": {
"message": "このサイトはもうサポートされていません。詳しくは、ここをお読みください"
},
"popup_bannerUpdateAvailable": {
"message": "アップデートが利用可能です。新しい問題を報告する前に、既知の問題を解決するため最新バージョンをインストールしてください"
},
"popup_contributeOption": {
"message": "このプロジェクトに貢献する"
},
"popup_databaseVersion": {
"message": "データベースバージョン"
},
"popup_extensionVersion": {
"message": "拡張機能のバージョン"
},
"popup_helpOption": {
"message": "ヘルプまたは問題?"
},
"popup_rateOption": {
"message": "この拡張機能を評価する"
},
"report_bodyText": {
"message": "このレポートには個人情報を含めないでください。追加の詳細を提供する場合は、次のステップで問題に移動し、コメントを追加してください。"
},
"report_cancelButtonText": {
"message": "キャンセル"
},
"report_reasonInputError": {
"message": "10文字以上、1000文字以下の理由を入力してください"
},
"report_reasonInputLabel": {
"message": "理由"
},
"report_reasonInputPlaceholder": {
"message": "ポップアップが表示されました"
},
"report_submitErrorExtraText": {
"message": "既に問題が開かれているか、「修正しない」とマークされています。追加情報を提供する場合は、次のボタンから問題に移動してください。"
},
"report_submitErrorText": {
"message": "レポートが送信されませんでしたか?"
},
"report_submitSuccessExtraText": {
"message": "処理が完了するまでの間、このウェブサイトを拡張機能の設定で除外リストに追加するか、ブラウザツールバーの拡張アイコンをクリックしてこのウェブサイトで拡張機能を無効にしてください"
},
"report_submitSuccessText": {
"message": "レポートが送信されました!"
},
"report_urlInputError": {
"message": "1000文字以下の有効なURLを入力してください"
},
"report_urlInputLabel": {
"message": "URL"
}
}

View File

@ -0,0 +1,98 @@
{
"appDesc": {
"message": "쿠키 동의 대화 상자를 말한 사람이 있나요? 😋"
},
"contextMenu_issueOption": {
"message": "이 문제를 팔로우하기"
},
"contextMenu_reportOption": {
"message": "이 웹사이트 신고하기"
},
"contextMenu_settingsOption": {
"message": "제외 목록 관리하기"
},
"options_addButton": {
"message": "제외 추가"
},
"options_addPrompt": {
"message": "도메인 입력"
},
"options_clearButton": {
"message": "목록 지우기"
},
"options_empty": {
"message": "제외 항목을 찾을 수 없습니다"
},
"options_exclusionListTitle": {
"message": "제외 목록"
},
"options_exportButton": {
"message": "목록 내보내기"
},
"options_filterPlaceholder": {
"message": "입력 후 ENTER를 눌러 필터링"
},
"options_importButton": {
"message": "목록 가져오기"
},
"popup_bannerIssueOpen": {
"message": "이 페이지에 대한 문제가 보고되었습니다. 확장이 일시적으로 예상대로 작동하지 않을 수 있습니다. 문제를 팔로우하거나 추가 정보를 제공하십시오"
},
"popup_bannerIssueWontFix": {
"message": "이 페이지에 대한 문제가 보고되었지만 수정되지 않습니다. 이 페이지에서 확장이 예상대로 작동하지 않을 수 있습니다. 아래 버튼을 사용하여 확장을 끄는 것이 좋습니다"
},
"popup_bannerSupport": {
"message": "이 사이트는 더 이상 지원되지 않습니다. 자세한 내용은 여기를 계속 읽어 주세요"
},
"popup_bannerUpdateAvailable": {
"message": "업데이트 가능. 새 문제를 보고하기 전에 알려진 문제를 해결하려면 최신 버전을 설치하십시오"
},
"popup_contributeOption": {
"message": "이 프로젝트에 기여하기"
},
"popup_databaseVersion": {
"message": "데이터베이스 버전"
},
"popup_extensionVersion": {
"message": "확장 버전"
},
"popup_helpOption": {
"message": "도움말 또는 문제?"
},
"popup_rateOption": {
"message": "이 확장 평가하기"
},
"report_bodyText": {
"message": "이 보고서에 개인 정보를 공유하지 마십시오. 더 자세한 내용을 추가하려면 다음 단계에서 문제로 이동하여 댓글을 추가하십시오."
},
"report_cancelButtonText": {
"message": "취소"
},
"report_reasonInputError": {
"message": "10자 이상, 1000자 이하의 이유를 입력하십시오"
},
"report_reasonInputLabel": {
"message": "이유"
},
"report_reasonInputPlaceholder": {
"message": "팝업이 나타났습니다"
},
"report_submitErrorExtraText": {
"message": "이미 열린 문제가 있거나 '수정하지 않음'으로 표시된 것 같습니다. 추가 정보를 추가하려면 다음 버튼을 사용하여 문제로 이동하십시오."
},
"report_submitErrorText": {
"message": "보고서가 전송되지 않았습니까?"
},
"report_submitSuccessExtraText": {
"message": "이 문제를 처리하는 동안 확장 설정에서 제외 목록에 이 웹사이트를 추가하거나 브라우저 툴바의 확장 아이콘을 클릭하여 이 웹사이트에서 확장을 비활성화할 수 있습니다"
},
"report_submitSuccessText": {
"message": "보고서가 전송되었습니다!"
},
"report_urlInputError": {
"message": "유효한 URL을 1000자 이하로 입력하십시오"
},
"report_urlInputLabel": {
"message": "URL"
}
}

View File

@ -3,7 +3,7 @@
"message": "Czy ktoś wspomniał o dialogach zgody na pliki cookie? 😋"
},
"contextMenu_issueOption": {
"message": "Śledź ten problem na GitHub"
"message": "Śledź ten problem"
},
"contextMenu_reportOption": {
"message": "Zgłoś tę stronę"
@ -20,6 +20,9 @@
"options_clearButton": {
"message": "Wyczyść listę"
},
"options_empty": {
"message": "Nie znaleziono wykluczeń"
},
"options_exclusionListTitle": {
"message": "Lista wykluczeń"
},
@ -32,6 +35,18 @@
"options_importButton": {
"message": "Importuj listę"
},
"popup_bannerIssueOpen": {
"message": "Zgłoszono problem z tą stroną. Rozszerzenie może tymczasowo nie działać zgodnie z oczekiwaniami. Śledź problem lub dodaj więcej informacji."
},
"popup_bannerIssueWontFix": {
"message": "Zgłoszono problem z tą stroną, ale nie zostanie on naprawiony. Należy pamiętać, że rozszerzenie może nie działać zgodnie z oczekiwaniami na tej stronie. Zdecydowanie zalecamy wyłączenie rozszerzenia za pomocą poniższego przycisku."
},
"popup_bannerSupport": {
"message": "Ta strona nie jest już obsługiwana. Aby uzyskać więcej informacji, proszę czytać dalej tutaj"
},
"popup_bannerUpdateAvailable": {
"message": "Dostępna aktualizacja. Zainstaluj najnowszą wersję, aby rozwiązać znane problemy przed zgłoszeniem nowych"
},
"popup_contributeOption": {
"message": "Wesprzyj ten projekt"
},
@ -47,28 +62,37 @@
"popup_rateOption": {
"message": "Oceń to rozszerzenie"
},
"reportDialog_bodyText": {
"message": "Prosimy, nie udostępniaj żadnych danych osobowych w tym raporcie. Jeśli chcesz dodać więcej szczegółów, otwórz zgłoszenie na GitHub w następnym kroku i dodaj komentarz."
"report_bodyText": {
"message": "Pros, nie udostępniaj żadnych danych osobowych w tym raporcie. Jeśli chcesz dodać więcej szczegółów, przejdź do zgłoszenia w kolejnym kroku i dodaj komentarz."
},
"reportDialog_reasonInputError": {
"report_cancelButtonText": {
"message": "Anuluj"
},
"report_reasonInputError": {
"message": "Proszę podać powód zawierający co najmniej 10 znaków i nie więcej niż 1000 znaków"
},
"reportDialog_reasonInputLabel": {
"report_reasonInputLabel": {
"message": "Powód"
},
"reportDialog_reasonInputPlaceholder": {
"report_reasonInputPlaceholder": {
"message": "Pojawił się popup"
},
"reportDialog_submitExtraText": {
"report_submitErrorExtraText": {
"message": "Wygląda na to, że istnieje już otwarte zgłoszenie lub zostało oznaczone jako 'nie do rozwiązania'. Wprowadź zgłoszenie, aby dodać więcej informacji, używając poniższego przycisku."
},
"report_submitErrorText": {
"message": "Raport nie został wysłany?"
},
"report_submitSuccessExtraText": {
"message": "Podczas gdy nad tym pracujemy, możesz dodać tę stronę do listy wykluczeń w ustawieniach rozszerzenia lub po prostu wyłączyć rozszerzenie dla tej strony, klikając ikonę rozszerzenia na pasku narzędzi przeglądarki."
},
"reportDialog_submitText": {
"report_submitSuccessText": {
"message": "Zgłoszenie wysłane!"
},
"reportDialog_urlInputError": {
"report_urlInputError": {
"message": "Proszę wprowadzić prawidłowy URL zawierający nie więcej niż 1000 znaków"
},
"reportDialog_urlInputLabel": {
"report_urlInputLabel": {
"message": "URL"
}
}

View File

@ -3,7 +3,7 @@
"message": "Alguém falou sobre diálogos de consentimento de cookies? 😋"
},
"contextMenu_issueOption": {
"message": "Siga este problema no GitHub"
"message": "Siga este problema"
},
"contextMenu_reportOption": {
"message": "Denunciar este site"
@ -20,6 +20,9 @@
"options_clearButton": {
"message": "Limpar lista"
},
"options_empty": {
"message": "Nenhuma exclusão encontrada"
},
"options_exclusionListTitle": {
"message": "Lista de exclusão"
},
@ -32,6 +35,18 @@
"options_importButton": {
"message": "Importar lista"
},
"popup_bannerIssueOpen": {
"message": "Um problema foi relatado para esta página. A extensão pode não funcionar conforme o esperado temporariamente. Acompanhe o problema ou adicione mais informações."
},
"popup_bannerIssueWontFix": {
"message": "Um problema foi relatado para esta página, mas não será corrigido. Esteja ciente de que a extensão pode não funcionar conforme o esperado nesta página. Recomendamos desativar a extensão usando o botão abaixo."
},
"popup_bannerSupport": {
"message": "Este site não é mais compatível. Para mais informações, continue lendo aqui"
},
"popup_bannerUpdateAvailable": {
"message": "Atualização disponível. Por favor, instale a versão mais recente para corrigir problemas conhecidos antes de relatar novos"
},
"popup_contributeOption": {
"message": "Contribuir para este projeto"
},
@ -47,28 +62,37 @@
"popup_rateOption": {
"message": "Avalie esta extensão"
},
"reportDialog_bodyText": {
"message": "Por favor, não compartilhe nenhuma informação pessoal neste relatório. Se você quiser adicionar mais detalhes, abra o problema no GitHub na próxima etapa e adicione um comentário."
"report_bodyText": {
"message": "Por favor, não compartilhe informações pessoais neste relatório. Se quiser adicionar mais detalhes, vá até o problema no próximo passo e adicione um comentário."
},
"reportDialog_reasonInputError": {
"report_cancelButtonText": {
"message": "Cancelar"
},
"report_reasonInputError": {
"message": "Por favor, insira um motivo com pelo menos 10 caracteres e no máximo 1000 caracteres"
},
"reportDialog_reasonInputLabel": {
"report_reasonInputLabel": {
"message": "Motivo"
},
"reportDialog_reasonInputPlaceholder": {
"report_reasonInputPlaceholder": {
"message": "Popup apareceu"
},
"reportDialog_submitExtraText": {
"report_submitErrorExtraText": {
"message": "Parece que já existe um problema aberto ou marcado como 'não corrigido'. Por favor, acesse o problema para adicionar mais informações usando o botão a seguir."
},
"report_submitErrorText": {
"message": "Relatório não enviado?"
},
"report_submitSuccessExtraText": {
"message": "Enquanto estamos trabalhando nisso, você pode adicionar este site à lista de exclusão nas configurações da extensão ou simplesmente desativar a extensão para este site clicando no ícone da extensão na barra de ferramentas do navegador."
},
"reportDialog_submitText": {
"report_submitSuccessText": {
"message": "Relatório enviado!"
},
"reportDialog_urlInputError": {
"report_urlInputError": {
"message": "Por favor, insira uma URL válida com no máximo 1000 caracteres"
},
"reportDialog_urlInputLabel": {
"report_urlInputLabel": {
"message": "URL"
}
}

View File

@ -3,7 +3,7 @@
"message": "Alguém mencionou diálogos de consentimento de cookies? 😋"
},
"contextMenu_issueOption": {
"message": "Siga este problema no GitHub"
"message": "Siga este problema"
},
"contextMenu_reportOption": {
"message": "Reportar este site"
@ -20,6 +20,9 @@
"options_clearButton": {
"message": "Limpar lista"
},
"options_empty": {
"message": "Nenhuma exclusão encontrada"
},
"options_exclusionListTitle": {
"message": "Lista de exclusão"
},
@ -32,6 +35,18 @@
"options_importButton": {
"message": "Importar lista"
},
"popup_bannerIssueOpen": {
"message": "Foi relatado um problema para esta página. A extensão pode não funcionar conforme o esperado temporariamente. Siga o problema ou adicione mais informações."
},
"popup_bannerIssueWontFix": {
"message": "Foi relatado um problema para esta página, mas não será resolvido. Esteja ciente de que a extensão pode não funcionar conforme o esperado nesta página. Recomendamos desativar a extensão usando o botão abaixo."
},
"popup_bannerSupport": {
"message": "Este site já não é suportado. Para mais informações, continue a ler aqui"
},
"popup_bannerUpdateAvailable": {
"message": "Atualização disponível. Por favor, instale a versão mais recente para resolver problemas conhecidos antes de relatar novos"
},
"popup_contributeOption": {
"message": "Contribuir para este projeto"
},
@ -47,28 +62,37 @@
"popup_rateOption": {
"message": "Avalie esta extensão"
},
"reportDialog_bodyText": {
"message": "Por favor, não partilhe nenhuma informação pessoal neste relatório. Se quiser adicionar mais detalhes, abra o problema no GitHub na próxima etapa e adicione um comentário."
"report_bodyText": {
"message": "Por favor, não partilhe informações pessoais neste relatório. Se quiser adicionar mais detalhes, vá até ao problema no próximo passo e adicione um comentário."
},
"reportDialog_reasonInputError": {
"report_cancelButtonText": {
"message": "Cancelar"
},
"report_reasonInputError": {
"message": "Por favor, insira um motivo com pelo menos 10 caracteres e no máximo 1000 caracteres"
},
"reportDialog_reasonInputLabel": {
"report_reasonInputLabel": {
"message": "Motivo"
},
"reportDialog_reasonInputPlaceholder": {
"report_reasonInputPlaceholder": {
"message": "Popup apareceu"
},
"reportDialog_submitExtraText": {
"report_submitErrorExtraText": {
"message": "Parece que já existe um problema aberto ou marcado como 'não corrigido'. Por favor, aceda ao problema para adicionar mais informações usando o botão seguinte."
},
"report_submitErrorText": {
"message": "Relatório não enviado?"
},
"report_submitSuccessExtraText": {
"message": "Enquanto trabalhamos nisso, pode adicionar este site à lista de exclusão nas definições da extensão ou simplesmente desativar a extensão para este site clicando no ícone da extensão na barra de ferramentas do navegador."
},
"reportDialog_submitText": {
"report_submitSuccessText": {
"message": "Relatório enviado!"
},
"reportDialog_urlInputError": {
"report_urlInputError": {
"message": "Por favor, insira uma URL válida com no máximo 1000 caracteres"
},
"reportDialog_urlInputLabel": {
"report_urlInputLabel": {
"message": "URL"
}
}

View File

@ -0,0 +1,98 @@
{
"appDesc": {
"message": "A menționat cineva dialogurile de consimțământ pentru cookie-uri? 😋"
},
"contextMenu_issueOption": {
"message": "Urmărește această problemă"
},
"contextMenu_reportOption": {
"message": "Raportează acest site web"
},
"contextMenu_settingsOption": {
"message": "Gestionează lista de excludere"
},
"options_addButton": {
"message": "Adaugă excludere"
},
"options_addPrompt": {
"message": "Introdu domeniul"
},
"options_clearButton": {
"message": "Golește lista"
},
"options_empty": {
"message": "Nu au fost găsite excluderi"
},
"options_exclusionListTitle": {
"message": "Lista de excludere"
},
"options_exportButton": {
"message": "Exportă lista"
},
"options_filterPlaceholder": {
"message": "Apasă ENTER pentru a filtra după tastare"
},
"options_importButton": {
"message": "Importă lista"
},
"popup_bannerIssueOpen": {
"message": "A fost raportată o problemă pentru această pagină. Extensia poate să nu funcționeze temporar conform așteptărilor. Urmăriți problema sau adăugați mai multe informații."
},
"popup_bannerIssueWontFix": {
"message": "A fost raportată o problemă pentru această pagină, dar nu va fi rezolvată. Rețineți că extensia poate să nu funcționeze conform așteptărilor pe această pagină. Vă recomandăm cu insistență să dezactivați extensia folosind butonul de mai jos."
},
"popup_bannerSupport": {
"message": "Acest site nu mai este acceptat. Pentru mai multe informații, continuați să citiți aici"
},
"popup_bannerUpdateAvailable": {
"message": "Actualizare disponibilă. Vă rugăm să instalați cea mai recentă versiune pentru a rezolva problemele cunoscute înainte de a raporta altele noi."
},
"popup_contributeOption": {
"message": "Contribuie la acest proiect"
},
"popup_databaseVersion": {
"message": "Versiunea bazei de date"
},
"popup_extensionVersion": {
"message": "Versiunea extensiei"
},
"popup_helpOption": {
"message": "Ajutor sau probleme?"
},
"popup_rateOption": {
"message": "Evaluează această extensie"
},
"report_bodyText": {
"message": "Vă rugăm să nu împărtășiți informații personale în acest raport. Dacă doriți să adăugați mai multe detalii, accesați problema în pasul următor și adăugați un comentariu."
},
"report_cancelButtonText": {
"message": "Anulează"
},
"report_reasonInputError": {
"message": "Vă rugăm să introduceți un motiv cu cel puțin 10 caractere și maximum 1000 de caractere"
},
"report_reasonInputLabel": {
"message": "Motiv"
},
"report_reasonInputPlaceholder": {
"message": "A apărut un popup"
},
"report_submitErrorExtraText": {
"message": "Se pare că există deja o problemă deschisă sau marcată ca 'nu va fi rezolvată'. Vă rugăm să intrați în problemă pentru a adăuga mai multe informații folosind butonul de mai jos."
},
"report_submitErrorText": {
"message": "Raportul nu a fost trimis?"
},
"report_submitSuccessExtraText": {
"message": "În timp ce lucrăm la acest aspect, poți adăuga acest site web la lista de excludere din setările extensiei sau poți dezactiva extensia pentru acest site web făcând clic pe pictograma extensiei din bara de instrumente a browserului."
},
"report_submitSuccessText": {
"message": "Raport trimis!"
},
"report_urlInputError": {
"message": "Vă rugăm să introduceți o adresă URL validă cu maximum 1000 de caractere"
},
"report_urlInputLabel": {
"message": "URL"
}
}

View File

@ -3,7 +3,7 @@
"message": "Кто-то сказал диалоги согласия на использование cookie? 😋"
},
"contextMenu_issueOption": {
"message": "Следить за этим вопросом на GitHub"
"message": "Следить за этой проблемой"
},
"contextMenu_reportOption": {
"message": "Сообщить об этом сайте"
@ -20,6 +20,9 @@
"options_clearButton": {
"message": "Очистить список"
},
"options_empty": {
"message": "Исключения не найдены"
},
"options_exclusionListTitle": {
"message": "Список исключений"
},
@ -32,6 +35,18 @@
"options_importButton": {
"message": "Импорт списка"
},
"popup_bannerIssueOpen": {
"message": "Для этой страницы было сообщено о проблеме. Расширение может временно не работать должным образом. Следите за проблемой или добавьте дополнительную информацию."
},
"popup_bannerIssueWontFix": {
"message": "Для этой страницы было сообщено о проблеме, но она не будет исправлена. Обратите внимание, что расширение может работать некорректно на этой странице. Мы настоятельно рекомендуем отключить расширение, используя кнопку ниже."
},
"popup_bannerSupport": {
"message": "Этот сайт больше не поддерживается. Для получения дополнительной информации продолжайте читать здесь"
},
"popup_bannerUpdateAvailable": {
"message": "Доступно обновление. Установите последнюю версию, чтобы устранить известные проблемы перед отправкой новых отчётов."
},
"popup_contributeOption": {
"message": "Содействовать этому проекту"
},
@ -47,28 +62,37 @@
"popup_rateOption": {
"message": "Оцените это расширение"
},
"reportDialog_bodyText": {
"message": "Пожалуйста, не делитесь личной информацией в этом отчете. Если вы хотите добавить больше деталей, откройте вопрос на GitHub на следующем шаге и добавьте комментарий."
"report_bodyText": {
"message": "Пожалуйста, не делитесь личной информацией в этом отчете. Если вы хотите добавить больше деталей, перейдите к проблеме на следующем этапе и добавьте комментарий."
},
"reportDialog_reasonInputError": {
"report_cancelButtonText": {
"message": "Отмена"
},
"report_reasonInputError": {
"message": "Пожалуйста, введите причину длиной не менее 10 и не более 1000 символов"
},
"reportDialog_reasonInputLabel": {
"report_reasonInputLabel": {
"message": "Причина"
},
"reportDialog_reasonInputPlaceholder": {
"report_reasonInputPlaceholder": {
"message": "Появилось всплывающее окно"
},
"reportDialog_submitExtraText": {
"report_submitErrorExtraText": {
"message": "Похоже, что уже существует открытая проблема или она помечена как 'не будет исправлено'. Пожалуйста, войдите в проблему, чтобы добавить больше информации, используя следующую кнопку."
},
"report_submitErrorText": {
"message": "Отчет не отправлен?"
},
"report_submitSuccessExtraText": {
"message": "Пока мы работаем над этим, вы можете добавить этот сайт в список исключений в настройках расширения или просто отключить расширение для этого сайта, нажав на значок расширения на панели инструментов вашего браузера."
},
"reportDialog_submitText": {
"report_submitSuccessText": {
"message": "Отчет отправлен!"
},
"reportDialog_urlInputError": {
"report_urlInputError": {
"message": "Пожалуйста, введите корректный URL длиной не более 1000 символов"
},
"reportDialog_urlInputLabel": {
"report_urlInputLabel": {
"message": "URL"
}
}

View File

@ -0,0 +1,98 @@
{
"appDesc": {
"message": "Biri çerez onayı diyaloglarını mı söyledi? 😋"
},
"contextMenu_issueOption": {
"message": "Bu sorunu takip et"
},
"contextMenu_reportOption": {
"message": "Bu web sitesini bildir"
},
"contextMenu_settingsOption": {
"message": "Hariç tutma listesini yönet"
},
"options_addButton": {
"message": "Hariç tutma ekle"
},
"options_addPrompt": {
"message": "Alan adı girin"
},
"options_clearButton": {
"message": "Listeyi temizle"
},
"options_empty": {
"message": "Hariç tutma bulunamadı"
},
"options_exclusionListTitle": {
"message": "Hariç tutma listesi"
},
"options_exportButton": {
"message": "Listeyi dışa aktar"
},
"options_filterPlaceholder": {
"message": "Yazdıktan sonra ENTER tuşuna basarak filtreleyin"
},
"options_importButton": {
"message": "Listeyi içe aktar"
},
"popup_bannerIssueOpen": {
"message": "Bu sayfa için bir sorun bildirildi. Uzantı geçici olarak beklenildiği gibi çalışmayabilir. Sorunu takip edin veya daha fazla bilgi ekleyin"
},
"popup_bannerIssueWontFix": {
"message": "Bu sayfa için bir sorun bildirildi ancak düzeltilmeyecek. Bu sayfada uzantının beklenildiği gibi çalışmayabileceğini unutmayın. Aşağıdaki düğmeyi kullanarak uzantıyı kapatmanızı öneririz"
},
"popup_bannerSupport": {
"message": "Bu site artık desteklenmiyor. Daha fazla bilgi için lütfen buradan okumaya devam edin"
},
"popup_bannerUpdateAvailable": {
"message": "Güncelleme mevcut. Yeni sorunları bildirmeden önce bilinen sorunları gidermek için lütfen en son sürümü yükleyin"
},
"popup_contributeOption": {
"message": "Bu projeye katkıda bulun"
},
"popup_databaseVersion": {
"message": "Veritabanı sürümü"
},
"popup_extensionVersion": {
"message": "Uzantı sürümü"
},
"popup_helpOption": {
"message": "Yardım veya sorunlar?"
},
"popup_rateOption": {
"message": "Bu uzantıyı değerlendir"
},
"report_bodyText": {
"message": "Bu raporda kişisel bilgi paylaşmayın. Daha fazla ayrıntı eklemek istiyorsanız, bir sonraki adımda soruna gidin ve yorum ekleyin."
},
"report_cancelButtonText": {
"message": "İptal"
},
"report_reasonInputError": {
"message": "Lütfen en az 10 ve en fazla 1000 karakterlik bir sebep girin"
},
"report_reasonInputLabel": {
"message": "Sebep"
},
"report_reasonInputPlaceholder": {
"message": "Açılır pencere göründü"
},
"report_submitErrorExtraText": {
"message": "Zaten açık bir sorun var gibi görünüyor veya 'düzeltmeyecek' olarak işaretlenmiş. Aşağıdaki düğmeyi kullanarak sorun sayfasına giderek daha fazla bilgi ekleyin."
},
"report_submitErrorText": {
"message": "Rapor gönderilmedi mi?"
},
"report_submitSuccessExtraText": {
"message": "Bu sorun üzerinde çalışırken, uzantı ayarlarında bu web sitesini hariç tutma listesine ekleyebilir veya tarayıcı araç çubuğundaki uzantı simgesine tıklayarak bu web sitesi için uzantıyı devre dışı bırakabilirsiniz"
},
"report_submitSuccessText": {
"message": "Rapor gönderildi!"
},
"report_urlInputError": {
"message": "Lütfen en fazla 1000 karakterle geçerli bir URL girin"
},
"report_urlInputLabel": {
"message": "URL"
}
}

View File

@ -0,0 +1 @@
export default 'off-icon';

View File

@ -0,0 +1 @@
export default 'on-icon';

View File

@ -0,0 +1 @@
export default 'warn-icon';

View File

@ -0,0 +1,56 @@
import { jest } from '@jest/globals';
chrome.action = {
...chrome.action,
openPopup: jest.fn(),
setBadgeBackgroundColor: jest.fn(),
setBadgeText: jest.fn(),
setIcon: jest.fn(),
} as typeof chrome.action;
chrome.contextMenus = {
...chrome.contextMenus,
create: jest.fn(),
onClicked: {
...(chrome.contextMenus ?? {}).onClicked,
addListener: jest.fn(),
},
removeAll: jest.fn(),
} as typeof chrome.contextMenus;
chrome.declarativeNetRequest = {
...chrome.declarativeNetRequest,
updateSessionRules: jest.fn(),
} as typeof chrome.declarativeNetRequest;
chrome.runtime = {
...chrome.runtime,
getManifest: jest.fn(
() =>
({
content_scripts: [{ matches: ['https://example.com/*'] }],
version: '1.0.0',
}) as chrome.runtime.ManifestV3
),
onInstalled: {
...(chrome.runtime ?? {}).onInstalled,
addListener: jest.fn(),
},
onStartup: {
...(chrome.runtime ?? {}).onStartup,
addListener: jest.fn(),
},
openOptionsPage: jest.fn(),
} as typeof chrome.runtime;
chrome.webRequest = {
...chrome.webRequest,
onBeforeRequest: {
...(chrome.webRequest ?? {}).onBeforeRequest,
addListener: jest.fn(),
},
onErrorOccurred: {
...(chrome.webRequest ?? {}).onErrorOccurred,
addListener: jest.fn(),
},
} as typeof chrome.webRequest;

View File

@ -0,0 +1,5 @@
import { jest } from '@jest/globals';
jest.mock('@plasmohq/messaging', () => ({
sendToContentScript: jest.fn(),
}));

View File

@ -0,0 +1,6 @@
import { jest } from '@jest/globals';
jest.mock('~utils/domain', () => ({
formatDomainFromURL: jest.fn(() => 'example.com'),
validateSupport: jest.fn(),
}));

View File

@ -0,0 +1,9 @@
import { jest } from '@jest/globals';
jest.mock('~utils/storage', () => ({
storage: {
get: async (key: string) => (await chrome.storage.local.get(key))?.[key],
remove: (key: string) => chrome.storage.local.remove(key),
set: (key: string, value: any) => chrome.storage.local.set({ [key]: value }),
},
}));

View File

@ -2,20 +2,84 @@
"name": "browser-extension",
"version": "1.0.0",
"scripts": {
"build": "rimraf build; sh scripts/build.sh; sh scripts/pack.sh",
"lint": "eslint --fix"
"dev": "plasmo dev",
"build": "plasmo build",
"lint": "eslint --fix",
"package": "plasmo package",
"test": "jest"
},
"dependencies": {
"@plasmohq/messaging": "^0.6.2",
"@plasmohq/storage": "^1.13.0",
"plasmo": "^0.89.4",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@types/firefox-webext-browser": "^120.0.3",
"eslint": "^8.57.0",
"@jest/globals": "29.7.0",
"@jest/types": "29.6.3",
"@testing-library/react": "^16.0.1",
"@types/chrome": "^0.0.283",
"@types/node": "^20.11.5",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@typescript-eslint/eslint-plugin": "^8.14.0",
"@typescript-eslint/parser": "^8.14.0",
"eslint": "^8.57.1",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"minify": "^9.2.0",
"rimraf": "^5.0.5"
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jest-dom": "^5.4.0",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-simple-import-sort": "^12.1.1",
"eslint-plugin-testing-library": "^6.4.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest-webextension-mock": "^3.9.0",
"postcss": "^8.4.49",
"postcss-modules": "^6.0.1",
"prettier": "^3.3.3",
"ts-jest": "^29.2.5",
"typescript": "^5.6.3"
},
"engines": {
"node": "20.x"
},
"packageManager": "yarn@4.1.0",
"license": "MIT"
"manifest": {
"author": "wanhose",
"browser_specific_settings": {
"gecko": {
"id": "{77e2c00b-e173-4604-863d-01645d8d2826}",
"strict_min_version": "126.0",
"update_url": "https://www.cookie-dialog-monster.com/releases/mozilla/updates.json"
}
},
"default_locale": "en",
"description": "__MSG_appDesc__",
"host_permissions": [
"http://*/*",
"https://*/*"
],
"name": "Cookie Dialog Monster",
"permissions": [
"contextMenus",
"declarativeNetRequest",
"storage",
"webRequest"
],
"version": "8.0.5",
"web_accessible_resources": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"resources": [
"https://fonts.googleapis.com/css?family=Inter"
]
}
]
}
}

View File

@ -1,16 +0,0 @@
#!/bin/bash
input="./src"
output="./build"
mkdir $output
for file in $(find $input -name "*.css" -o -name "*.html" -o -name "*.js" | sed "s|^$input/||"); do
input_file="$input/$file"
output_file="$output/$file"
mkdir -p "${output_file%/*}" && touch "$output_file"
yarn minify $input_file > $output_file
done
cp -nR "$input/." $output

View File

@ -1,7 +0,0 @@
#!/usr/bin/env bash
path=$(pwd)
version=$(jq -r '.version' "$path/build/manifest.json")
cd "$path/build" || exit
zip -r "$path/$(basename $path)-$version.zip" . -x */\.* *.git* \.* *.md *.sh *.zip

View File

@ -1,74 +0,0 @@
{
"appDesc": {
"message": "Hat jemand Cookie-Einwilligungsdialoge gesagt? 😋"
},
"contextMenu_issueOption": {
"message": "Folgen Sie diesem Problem auf GitHub"
},
"contextMenu_reportOption": {
"message": "Diese Website melden"
},
"contextMenu_settingsOption": {
"message": "Ausschlussliste verwalten"
},
"options_addButton": {
"message": "Ausschluss hinzufügen"
},
"options_addPrompt": {
"message": "Domain eingeben"
},
"options_clearButton": {
"message": "Liste leeren"
},
"options_exclusionListTitle": {
"message": "Ausschlussliste"
},
"options_exportButton": {
"message": "Liste exportieren"
},
"options_filterPlaceholder": {
"message": "Drücken Sie ENTER, um nach dem Tippen zu filtern"
},
"options_importButton": {
"message": "Liste importieren"
},
"popup_contributeOption": {
"message": "Zu diesem Projekt beitragen"
},
"popup_databaseVersion": {
"message": "Datenbankversion"
},
"popup_extensionVersion": {
"message": "Erweiterungsversion"
},
"popup_helpOption": {
"message": "Hilfe oder Probleme?"
},
"popup_rateOption": {
"message": "Bewerten Sie diese Erweiterung"
},
"reportDialog_bodyText": {
"message": "Bitte geben Sie in diesem Bericht keine persönlichen Informationen weiter. Wenn Sie weitere Details hinzufügen möchten, öffnen Sie das GitHub-Problem im nächsten Schritt und fügen Sie einen Kommentar hinzu."
},
"reportDialog_reasonInputError": {
"message": "Bitte geben Sie einen Grund mit mindestens 10 und höchstens 1000 Zeichen ein"
},
"reportDialog_reasonInputLabel": {
"message": "Grund"
},
"reportDialog_reasonInputPlaceholder": {
"message": "Popup ist erschienen"
},
"reportDialog_submitExtraText": {
"message": "Während wir daran arbeiten, können Sie diese Website zur Ausschlussliste in den Erweiterungseinstellungen hinzufügen oder einfach die Erweiterung für diese Website deaktivieren, indem Sie auf das Erweiterungssymbol in Ihrer Browser-Symbolleiste klicken."
},
"reportDialog_submitText": {
"message": "Bericht gesendet!"
},
"reportDialog_urlInputError": {
"message": "Bitte geben Sie eine gültige URL mit höchstens 1000 Zeichen ein"
},
"reportDialog_urlInputLabel": {
"message": "URL"
}
}

View File

@ -1,74 +0,0 @@
{
"appDesc": {
"message": "¿Alguien mencionó los diálogos de consentimiento de cookies? 😋"
},
"contextMenu_issueOption": {
"message": "Sigue este problema en GitHub"
},
"contextMenu_reportOption": {
"message": "Reportar este sitio web"
},
"contextMenu_settingsOption": {
"message": "Gestionar lista de exclusión"
},
"options_addButton": {
"message": "Agregar exclusión"
},
"options_addPrompt": {
"message": "Ingrese dominio"
},
"options_clearButton": {
"message": "Borrar lista"
},
"options_exclusionListTitle": {
"message": "Lista de exclusión"
},
"options_exportButton": {
"message": "Exportar lista"
},
"options_filterPlaceholder": {
"message": "Presiona ENTER para filtrar después de escribir"
},
"options_importButton": {
"message": "Importar lista"
},
"popup_contributeOption": {
"message": "Contribuir a este proyecto"
},
"popup_databaseVersion": {
"message": "Versión de la base de datos"
},
"popup_extensionVersion": {
"message": "Versión de la extensión"
},
"popup_helpOption": {
"message": "¿Ayuda o problemas?"
},
"popup_rateOption": {
"message": "Califica esta extensión"
},
"reportDialog_bodyText": {
"message": "Por favor, no compartas ninguna información personal en este informe. Si deseas agregar más detalles, abre el problema de GitHub en el siguiente paso y agrega un comentario."
},
"reportDialog_reasonInputError": {
"message": "Por favor, ingrese una razón con al menos 10 caracteres y no más de 1000 caracteres"
},
"reportDialog_reasonInputLabel": {
"message": "Razón"
},
"reportDialog_reasonInputPlaceholder": {
"message": "Apareció un popup"
},
"reportDialog_submitExtraText": {
"message": "Mientras trabajamos en esto, puedes agregar este sitio web a la lista de exclusión en la configuración de la extensión o simplemente deshabilitar la extensión para este sitio web haciendo clic en el ícono de la extensión en la barra de herramientas de tu navegador."
},
"reportDialog_submitText": {
"message": "¡Informe enviado!"
},
"reportDialog_urlInputError": {
"message": "Por favor, ingrese una URL válida con no más de 1000 caracteres"
},
"reportDialog_urlInputLabel": {
"message": "URL"
}
}

View File

@ -1,74 +0,0 @@
{
"appDesc": {
"message": "A menționat cineva dialogurile de consimțământ pentru cookie-uri? 😋"
},
"contextMenu_issueOption": {
"message": "Urmărește această problemă pe GitHub"
},
"contextMenu_reportOption": {
"message": "Raportează acest site web"
},
"contextMenu_settingsOption": {
"message": "Gestionează lista de excludere"
},
"options_addButton": {
"message": "Adaugă excludere"
},
"options_addPrompt": {
"message": "Introdu domeniul"
},
"options_clearButton": {
"message": "Golește lista"
},
"options_exclusionListTitle": {
"message": "Lista de excludere"
},
"options_exportButton": {
"message": "Exportă lista"
},
"options_filterPlaceholder": {
"message": "Apasă ENTER pentru a filtra după tastare"
},
"options_importButton": {
"message": "Importă lista"
},
"popup_contributeOption": {
"message": "Contribuie la acest proiect"
},
"popup_databaseVersion": {
"message": "Versiunea bazei de date"
},
"popup_extensionVersion": {
"message": "Versiunea extensiei"
},
"popup_helpOption": {
"message": "Ajutor sau probleme?"
},
"popup_rateOption": {
"message": "Evaluează această extensie"
},
"reportDialog_bodyText": {
"message": "Te rugăm să nu împărtășești informații personale în acest raport. Dacă dorești să adaugi mai multe detalii, deschide problema GitHub în pasul următor și adaugă un comentariu."
},
"reportDialog_reasonInputError": {
"message": "Vă rugăm să introduceți un motiv cu cel puțin 10 caractere și maximum 1000 de caractere"
},
"reportDialog_reasonInputLabel": {
"message": "Motiv"
},
"reportDialog_reasonInputPlaceholder": {
"message": "A apărut un popup"
},
"reportDialog_submitExtraText": {
"message": "În timp ce lucrăm la acest aspect, poți adăuga acest site web la lista de excludere din setările extensiei sau poți dezactiva extensia pentru acest site web făcând clic pe pictograma extensiei din bara de instrumente a browserului."
},
"reportDialog_submitText": {
"message": "Raport trimis!"
},
"reportDialog_urlInputError": {
"message": "Vă rugăm să introduceți o adresă URL validă cu maximum 1000 de caractere"
},
"reportDialog_urlInputLabel": {
"message": "URL"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,11 @@
import onClicked from './utils/contextMenus/onClicked';
import onInstalled from './utils/runtime/onInstalled';
import onStartup from './utils/runtime/onStartup';
import onBeforeRequest from './utils/webRequest/onBeforeRequest';
import onErrorOccurred from './utils/webRequest/onErrorOccurred';
chrome.contextMenus.onClicked.addListener(onClicked);
chrome.runtime.onInstalled.addListener(onInstalled);
chrome.runtime.onStartup.addListener(onStartup);
chrome.webRequest.onBeforeRequest.addListener(onBeforeRequest as any, { urls: ['<all_urls>'] });
chrome.webRequest.onErrorOccurred.addListener(onErrorOccurred, { urls: ['<all_urls>'] });

View File

@ -0,0 +1,19 @@
import type { PlasmoMessaging } from '@plasmohq/messaging';
import { DEFAULT_EXTENSION_DATA } from '~utils/constants';
import { storage } from '~utils/storage';
import type { ExtensionData } from '~utils/types';
const handler: PlasmoMessaging.MessageHandler<never, Response> = async (req, res) => {
const data = (await storage.get<ExtensionData>('data')) || DEFAULT_EXTENSION_DATA;
res.send({ data, success: true });
return;
};
interface Response {
readonly data?: ExtensionData;
readonly success: boolean;
}
export default handler;

View File

@ -0,0 +1,29 @@
import type { PlasmoMessaging } from '@plasmohq/messaging';
import { API_URL } from '~utils/constants';
import { storage } from '~utils/storage';
import type { ExtensionData } from '~utils/types';
const handler: PlasmoMessaging.MessageHandler<never, Response> = async (req, res) => {
try {
const response = await fetch(`${API_URL}/data/`);
const { data } = await response.json();
if (data) {
await storage.set('data', data);
res.send({ data, success: true });
return;
}
res.send({ success: false });
} catch {
res.send({ success: false });
}
};
interface Response {
readonly data?: ExtensionData;
readonly success: boolean;
}
export default handler;

View File

@ -0,0 +1,63 @@
import type { PlasmoMessaging } from '@plasmohq/messaging';
import { API_URL, DEFAULT_DOMAIN_CONFIG } from '~utils/constants';
import { noop } from '~utils/error';
import { storage } from '~utils/storage';
import type { DomainConfig } from '~utils/types';
import updateIconHandler from '../extension/updateIcon';
const handler: PlasmoMessaging.MessageHandler<Request, Response> = async (req, res) => {
const { domain } = req.body ?? {};
const { tab } = req.sender || {};
if (domain) {
let data = (await storage.get<DomainConfig>(domain)) || DEFAULT_DOMAIN_CONFIG;
const now = Date.now();
if ((data.issue?.expiresAt && now > data.issue.expiresAt) || !data.issue?.expiresAt) {
const response = await fetch(`${API_URL}/issues/${domain}/`);
if (response.status === 429) {
res.send({ data, success: true });
return;
}
const issue = await response.json();
if (issue.success) {
data = { ...data, issue: { ...issue.data, expiresAt: now + 8 * 60 * 60 * 1000 } };
await storage.set(domain, data);
if (tab?.id !== undefined) {
await updateIconHandler(
{ body: { domain }, name: 'extension/updateIcon', sender: { tab } },
{ send: noop }
);
}
res.send({ data, success: true });
return;
}
data = { ...data, issue: { expiresAt: now + 24 * 60 * 60 * 1000 } };
await storage.set(domain, data);
}
res.send({ data, success: true });
return;
}
res.send({ success: false });
};
interface Request {
readonly domain: string;
}
interface Response {
readonly data?: DomainConfig;
readonly success: boolean;
}
export default handler;

View File

@ -0,0 +1,35 @@
import type { PlasmoMessaging } from '@plasmohq/messaging';
import { API_URL } from '~utils/constants';
import { storage } from '~utils/storage';
const handler: PlasmoMessaging.MessageHandler<never, Response> = async (req, res) => {
try {
const response = await fetch(`${API_URL}/version/`);
if (response.status === 429) {
res.send({ success: false });
return;
}
const { data } = await response.json();
const { version } = chrome.runtime.getManifest();
if (data !== version) {
await storage.set('updateAvailable', data);
res.send({ success: true });
return;
}
await storage.remove('updateAvailable');
res.send({ success: false });
} catch {
res.send({ success: false });
}
};
interface Response {
readonly success: boolean;
}
export default handler;

View File

@ -0,0 +1,25 @@
import type { PlasmoMessaging } from '@plasmohq/messaging';
const handler: PlasmoMessaging.MessageHandler<Request, Response> = async (req, res) => {
const { value } = req.body || {};
const { frameId, tab } = req.sender || {};
if (frameId === 0 && tab?.id !== undefined) {
await chrome.action.setBadgeBackgroundColor({ color: '#6b7280' });
await chrome.action.setBadgeText({ tabId: tab.id, text: value ? `${value}` : '' });
res.send({ success: true });
return;
}
res.send({ success: false });
};
interface Request {
readonly value: number;
}
interface Response {
readonly success: boolean;
}
export default handler;

View File

@ -0,0 +1,49 @@
import type { PlasmoMessaging } from '@plasmohq/messaging';
import offIcon from 'url:~assets/off.png';
import onIcon from 'url:~assets/on.png';
import warnIcon from 'url:~assets/warn.png';
import { DEFAULT_DOMAIN_CONFIG, DEFAULT_EXTENSION_DATA } from '~utils/constants';
import { validateSupport } from '~utils/domain';
import { storage } from '~utils/storage';
import type { DomainConfig, ExtensionData } from '~utils/types';
const handler: PlasmoMessaging.MessageHandler<Request, Response> = async (req, res) => {
const { domain } = req.body || {};
const { frameId, tab } = req.sender || {};
if (domain && frameId === 0 && tab?.id !== undefined) {
const config = (await storage.get<DomainConfig>(domain)) || DEFAULT_DOMAIN_CONFIG;
const data = (await storage.get<ExtensionData>('data')) || DEFAULT_EXTENSION_DATA;
if (
config.on &&
tab.url &&
validateSupport(new URL(tab.url).hostname, data.exclusions.domains)
) {
if (config.issue?.url) {
await chrome.action.setIcon({ path: warnIcon, tabId: tab.id });
res.send({ success: true });
return;
}
await chrome.action.setIcon({ path: onIcon, tabId: tab.id });
res.send({ success: true });
} else {
await chrome.action.setIcon({ path: offIcon, tabId: tab.id });
res.send({ success: true });
}
}
res.send({ success: false });
};
interface Request {
readonly domain: string;
}
interface Response {
readonly success: boolean;
}
export default handler;

View File

@ -0,0 +1,15 @@
import { REPORT_MENU_ITEM_ID, SETTINGS_MENU_ITEM_ID } from '~utils/constants';
import { suppressLastError } from '~utils/error';
export default function onClicked(data: chrome.contextMenus.OnClickData) {
switch (data.menuItemId) {
case REPORT_MENU_ITEM_ID:
chrome.action.openPopup(suppressLastError);
break;
case SETTINGS_MENU_ITEM_ID:
chrome.runtime.openOptionsPage(suppressLastError);
break;
default:
break;
}
}

View File

@ -0,0 +1,38 @@
import databaseRefreshHandler from '~background/messages/database/refresh';
import {
EXTENSION_MENU_ITEM_ID,
REPORT_MENU_ITEM_ID,
SETTINGS_MENU_ITEM_ID,
} from '~utils/constants';
import { noop } from '~utils/error';
import { storage } from '~utils/storage';
export default async function onInstalled() {
await chrome.contextMenus.removeAll();
const documentUrlPatterns = chrome.runtime.getManifest().content_scripts?.[0].matches;
await chrome.contextMenus.create({
contexts: ['all'],
documentUrlPatterns,
id: EXTENSION_MENU_ITEM_ID,
title: 'Cookie Dialog Monster',
});
await chrome.contextMenus.create({
contexts: ['all'],
documentUrlPatterns,
id: SETTINGS_MENU_ITEM_ID,
parentId: EXTENSION_MENU_ITEM_ID,
title: chrome.i18n.getMessage('contextMenu_settingsOption'),
});
await chrome.contextMenus.create({
contexts: ['all'],
documentUrlPatterns,
id: REPORT_MENU_ITEM_ID,
parentId: EXTENSION_MENU_ITEM_ID,
title: chrome.i18n.getMessage('contextMenu_reportOption'),
});
await storage.remove('updateAvailable');
await databaseRefreshHandler({ name: 'database/refresh' }, { send: noop });
}

View File

@ -0,0 +1,10 @@
import databaseRefreshHandler from '~background/messages/database/refresh';
import extensionUpdateAvailableHandler from '~background/messages/extension/updateAvailable';
import { noop } from '~utils/error';
import { storage } from '~utils/storage';
export default async function onStartup() {
await storage.remove('updateAvailable');
await databaseRefreshHandler({ name: 'database/refresh' }, { send: noop });
await extensionUpdateAvailableHandler({ name: 'extension/updateAvailable' }, { send: noop });
}

View File

@ -0,0 +1,32 @@
import { DEFAULT_DOMAIN_CONFIG, DEFAULT_EXTENSION_DATA } from '~utils/constants';
import { formatDomainFromURL, validateSupport } from '~utils/domain';
import { storage } from '~utils/storage';
import type { DomainConfig, ExtensionData } from '~utils/types';
export default async function onBeforeRequest(details: chrome.webRequest.WebRequestBodyDetails) {
const { tabId, type, url } = details;
const location = new URL(url);
const domain = formatDomainFromURL(location);
if (tabId > -1 && type === 'main_frame') {
const data = (await storage.get<ExtensionData>('data')) || DEFAULT_EXTENSION_DATA;
if (!validateSupport(location.hostname, data.exclusions.domains)) {
return;
}
const config = (await storage.get<DomainConfig>(domain)) || DEFAULT_DOMAIN_CONFIG;
if (data.rules.length) {
const rulesWithTabId = data.rules.map((rule) => ({
...rule,
condition: { ...rule.condition, tabIds: [tabId] },
}));
chrome.declarativeNetRequest.updateSessionRules({
addRules: config.on ? rulesWithTabId : undefined,
removeRuleIds: data.rules.map((rule) => rule.id),
});
}
}
}

View File

@ -0,0 +1,15 @@
import { sendToContentScript } from '@plasmohq/messaging';
export default async function onErrorOccurred(details: chrome.webRequest.WebResponseErrorDetails) {
const { error, tabId } = details;
if (error === 'net::ERR_BLOCKED_BY_CLIENT' && tabId > -1) {
await sendToContentScript({
body: {
value: error,
},
name: 'INCREASE_LOG_COUNT',
tabId,
});
}
}

View File

@ -0,0 +1,368 @@
import { sendToBackground } from '@plasmohq/messaging';
import type { PlasmoCSConfig } from 'plasmo';
import { DEFAULT_DOMAIN_CONFIG, DEFAULT_EXTENSION_DATA } from '~utils/constants';
import { formatDomainFromURL, validateSupport } from '~utils/domain';
import type { DomainConfig, ExtensionData } from '~utils/types';
export const config: PlasmoCSConfig = {
all_frames: true,
matches: ['http://*/*', 'https://*/*'],
run_at: 'document_start',
};
class NotifiableSet extends Set {
constructor(...args: any[]) {
super(...args);
}
add(value: any): this {
super.add(value);
sendToBackground({ body: { value: super.size }, name: 'extension/updateBadge' });
return this;
}
}
let { actions, exclusions, keywords, tokens }: ExtensionData = DEFAULT_EXTENSION_DATA;
let domainConfig: DomainConfig = DEFAULT_DOMAIN_CONFIG;
let initiallyVisible: boolean = false;
const domain = formatDomainFromURL(new URL(location.href));
const log = new NotifiableSet();
const observer = new MutationObserver(mutationHandler);
const options: MutationObserverInit = { childList: true, subtree: true };
const seen = new Set<HTMLElement>();
document.addEventListener('visibilitychange', setUpAfterWaitForBody);
window.addEventListener('pageshow', setUpAfterWaitForBody);
setUpAfterWaitForBody();
function clean(elements: readonly HTMLElement[], skipMatch?: boolean): void {
let index = 0;
const size = 50;
function chunk() {
const end = Math.min(index + size, elements.length);
for (; index < end; index++) {
const element = elements[index];
if (match(element, skipMatch)) {
if (element instanceof HTMLDialogElement) element.close();
hide(element);
log.add(`${Date.now()}`);
}
seen.add(element);
}
if (index < elements.length) {
requestAnimationFrame(chunk);
}
}
requestAnimationFrame(chunk);
}
function forceClean(from: HTMLElement): void {
const elements = getElements(tokens.selectors, { filterEarly: true, from });
if (elements.length) {
fix();
clean(elements, true);
}
}
function hasKeyword(element: HTMLElement): boolean {
return !!keywords.length && !!element.outerHTML.match(new RegExp(keywords.join('|')));
}
function filterNodeEarly(node: Node, stopRecursion?: boolean): readonly HTMLElement[] {
if (node.nodeType !== Node.ELEMENT_NODE || !(node instanceof HTMLElement)) {
return [];
}
if (hasKeyword(node) && !stopRecursion) {
return [node, ...[...node.children].flatMap((node) => filterNodeEarly(node, true))];
}
return [node];
}
function fix(): void {
for (const action of actions) {
const { name, property, selector } = action;
if (domain.match(action.domain.replaceAll(/\*/g, '[^ ]*'))) {
switch (name) {
case 'click': {
const element = document.querySelector(selector);
if (element instanceof HTMLElement) {
element.click();
log.add(name);
}
break;
}
case 'remove': {
const element = document.querySelector(selector);
if (element instanceof HTMLElement && property) {
element.style.removeProperty(property);
log.add(name);
}
break;
}
case 'reload': {
window.location.reload();
break;
}
case 'reset': {
const element = document.querySelector(selector);
if (element instanceof HTMLElement && property) {
element.style.setProperty(property, 'initial', 'important');
log.add(name);
}
break;
}
case 'resetAll': {
const elements = getElements(selector);
if (property) {
elements.forEach((e) => e?.style?.setProperty(property, 'initial', 'important'));
log.add(name);
}
break;
}
}
}
}
const backdrops = getElements(tokens.backdrops);
for (const backdrop of backdrops) {
if (backdrop.children.length === 0 && !seen.has(backdrop)) {
log.add(`${Date.now()}`);
seen.add(backdrop);
hide(backdrop);
}
}
const skips = exclusions.overflows.map((x) => (x.split('.').length < 3 ? `*${x}` : x));
if (!skips.some((x) => domain.match(x.replaceAll(/\*/g, '[^ ]*')))) {
for (const element of [document.body, document.documentElement]) {
element?.classList.remove(...(tokens.classes ?? []));
element?.style.setProperty('position', 'initial', 'important');
element?.style.setProperty('overflow-y', 'initial', 'important');
}
}
const ionRouterOutlet = document.getElementsByTagName('ion-router-outlet')[0];
if (ionRouterOutlet) {
// 2024-08-02: fix #644 temporarily
ionRouterOutlet.removeAttribute('inert');
log.add('ion-router-outlet');
}
const t4Wrapper = document.getElementsByClassName('t4-wrapper')[0];
if (t4Wrapper) {
log.add('t4-wrapper');
// 2024-09-12: fix #945 temporarily
t4Wrapper.removeAttribute('inert');
}
}
function getElements(selector: Selector, params: GetElementsParams = {}): readonly HTMLElement[] {
const { filterEarly, from } = params;
let result: HTMLElement[] = [];
if (selector.length) {
result = [
...(from ?? document).querySelectorAll(selector as string),
] as unknown as HTMLElement[];
if (filterEarly) {
result = result.flatMap((node) => filterNodeEarly(node));
}
}
return result;
}
function getElementsWithChildren(
selector: Selector,
params: GetElementsParams = {}
): readonly HTMLElement[] {
const elements = getElements(selector, params);
const elementsWithChildren = elements.flatMap((element) => [element, ...element.children]);
return elementsWithChildren as unknown as readonly HTMLElement[];
}
function hide(element: HTMLElement) {
element.style.setProperty('clip-path', 'circle(0px)', 'important');
element.style.setProperty('display', 'none', 'important');
element.style.setProperty('height', '0px', 'important');
element.style.setProperty('overflow', 'hidden', 'important');
element.style.setProperty('transform', 'scale(0)', 'important');
}
function isInViewport(element: HTMLElement): boolean {
const styles = window.getComputedStyle(element);
const height = window.innerHeight || document.documentElement.clientHeight;
const position = element.getBoundingClientRect();
const scroll = window.scrollY;
return (
position.bottom === position.top ||
(scroll + position.top <= scroll + height && scroll + position.bottom >= scroll) ||
styles.animationDuration !== '0s' ||
styles.transitionDuration !== '0s'
);
}
function match(element: HTMLElement, skipMatch?: boolean): boolean {
if (!exclusions.tags.length || !tokens.selectors.length) {
return false;
}
if (!(element instanceof HTMLElement) || !element.tagName) {
return false;
}
if (seen.has(element)) {
return false;
}
const tagName = element.tagName.toUpperCase();
if (exclusions.tags.includes(tagName)) {
return false;
}
const hasAttributes = !!element.getAttributeNames().filter((x) => x !== 'data-nosnippet').length;
if (!hasAttributes && !tagName.includes('-')) {
forceClean(element);
}
// 2023-06-10: fix #113 temporarily
if (element.classList.contains('chat-line__message')) {
return false;
}
// 2024-08-03: fix #701 temporarily
if (element.classList.contains('sellos')) {
return false;
}
const isDialog = tagName === 'DIALOG' && element.getAttribute('open') === 'true';
const isFakeDialog = tagName === 'DIV' && element.className.includes('cmp');
return (
(isDialog || isFakeDialog || isInViewport(element)) &&
(skipMatch || element.matches(tokens.selectors as unknown as string))
);
}
function mutationHandler(mutations: readonly MutationRecord[]): void {
if (!domainConfig.on || !tokens.selectors.length) {
return;
}
const nodes = mutations.flatMap((mutation) => [...mutation.addedNodes]);
const elements = nodes.flatMap((node) => filterNodeEarly(node));
run({ elements });
}
function run(params: RunParams = {}): void {
const { containers, elements, skipMatch } = params;
if (document.body?.children.length && domainConfig.on && tokens.selectors.length) {
fix();
if (elements?.length) {
clean(elements, skipMatch);
}
if (elements === undefined && containers?.length) {
clean(containers.flatMap((x) => getElementsWithChildren(x, { filterEarly: true })));
}
}
}
function runtimeMessageHandler(message: RuntimeMessage): void {
switch (message.name) {
case 'INCREASE_LOG_COUNT': {
log.add(message.value);
break;
}
default:
break;
}
}
async function setUp(params: SetUpParams = {}): Promise<void> {
const { data } = await sendToBackground({ name: 'database/get' });
exclusions = data?.exclusions ?? exclusions;
sendToBackground({ body: { domain }, name: 'extension/updateIcon' });
if (!validateSupport(location.hostname, exclusions.domains)) {
observer.disconnect();
return;
}
domainConfig = (await sendToBackground({ body: { domain }, name: 'domain/config' }))?.data;
if (domainConfig.on) {
chrome.runtime.onMessage.addListener(runtimeMessageHandler);
actions = data?.actions ?? actions;
keywords = data?.keywords ?? keywords;
tokens = data?.tokens ?? tokens;
observer.observe(document.body ?? document.documentElement, options);
if (!params.skipRunFn) run({ containers: tokens.containers });
}
}
async function setUpAfterWaitForBody(): Promise<void> {
if (document.visibilityState === 'visible' && !initiallyVisible) {
if (document.body) {
initiallyVisible = true;
await setUp();
return;
}
setTimeout(setUpAfterWaitForBody, 50);
}
}
interface GetElementsParams {
readonly filterEarly?: boolean;
readonly from?: HTMLElement;
}
interface RunParams {
readonly containers?: readonly string[];
readonly elements?: readonly HTMLElement[];
readonly skipMatch?: boolean;
}
interface RuntimeMessage {
readonly name: string;
readonly value?: string;
}
type Selector = string | readonly string[];
interface SetUpParams {
readonly skipRunFn?: boolean;
}

View File

@ -0,0 +1,17 @@
export function useBrowser(): UseBrowserResult {
const isChromium = navigator.userAgent.indexOf('Chrome') !== -1;
const isEdge = navigator.userAgent.indexOf('Edg') !== -1;
const isFirefox = navigator.userAgent.indexOf('Firefox') !== -1;
return {
isChromium,
isEdge,
isFirefox,
};
}
export interface UseBrowserResult {
readonly isChromium: boolean;
readonly isEdge: boolean;
readonly isFirefox: boolean;
}

View File

@ -0,0 +1,41 @@
import { sendToBackground } from '@plasmohq/messaging';
import { useStorage } from '@plasmohq/storage/hook';
import { useCallback, useEffect, useRef, useState } from 'react';
import { storage } from '~utils/storage';
import type { ExtensionData } from '~utils/types';
export function useData(): UseDataResult {
const timeoutId = useRef<number | undefined>(undefined);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isRefetched, setIsRefetched] = useState<boolean>(false);
const [data] = useStorage<ExtensionData>({ instance: storage, key: 'data' });
const refetch = useCallback(async () => {
setIsLoading(true);
await sendToBackground({ name: 'database/refresh' });
setIsLoading(false);
setIsRefetched(true);
timeoutId.current = window.setTimeout(() => setIsRefetched(false), 60000);
}, []);
useEffect(() => {
return () => {
window.clearTimeout(timeoutId.current);
};
}, []);
return {
data,
isLoading,
isRefetched,
refetch,
};
}
export interface UseDataResult {
readonly data?: ExtensionData;
readonly isLoading: boolean;
readonly isRefetched: boolean;
readonly refetch: () => Promise<void>;
}

View File

@ -0,0 +1,52 @@
import { useStorage } from '@plasmohq/storage/hook';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { DEFAULT_DOMAIN_CONFIG } from '~utils/constants';
import { formatDomainFromURL, validateSupport } from '~utils/domain';
import { storage } from '~utils/storage';
import type { DomainConfig, ExtensionData } from '~utils/types';
import { useTab } from './useTab';
export function useDomain(): UseDomainResult {
const [domain, setDomain] = useState<string>('');
const [config, setConfig] = useStorage<DomainConfig>({ instance: storage, key: domain });
const [data] = useStorage<ExtensionData>({ instance: storage, key: 'data' });
const tab = useTab();
const tabId = tab ? (tab.id ?? -1) : -1;
const isSupported = useMemo(
() =>
!!data?.exclusions.domains.length &&
!!tab?.url &&
validateSupport(new URL(tab.url).hostname, data.exclusions.domains),
[data, tab]
);
const toggleStatus = useCallback(async () => {
if (tabId > -1) {
await setConfig((prev = DEFAULT_DOMAIN_CONFIG) => ({ ...prev, on: !prev.on }));
await chrome.tabs.reload(tabId, { bypassCache: true });
}
}, [domain, tabId]);
useEffect(() => {
if (tab?.url) {
setDomain(formatDomainFromURL(new URL(tab.url)));
}
}, [tab]);
return {
config: config || DEFAULT_DOMAIN_CONFIG,
domain,
isSupported,
toggleStatus,
};
}
export interface UseDomainResult {
readonly config: DomainConfig;
readonly domain: string;
readonly isSupported: boolean;
readonly toggleStatus: () => Promise<void>;
}

View File

@ -0,0 +1,90 @@
import { useCallback, useState } from 'react';
import { API_URL } from '~utils/constants';
import type { ReportParams, ReportResult } from '~utils/types';
import { useExtension } from './useExtension';
export function useDomainReport(): UseDomainReportResult {
const extension = useExtension();
const [errors, setErrors] = useState<{ readonly [key: string]: string }>({});
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
const report = useCallback(
async (params: ReportParams): Promise<ReportResult | undefined> => {
try {
setIsSubmitting(true);
const reason = params.reason;
const url = params.url;
const errors = validateForm(params);
if (errors) {
setErrors(errors);
setIsSubmitting(false);
return;
}
const userAgent = navigator.userAgent;
const version = extension.version;
const body = JSON.stringify({ reason, url, userAgent, version });
const headers = { 'Cache-Control': 'no-cache', 'Content-type': 'application/json' };
const requestInit = { body, headers, method: 'POST' };
const response = await fetch(`${API_URL}/report/`, requestInit);
if (response.ok) {
return await response.json();
}
if (response.status === 429) {
throw new Error(chrome.i18n.getMessage('report_rateLimitError'));
}
throw new Error(chrome.i18n.getMessage('report_unknownError'));
} catch (error) {
if (error instanceof Error) {
setErrors({ url: error.message });
}
} finally {
setIsSubmitting(false);
}
},
[extension.version]
);
return {
errors,
isSubmitting,
report,
};
}
function validateForm(params: ReportParams): Partial<ReportParams> | undefined {
const { reason, url } = params;
let errors: Partial<ReportParams> | undefined = undefined;
if (!reason || reason.length < 10 || reason.length > 1000) {
errors = {
...(errors ?? {}),
reason: chrome.i18n.getMessage('report_reasonInputError'),
};
}
try {
if (/\s/.test(url)) throw new Error();
new URL(url);
} catch {
errors = {
...(errors ?? {}),
url: chrome.i18n.getMessage('report_urlInputError'),
};
}
return errors;
}
export interface UseDomainReportResult {
readonly errors: { readonly [key: string]: string };
readonly isSubmitting: boolean;
readonly report: (params: ReportParams) => Promise<ReportResult | undefined>;
}

View File

@ -0,0 +1,52 @@
import { useCallback, useEffect, useState } from 'react';
import { storage } from '~utils/storage';
async function getExclusions(): Promise<readonly Exclusion[]> {
const result = await storage.rawGetAll();
return Object.keys(result).flatMap((key) => (result[key]?.on === false ? [{ name: key }] : []));
}
export function useExclusions(): readonly Exclusion[] {
const [exclusions, setExclusions] = useState<readonly Exclusion[]>([]);
const handleStorageChange = useCallback(
(
changes: { [key: string]: chrome.storage.StorageChange },
areaName: chrome.storage.AreaName
) => {
if (areaName === 'local') {
for (const key in changes) {
if (key === 'data') {
continue;
}
setExclusions((prev) => {
if (changes[key].newValue?.on === false) {
return [...prev, { name: key }];
}
return prev.filter((exclusion) => exclusion.name !== key);
});
}
}
},
[]
);
useEffect(() => {
chrome.storage.onChanged.addListener(handleStorageChange);
getExclusions().then(setExclusions);
return () => {
chrome.storage.onChanged.removeListener(handleStorageChange);
};
}, [handleStorageChange]);
return exclusions;
}
export interface Exclusion {
readonly name: string;
}

View File

@ -0,0 +1,18 @@
import { useStorage } from '@plasmohq/storage/hook';
import { storage } from '~utils/storage';
export function useExtension(): UseExtensionResult {
const [updateAvailable] = useStorage({ instance: storage, key: 'updateAvailable' });
const { version } = chrome.runtime.getManifest();
return {
updateAvailable,
version,
};
}
export interface UseExtensionResult {
readonly updateAvailable?: string;
readonly version: string;
}

View File

@ -0,0 +1,11 @@
import { useEffect, useState } from 'react';
export function useTab(): chrome.tabs.Tab | undefined {
const [tab, setTab] = useState<chrome.tabs.Tab | undefined>(undefined);
useEffect(() => {
chrome.tabs.query({ active: true, currentWindow: true }).then((tabs) => setTab(tabs[0]));
}, []);
return tab;
}

View File

@ -1,67 +0,0 @@
{
"manifest_version": 3,
"name": "Cookie Dialog Monster",
"version": "7.3.2",
"default_locale": "en",
"description": "__MSG_appDesc__",
"icons": {
"16": "assets/icons/16.png",
"48": "assets/icons/48.png",
"128": "assets/icons/128.png"
},
"action": {
"default_icon": "assets/icons/disabled.png",
"default_title": "Cookie Dialog Monster"
},
"options_page": "options.html",
"author": "wanhose",
"background": {
"scripts": ["scripts/background.js"],
"service_worker": "scripts/background.js"
},
"browser_specific_settings": {
"gecko": {
"id": "{77e2c00b-e173-4604-863d-01645d8d2826}",
"strict_min_version": "126.0",
"update_url": "https://www.cookie-dialog-monster.com/mozilla/updates.json"
}
},
"content_scripts": [
{
"all_frames": true,
"exclude_matches": [
"*://*.bauhaus.cz/*",
"*://*.codesandbox.io/*",
"*://*.facebook.com/*",
"*://*.googleapis.com/embed/*",
"*://*.olympics.com/*",
"*://*.youtube-nocookie.com/embed/*",
"*://*.youtube.com/embed/*",
"*://www.youtube.com/*",
"*://translate.google.ca/*",
"*://translate.google.co.in/*",
"*://translate.google.co.jp/*",
"*://translate.google.co.uk/*",
"*://translate.google.com.au/*",
"*://translate.google.com.br/*",
"*://translate.google.com/*",
"*://translate.google.de/*",
"*://translate.google.es/*",
"*://translate.google.fr/*",
"*://translate.google.it/*",
"*://www.cookie-dialog-monster.com/*"
],
"js": ["scripts/content.js", "scripts/dialog.js"],
"matches": ["http://*/*", "https://*/*"],
"run_at": "document_start"
}
],
"host_permissions": ["http://*/*", "https://*/*"],
"permissions": ["contextMenus", "declarativeNetRequest", "scripting", "storage", "webRequest"],
"web_accessible_resources": [
{
"matches": ["http://*/*", "https://*/*"],
"resources": ["https://fonts.googleapis.com/css?family=Inter"]
}
]
}

View File

@ -1,125 +0,0 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Cookie Dialog Monster > Exclusion List</title>
<link rel="stylesheet" href="/styles/reset.css" />
<link rel="stylesheet" href="/styles/options.css" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter" />
<script src="/scripts/options.js"></script>
</head>
<body>
<header>
<div>
<h1 class="header-title">
Cookie Dialog Monster > <span data-i18n="options_exclusionListTitle"></span>
</h1>
</div>
</header>
<main>
<div class="button-group">
<button data-variant="large" id="add-button">
<span data-i18n="options_addButton"></span>
<svg
fill="none"
height="14"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="14"
>
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
</button>
<button data-variant="large" id="clear-button">
<span data-i18n="options_clearButton"></span>
<svg
aria-hidden="true"
fill="none"
height="14"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="14"
>
<polyline points="3 6 5 6 21 6"></polyline>
<path
d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"
/>
<line x1="10" y1="11" x2="10" y2="17"></line>
<line x1="14" y1="11" x2="14" y2="17"></line>
</svg>
</button>
<button data-variant="large" id="import-button">
<span data-i18n="options_importButton"></span>
<svg
aria-hidden="true"
fill="none"
height="14"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="14"
>
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="17 8 12 3 7 8"></polyline>
<line x1="12" y1="3" x2="12" y2="15"></line>
</svg>
<input accept=".cdm" id="file-input" style="display: none" type="file" />
</button>
<button data-variant="large" id="export-button">
<span data-i18n="options_exportButton"></span>
<svg
aria-hidden="true"
fill="none"
height="14"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="14"
>
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="7 10 12 15 17 10"></polyline>
<line x1="12" y1="15" x2="12" y2="3"></line>
</svg>
</button>
</div>
<input id="filter-input" data-i18n-placeholder="options_filterPlaceholder" />
<ul id="exclusion-list">
<li id="exclusion-list-item-template" style="display: none">
<span></span>
<button>
<svg
aria-hidden="true"
fill="none"
height="14"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="14"
>
<polyline points="3 6 5 6 21 6"></polyline>
<path
d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"
/>
<line x1="10" y1="11" x2="10" y2="17"></line>
<line x1="14" y1="11" x2="14" y2="17"></line>
</svg>
</button>
</li>
<li id="exclusion-list-item-empty" style="display: none"></li>
</ul>
</main>
<footer></footer>
</body>
</html>

View File

@ -0,0 +1,13 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Cookie Dialog Monster > Exclusion List</title>
<link rel="stylesheet" href="~src/styles/reset.css" />
<link rel="stylesheet" href="~src/styles/common.css" />
<link rel="stylesheet" href="~src/styles/options.css" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter" />
</head>
<body></body>
</html>

View File

@ -0,0 +1,227 @@
import { type ChangeEvent, type JSX, type MouseEvent, useCallback, useRef, useState } from 'react';
import { useExclusions } from '~hooks/useExclusions';
import { DOMAIN_REG_EXP } from '~utils/domain';
import { storage } from '~utils/storage';
import type { DomainConfig } from '~utils/types';
export default function Options(): JSX.Element {
const exclusions = useExclusions();
const fileInputRef = useRef<HTMLInputElement | null>(null);
const [filterValue, setFilterValue] = useState<string>('');
const handleAddClick = useCallback(async () => {
const message = chrome.i18n.getMessage('options_addPrompt');
const domain = window.prompt(message)?.trim().replace('www.', '');
if (domain && (DOMAIN_REG_EXP.test(domain) || domain === 'localhost')) {
const prev = await storage.get<DomainConfig>(domain);
await storage.set(domain, { ...prev, on: false });
}
}, []);
const handleClearClick = useCallback(async () => {
for (const domain of exclusions) {
const prev = await storage.get<DomainConfig>(domain.name);
if (prev?.issue?.url) await storage.set(domain.name, { ...prev, on: true });
else await storage.remove(domain.name);
}
}, [exclusions]);
const handleDeleteClick = useCallback(async (event: MouseEvent<HTMLButtonElement>) => {
const { value: domain } = event.currentTarget.dataset;
if (domain) {
const prev = await storage.get<DomainConfig>(domain);
if (prev?.issue?.url) await storage.set(domain, { ...prev, on: true });
else await storage.remove(domain);
}
}, []);
const handleExportClick = useCallback(() => {
const anchor = document.createElement('a');
const now = new Date();
const day = now.getDate().toString().padStart(2, '0');
const month = now.getMonth().toString().padStart(2, '0');
const year = now.getUTCFullYear();
const text = exclusions.reduce((prev, curr) => `${prev}\n${curr.name}`, '');
const defaultTitle = `${year}${month}${day}`;
const customTitle = window.prompt('Enter a file name', defaultTitle);
if (customTitle) {
const blob = new Blob([text], { type: 'octet/stream' });
const url = window.URL.createObjectURL(blob);
anchor.href = url;
anchor.download = `${customTitle || defaultTitle}.cdm`;
anchor.click();
window.URL.revokeObjectURL(url);
}
}, [exclusions]);
const handleFileChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
const file = event.currentTarget.files?.[0];
const reader = new FileReader();
reader.addEventListener('load', async (event) => {
const input =
event.currentTarget && 'result' in event.currentTarget
? (event.currentTarget.result as string)?.split('\n')
: [];
for (const value of input) {
const domain = value.replace('www.', '').trim();
if (domain && (DOMAIN_REG_EXP.test(domain) || domain === 'localhost')) {
const prev = await storage.get<DomainConfig>(domain);
await storage.set(domain, { ...prev, on: false });
}
}
});
if (file) {
event.currentTarget.value = '';
reader.readAsText(file);
}
}, []);
const handleFilterChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
setFilterValue(event.currentTarget.value);
}, []);
const handleImportClick = useCallback(() => {
fileInputRef.current?.click();
}, []);
return (
<>
<header className="header">
<div>
<h1>Cookie Dialog Monster &gt; {chrome.i18n.getMessage('options_exclusionListTitle')}</h1>
</div>
</header>
<main className="main">
<div className="button-group">
<button className="button" data-variant="large" onClick={handleAddClick}>
<span>{chrome.i18n.getMessage('options_addButton')}</span>
<svg
fill="none"
height="14"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="14"
>
<line x1="12" y1="5" x2="12" y2="19" />
<line x1="5" y1="12" x2="19" y2="12" />
</svg>
</button>
<button className="button" data-variant="large" onClick={handleClearClick}>
<span>{chrome.i18n.getMessage('options_clearButton')}</span>
<svg
aria-hidden="true"
fill="none"
height="14"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="14"
>
<polyline points="3 6 5 6 21 6" />
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
<line x1="10" y1="11" x2="10" y2="17" />
<line x1="14" y1="11" x2="14" y2="17" />
</svg>
</button>
<button className="button" data-variant="large" onClick={handleImportClick}>
<span>{chrome.i18n.getMessage('options_importButton')}</span>
<svg
aria-hidden="true"
fill="none"
height="14"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="14"
>
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
<polyline points="17 8 12 3 7 8" />
<line x1="12" y1="3" x2="12" y2="15" />
</svg>
<input
accept=".cdm"
onChange={handleFileChange}
ref={fileInputRef}
style={{ display: 'none' }}
type="file"
/>
</button>
<button className="button" data-variant="large" onClick={handleExportClick}>
<span>{chrome.i18n.getMessage('options_exportButton')}</span>
<svg
aria-hidden="true"
fill="none"
height="14"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="14"
>
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
<polyline points="7 10 12 15 17 10" />
<line x1="12" y1="15" x2="12" y2="3" />
</svg>
</button>
</div>
<input
className="input"
onChange={handleFilterChange}
placeholder={chrome.i18n.getMessage('options_filterPlaceholder')}
value={filterValue}
/>
<ul className="exclusion-list">
{exclusions.length ? (
exclusions.map((exclusion) => (
<li key={exclusion.name}>
<span>{exclusion.name}</span>
<button className="button" data-value={exclusion.name} onClick={handleDeleteClick}>
<svg
aria-hidden="true"
fill="none"
height="14"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="14"
>
<polyline points="3 6 5 6 21 6" />
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
<line x1="10" y1="11" x2="10" y2="17" />
<line x1="14" y1="11" x2="14" y2="17" />
</svg>
</button>
</li>
))
) : (
<li>{chrome.i18n.getMessage('options_empty')}</li>
)}
</ul>
</main>
<footer className="footer" />
</>
);
}

View File

@ -1,158 +0,0 @@
<html>
<head>
<meta charset="UTF-8" />
<link rel="stylesheet" href="/styles/reset.css" />
<link rel="stylesheet" href="/styles/popup.css" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter" />
<script src="/scripts/popup.js"></script>
</head>
<body>
<header>
<h1 class="header-title">Cookie Dialog Monster</h1>
<button id="settings-button">
<svg
aria-hidden="true"
fill="none"
height="18"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="18"
>
<circle cx="12" cy="12" r="3"></circle>
<path
d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"
/>
</svg>
</button>
</header>
<main>
<popup-button id="power-option" role="button" tabindex="0">
<svg
aria-hidden="true"
fill="none"
height="32"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="32"
>
<path d="M18.36 6.64a9 9 0 1 1-12.73 0" />
<line x1="12" y1="2" x2="12" y2="12" />
</svg>
<span id="host"></span>
</popup-button>
<popup-button data-href="mailto:hello@wanhose.dev" id="help-option" role="link" tabindex="0">
<svg
aria-hidden="true"
fill="none"
height="32"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="32"
>
<path
d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"
/>
</svg>
<span data-i18n="popup_helpOption"></span>
</popup-button>
<popup-button
data-href="https://github.com/wanhose/cookie-dialog-monster"
id="contribute-option"
role="link"
tabindex="0"
>
<svg
aria-hidden="true"
fill="none"
height="32"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="32"
>
<path
d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"
/>
</svg>
<span data-i18n="popup_contributeOption"></span>
</popup-button>
<popup-button data-href="#" id="rate-option" role="link" tabindex="0">
<svg
aria-hidden="true"
fill="none"
height="32"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="32"
>
<polygon
points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"
/>
</svg>
<span data-i18n="popup_rateOption"></span>
</popup-button>
<popup-data-container>
<popup-data>
<strong data-i18n="popup_databaseVersion"></strong>
<span id="database-version"></span>
<popup-data-button
data-animation="flip"
id="refresh-database-button"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
fill="none"
height="12"
id="refresh-database-spinner"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="12"
>
<polyline points="1 4 1 10 7 10"></polyline>
<polyline points="23 20 23 14 17 14"></polyline>
<path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15"></path>
</svg>
<svg
aria-hidden="true"
fill="none"
height="12"
id="refresh-database-check"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke="var(--color-success)"
viewBox="0 0 24 24"
width="12"
>
<polyline points="20 6 9 17 4 12"></polyline>
</svg>
</popup-data-button>
</popup-data>
<popup-data>
<strong data-i18n="popup_extensionVersion"></strong>
<span id="extension-version"></span>
</popup-data>
</popup-data-container>
</main>
<footer></footer>
</body>
</html>

View File

@ -0,0 +1,12 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="~src/styles/reset.css" />
<link rel="stylesheet" href="~src/styles/common.css" />
<link rel="stylesheet" href="~src/styles/popup.css" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter" />
</head>
<body></body>
</html>

View File

@ -0,0 +1,472 @@
import { type FormEvent, type JSX, type KeyboardEvent, useCallback, useState } from 'react';
import { useBrowser } from '~hooks/useBrowser';
import { useData } from '~hooks/useData';
import { useDomain } from '~hooks/useDomain';
import { useDomainReport } from '~hooks/useDomainReport';
import { useExtension } from '~hooks/useExtension';
import { CHROME_STORE_URL, EDGE_STORE_URL, FIREFOX_STORE_URL } from '~utils/constants';
import { suppressLastError } from '~utils/error';
import type { ReportParams } from '~utils/types';
export default function Popup(): JSX.Element {
const { isChromium, isEdge, isFirefox } = useBrowser();
const { data, isLoading, isRefetched, refetch } = useData();
const { config, domain, isSupported, toggleStatus } = useDomain();
const { errors, isSubmitting, report } = useDomainReport();
const extension = useExtension();
const [issueExists, setIssueExists] = useState<boolean>(false);
const [issueURL, setIssueURL] = useState<string>('');
const [view, setView] = useState<'main' | 'report'>('main');
const handleCancelClick = useCallback(() => {
setIssueExists(false);
setIssueURL('');
setView('main');
}, []);
const handleInputKeyDown = useCallback(
(event: KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => {
if (event.key === 'Enter') {
event.preventDefault();
event.currentTarget.blur();
}
},
[]
);
const handlePowerClick = useCallback(async () => {
await toggleStatus();
window.close();
}, [toggleStatus]);
const handleRefreshClick = useCallback(async () => {
await refetch();
}, [refetch]);
const handleReportClick = useCallback(() => {
setView('report');
}, []);
const handleSettingsClick = useCallback(async () => {
chrome.runtime.openOptionsPage(suppressLastError);
window.close();
}, []);
const handleSubmit = useCallback(
async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const params = Object.fromEntries(Object.entries(new FormData(event.currentTarget)));
const result = await report(params as ReportParams);
if (result) {
setIssueExists(!result.success);
setIssueURL(result.data);
}
},
[report]
);
return (
<>
<header className="header">
<h1>Cookie Dialog Monster</h1>
<div className="header-actions">
<a
className="header-button"
href="https://git.wanhose.dev/wanhose/cookie-dialog-monster/wiki/Help-or-issues%3F"
target="_blank"
rel="noreferrer"
>
<svg
aria-hidden="true"
fill="none"
height="18"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="18"
>
<circle cx="12" cy="12" r="10" />
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" />
<line x1="12" y1="17" x2="12.01" y2="17" />
</svg>
</a>
<button className="header-button" onClick={handleSettingsClick}>
<svg
aria-hidden="true"
fill="none"
height="18"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="18"
>
<circle cx="12" cy="12" r="3" />
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" />
</svg>
</button>
</div>
</header>
<main>
{config.issue?.url ? (
<p className="banner" data-variant="warning" role="alert">
<span>
{chrome.i18n.getMessage(
config.issue.flags?.includes('wontfix')
? 'popup_bannerIssueWontFix'
: 'popup_bannerIssueOpen'
)}
</span>
&nbsp;
<a href={config.issue.url} target="_blank" rel="noreferrer">
<svg
aria-hidden="true"
fill="none"
height="12"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="12"
>
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
<polyline points="15 3 21 3 21 9" />
<line x1="10" y1="14" x2="21" y2="3" />
</svg>
</a>
</p>
) : null}
{extension.updateAvailable ? (
<p className="banner" data-variant="notice" role="alert">
<span>{chrome.i18n.getMessage('popup_bannerUpdateAvailable')}</span>
&nbsp;
<a
href={`https://git.wanhose.dev/wanhose/cookie-dialog-monster/releases/${extension.updateAvailable}`}
target="_blank"
rel="noreferrer"
>
<svg
aria-hidden="true"
fill="none"
height="12"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="12"
>
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
<polyline points="15 3 21 3 21 9" />
<line x1="10" y1="14" x2="21" y2="3" />
</svg>
</a>
</p>
) : null}
{isSupported ? null : (
<p className="banner" data-variant="error" role="alert">
<span>{chrome.i18n.getMessage('popup_bannerSupport')}</span>
&nbsp;
<a
href="https://git.wanhose.dev/wanhose/cookie-dialog-monster/wiki/List-of-unsupported-sites"
target="_blank"
rel="noreferrer"
>
<svg
aria-hidden="true"
fill="none"
height="12"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="12"
>
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
<polyline points="15 3 21 3 21 9" />
<line x1="10" y1="14" x2="21" y2="3" />
</svg>
</a>
</p>
)}
{view === 'main' ? (
<div className="content">
<button
className="popup-button power-option"
data-value={config.on ? 'on' : 'off'}
disabled={!!config.issue || !!extension.updateAvailable || !isSupported}
onClick={handlePowerClick}
>
<svg
aria-hidden="true"
fill="none"
height="32"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="32"
>
<path d="M18.36 6.64a9 9 0 1 1-12.73 0" />
<line x1="12" y1="2" x2="12" y2="12" />
</svg>
<span>{domain}</span>
</button>
<button
className="popup-button"
disabled={!!config.issue || !!extension.updateAvailable || !isSupported}
onClick={handleReportClick}
>
<svg
aria-hidden="true"
fill="none"
height="32"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="32"
>
<path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z" />
<line x1="4" y1="22" x2="4" y2="15" />
</svg>
<span>{chrome.i18n.getMessage('contextMenu_reportOption')}</span>
</button>
<a
className="popup-button"
href="https://git.wanhose.dev/wanhose/cookie-dialog-monster"
target="_blank"
rel="noreferrer"
>
<svg
aria-hidden="true"
fill="none"
height="32"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="32"
>
<circle cx="18" cy="18" r="3" />
<circle cx="6" cy="6" r="3" />
<path d="M13 6h3a2 2 0 0 1 2 2v7" />
<line x1="6" y1="9" x2="6" y2="21" />
</svg>
<span>{chrome.i18n.getMessage('popup_contributeOption')}</span>
</a>
<a
className="popup-button rate-option"
href={
isEdge
? EDGE_STORE_URL
: isChromium
? CHROME_STORE_URL
: isFirefox
? FIREFOX_STORE_URL
: '#'
}
target="_blank"
rel="noreferrer"
>
<svg
aria-hidden="true"
fill="none"
height="32"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="32"
>
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
</svg>
<span>{chrome.i18n.getMessage('popup_rateOption')}</span>
</a>
<div className="popup-data-container">
<div className="popup-data">
<b>{chrome.i18n.getMessage('popup_databaseVersion')}</b>
<span>{data?.version || '?'}</span>
<button
className="popup-data-button"
data-animation="flip"
data-refreshing={isLoading && !isRefetched}
data-refreshed={isRefetched}
disabled={isLoading || isRefetched}
onClick={handleRefreshClick}
>
{isRefetched ? (
<svg
aria-hidden="true"
fill="none"
height="12"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
stroke="var(--color-success)"
viewBox="0 0 24 24"
width="12"
>
<polyline points="20 6 9 17 4 12" />
</svg>
) : (
<svg
aria-hidden="true"
fill="none"
height="12"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
stroke="currentColor"
viewBox="0 0 24 24"
width="12"
>
<polyline points="1 4 1 10 7 10" />
<polyline points="23 20 23 14 17 14" />
<path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15" />
</svg>
)}
</button>
</div>
<div className="popup-data">
<b>{chrome.i18n.getMessage('popup_extensionVersion')}</b>
<span>{extension.version}</span>
</div>
</div>
</div>
) : (
<div className="report">
{issueURL ? (
<>
{issueExists ? (
<div className="report-submit-error-view" hidden>
<svg
aria-hidden="true"
fill="none"
height="48"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
stroke="var(--color-error)"
viewBox="0 0 24 24"
width="48"
>
<circle cx="12" cy="12" r="10" />
<line x1="12" y1="8" x2="12" y2="12" />
<line x1="12" y1="16" x2="12.01" y2="16" />
</svg>
<div className="report-submit-text">
{chrome.i18n.getMessage('report_submitErrorText')}
</div>
<div className="report-submit-extra-text">
{chrome.i18n.getMessage('report_submitErrorExtraText')}
</div>
<div className="report-issue-button">
{chrome.i18n.getMessage('contextMenu_issueOption')}
</div>
</div>
) : (
<div className="report-submit-success-view" hidden>
<svg
aria-hidden="true"
fill="none"
height="48"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
stroke="var(--color-success)"
viewBox="0 0 24 24"
width="48"
>
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
<polyline points="22 4 12 14.01 9 11.01" />
</svg>
<div className="report-submit-text">
{chrome.i18n.getMessage('report_submitSuccessText')}
</div>
<div className="report-submit-extra-text">
{chrome.i18n.getMessage('report_submitSuccessExtraText')}
</div>
<div className="report-issue-button">
{chrome.i18n.getMessage('contextMenu_issueOption')}
</div>
</div>
)}
</>
) : (
<div className="report-form-view">
<div className="report-body-text">{chrome.i18n.getMessage('report_bodyText')}</div>
<form className="report-form" noValidate onSubmit={handleSubmit}>
<div className="report-input-group">
<div className="report-input-label" id="report-label-url">
<span>{chrome.i18n.getMessage('report_urlInputLabel')}</span>
<span className="report-input-label-required">*</span>
</div>
<input
aria-invalid={!!errors['url']}
aria-labelledby="report-label-url"
aria-required="true"
className="report-input"
disabled={isSubmitting}
name="url"
onKeyDown={handleInputKeyDown}
placeholder="https://www.example.com/"
/>
<div aria-hidden={!errors['url']} className="report-input-error">
{chrome.i18n.getMessage('report_urlInputError')}
</div>
</div>
<div className="report-input-group">
<div className="report-input-label" id="report-label-reason">
<span>{chrome.i18n.getMessage('report_reasonInputLabel')}</span>
<span className="report-input-label-required">*</span>
</div>
<textarea
aria-invalid={!!errors['reason']}
aria-labelledby="report-label-reason"
aria-required="true"
className="report-input"
disabled={isSubmitting}
name="reason"
onKeyDown={handleInputKeyDown}
placeholder={chrome.i18n.getMessage('report_reasonInputPlaceholder')}
rows={4}
/>
<div aria-hidden={!errors['reason']} className="report-input-error">
{chrome.i18n.getMessage('report_reasonInputError')}
</div>
</div>
<div className="report-buttons">
<button className="report-submit-button" disabled={isSubmitting} type="submit">
{chrome.i18n.getMessage('contextMenu_reportOption')}
</button>
<button
className="report-cancel-button"
disabled={isSubmitting}
onClick={handleCancelClick}
>
{chrome.i18n.getMessage('report_cancelButtonText')}
</button>
</div>
</form>
</div>
)}
</div>
)}
</main>
<footer />
</>
);
}

Some files were not shown because too many files have changed in this diff Show More