diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..8a42b78
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,7 @@
+[*]
+end_of_line = lf
+insert_final_newline = true
+
+[{*.js,*.ts,*.tsx,*.json,*.css}]
+indent_style = space
+indent_size = 2
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 0000000..121f232
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,28 @@
+module.exports = {
+ extends: ["standard-with-typescript", "standard-jsx", "prettier"],
+ parserOptions: {
+ project: "./tsconfig.json",
+ },
+ rules: {
+ "@typescript-eslint/member-delimiter-style": 2,
+ "@typescript-eslint/semi": ["error", "always"],
+ "@typescript-eslint/no-extra-semi": "error",
+ "@typescript-eslint/explicit-function-return-type": 0,
+ "@typescript-eslint/strict-boolean-expressions": 0,
+ "@typescript-eslint/no-floating-promises": 0,
+ "@typescript-eslint/prefer-nullish-coalescing": 0,
+ "@typescript-eslint/restrict-template-expressions": 0,
+ "@typescript-eslint/promise-function-async": 0,
+ "@typescript-eslint/consistent-type-definitions": 0,
+ "@typescript-eslint/no-misused-promises": ["error", {"checksConditionals": false}],
+ "@typescript-eslint/no-redeclare": 0,
+ "@typescript-eslint/no-non-null-assertion": 0,
+ "@typescript-eslint/member-delimiter-style": 0,
+ "import/no-absolute-path": 0,
+ "no-return-assign": 0,
+ "no-useless-return": 0,
+ "quotes": 0,
+ "object-curly-spacing": 2,
+ "n/no-callback-literal": 0,
+ },
+};
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3e2e84b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+build/
+node_modules/
diff --git a/.projectile b/.projectile
new file mode 100644
index 0000000..c9543be
--- /dev/null
+++ b/.projectile
@@ -0,0 +1,2 @@
+-node_modules/
+-build/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3bbc196
--- /dev/null
+++ b/README.md
@@ -0,0 +1,46 @@
+## About
+
+Photon is a Google Chrome extension that provides information about
+the public IP address you are using to connect to the internet. When
+the extension's icon is clicked, the extension makes a request to the
+[clean.wtfismyip.com](https://clean.wtfismyip.com)
+web service to discover information about your public IP address, and
+then displays that information in a simple HTML view.
+
+## Features
+
+* Displays a public IP address.
+* Displays the Internet Service Provider (ISP) associated with a public IP address.
+* Displays the city associated with a public IP address.
+* Displays the country associated with a public IP address.
+* Displays whether or not a public IP address is a Tor exit node.
+
+## Install
+
+Produce the "build" directory:
+
+ git clone https://github.com/0x1eef/photon.git
+ cd photon
+ npm i
+ npm run build
+
+Add the extension to Chrome:
+
+* Visit `chrome://extensions`.
+* Check "Developer mode" (top right hand corner).
+* Click "Load unpacked extension".
+* Choose the "build/" directory from the file dialog.
+* Done.
+
+## Sources
+
+* [Source code (GitHub)](https://github.com/0x1eef/photon#readme)
+* [Source code (GitLab)](https://gitlab.com/0x1eef/photon#about)
+
+## License
+
+[BSD Zero Clause](https://choosealicense.com/licenses/0bsd/).
+
+See [LICENSE](./LICENSE)
+
+
diff --git a/package-lock.json b/package-lock.json
index af744ef..fbaf28d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,6 +10,7 @@
"react-dom": "^18.2.0"
},
"devDependencies": {
+ "@types/chrome": "^0.0.246",
"@types/react": "^18.0.18",
"@types/react-dom": "^18.0.6",
"clean-webpack-plugin": "^4.0.0",
@@ -224,6 +225,16 @@
"node": ">= 8"
}
},
+ "node_modules/@types/chrome": {
+ "version": "0.0.246",
+ "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.246.tgz",
+ "integrity": "sha512-MxGxEomGxsJiL9xe/7ZwVgwdn8XVKWbPvxpVQl3nWOjrS0Ce63JsfzxUc4aU3GvRcUPYsfufHmJ17BFyKxeA4g==",
+ "dev": true,
+ "dependencies": {
+ "@types/filesystem": "*",
+ "@types/har-format": "*"
+ }
+ },
"node_modules/@types/eslint": {
"version": "8.44.3",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.3.tgz",
@@ -250,6 +261,21 @@
"integrity": "sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==",
"dev": true
},
+ "node_modules/@types/filesystem": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.33.tgz",
+ "integrity": "sha512-2KedRPzwu2K528vFkoXnnWdsG0MtUwPjuA7pRy4vKxlxHEe8qUDZibYHXJKZZr2Cl/ELdCWYqyb/MKwsUuzBWw==",
+ "dev": true,
+ "dependencies": {
+ "@types/filewriter": "*"
+ }
+ },
+ "node_modules/@types/filewriter": {
+ "version": "0.0.30",
+ "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.30.tgz",
+ "integrity": "sha512-lB98tui0uxc7erbj0serZfJlHKLNJHwBltPnbmO1WRpL5T325GOHRiQfr2E29V2q+S1brDO63Fpdt6vb3bES9Q==",
+ "dev": true
+ },
"node_modules/@types/glob": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
@@ -260,6 +286,12 @@
"@types/node": "*"
}
},
+ "node_modules/@types/har-format": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.13.tgz",
+ "integrity": "sha512-PwBsCBD3lDODn4xpje3Y1di0aDJp4Ww7aSfMRVw6ysnxD4I7Wmq2mBkSKaDtN403hqH5sp6c9xQUvFYY3+lkBg==",
+ "dev": true
+ },
"node_modules/@types/json-schema": {
"version": "7.0.13",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz",
diff --git a/package.json b/package.json
index e8909e6..e9e717e 100644
--- a/package.json
+++ b/package.json
@@ -1,10 +1,12 @@
{
- "name": "whatismyip",
+ "name": "photon",
+ "version": "0.1.0",
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
+ "@types/chrome": "^0.0.246",
"@types/react": "^18.0.18",
"@types/react-dom": "^18.0.6",
"clean-webpack-plugin": "^4.0.0",
diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json
new file mode 100644
index 0000000..78f6ed8
--- /dev/null
+++ b/src/_locales/en/messages.json
@@ -0,0 +1,26 @@
+{
+ "ip_address": {
+ "message": "IP Address"
+ },
+ "isp": {
+ "message": "ISP"
+ },
+ "city": {
+ "message": "City"
+ },
+ "country": {
+ "message": "Country"
+ },
+ "tor_exit_node": {
+ "message": "Tor exit node"
+ },
+ "loading": {
+ "message": "Loading..."
+ },
+ "yes": {
+ "message": "Yes"
+ },
+ "no": {
+ "message": "No"
+ }
+}
diff --git a/src/css/components/Response.css b/src/css/components/Response.css
new file mode 100644
index 0000000..7e57e24
--- /dev/null
+++ b/src/css/components/Response.css
@@ -0,0 +1,10 @@
+body .response {
+ padding: 30px;
+}
+
+body .response .row {
+ display: flex;
+ place-content: space-between;
+ font-size: var(--default-font-size);
+ margin: 0 0 10px 0;
+}
diff --git a/src/css/index.css b/src/css/index.css
new file mode 100644
index 0000000..6450af4
--- /dev/null
+++ b/src/css/index.css
@@ -0,0 +1,52 @@
+:root {
+ /* Defaults */
+ --default-font-size: 1.15em;
+ --default-font-family: "SF Mono","Segoe UI Mono","Roboto Mono",Menlo,Courier,monospace;
+
+ /* Colors */
+ --primary-red: #d73e48;
+ --primary-green: #32b643;
+ --primary-blue: #302ecd;
+ --secondary-blue: #807fe2;
+ --primary-white: #FFF;
+ --secondary-white: #f1f1fc;
+}
+
+body {
+ font-family: var(--default-font-family);
+ width: 368px;
+ height: 212px;
+ margin: 0;
+}
+
+#root, .loader {
+ height: 100%;
+}
+a {
+ color: var(--primary-blue) !important;
+ text-decoration: none;
+}
+a:visited {
+ color: var(--primary-blue) !important;
+}
+a:active, a:focus, a:hover {
+ color: var(--primary-blue) !important;
+ text-decoration: underline;
+}
+
+footer {
+ background: var(--secondary-white);
+ color: var(--primary-red);
+ position: fixed;
+ bottom: 0;
+ width: 100%;
+ padding: 10px;
+ text-align: center;
+}
+
+.loader {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ place-content: center;
+}
diff --git a/src/css/label.css b/src/css/label.css
new file mode 100644
index 0000000..ae6c199
--- /dev/null
+++ b/src/css/label.css
@@ -0,0 +1,17 @@
+.label {
+ padding: 4px;
+}
+
+.label-rounded {
+ border-radius: 5px;
+}
+
+.label-error {
+ background: var(--primary-red);
+ color: var(--primary-white);
+}
+
+.label-success {
+ background: var(--primary-green);
+ color: var(--primary-white);
+}
diff --git a/src/html/browseraction.html b/src/html/browseraction.html
new file mode 100644
index 0000000..70c2e28
--- /dev/null
+++ b/src/html/browseraction.html
@@ -0,0 +1,15 @@
+
+