UI overhaul
This commit is contained in:
		
							parent
							
								
									37334ae389
								
							
						
					
					
						commit
						d419af0fb9
					
				
							
								
								
									
										9
									
								
								frontend/.prettierrc.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								frontend/.prettierrc.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "trailingComma": "es5",
 | 
				
			||||||
 | 
					    "tabWidth": 4,
 | 
				
			||||||
 | 
					    "semi": true,
 | 
				
			||||||
 | 
					    "singleQuote": false,
 | 
				
			||||||
 | 
					    "useTabs": false,
 | 
				
			||||||
 | 
					    "printWidth": 100,
 | 
				
			||||||
 | 
					    "plugins": ["prettier-plugin-tailwindcss"]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										137
									
								
								frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										137
									
								
								frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							@ -11,7 +11,9 @@
 | 
				
			|||||||
                "@tailwindcss/vite": "^4.1.11",
 | 
					                "@tailwindcss/vite": "^4.1.11",
 | 
				
			||||||
                "react": "^19.1.0",
 | 
					                "react": "^19.1.0",
 | 
				
			||||||
                "react-dom": "^19.1.0",
 | 
					                "react-dom": "^19.1.0",
 | 
				
			||||||
 | 
					                "react-icons": "^5.5.0",
 | 
				
			||||||
                "react-router-dom": "^7.7.0",
 | 
					                "react-router-dom": "^7.7.0",
 | 
				
			||||||
 | 
					                "sonner": "^2.0.6",
 | 
				
			||||||
                "tailwindcss": "^4.1.11"
 | 
					                "tailwindcss": "^4.1.11"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "devDependencies": {
 | 
					            "devDependencies": {
 | 
				
			||||||
@ -20,10 +22,12 @@
 | 
				
			|||||||
                "@types/react-dom": "^19.1.6",
 | 
					                "@types/react-dom": "^19.1.6",
 | 
				
			||||||
                "@vitejs/plugin-react": "^4.7.0",
 | 
					                "@vitejs/plugin-react": "^4.7.0",
 | 
				
			||||||
                "@vitejs/plugin-react-swc": "^3.11.0",
 | 
					                "@vitejs/plugin-react-swc": "^3.11.0",
 | 
				
			||||||
 | 
					                "daisyui": "^5.0.46",
 | 
				
			||||||
                "eslint": "^9.31.0",
 | 
					                "eslint": "^9.31.0",
 | 
				
			||||||
                "eslint-plugin-react-hooks": "^5.2.0",
 | 
					                "eslint-plugin-react-hooks": "^5.2.0",
 | 
				
			||||||
                "eslint-plugin-react-refresh": "^0.4.20",
 | 
					                "eslint-plugin-react-refresh": "^0.4.20",
 | 
				
			||||||
                "globals": "^16.3.0",
 | 
					                "globals": "^16.3.0",
 | 
				
			||||||
 | 
					                "prettier-plugin-tailwindcss": "^0.6.14",
 | 
				
			||||||
                "vite": "^7.0.5"
 | 
					                "vite": "^7.0.5"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@ -2093,6 +2097,16 @@
 | 
				
			|||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "license": "MIT"
 | 
					            "license": "MIT"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/daisyui": {
 | 
				
			||||||
 | 
					            "version": "5.0.46",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.0.46.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-vMDZK1tI/bOb2Mc3Mk5WpquBG3ZqBz1YKZ0xDlvpOvey60dOS4/5Qhdowq1HndbQl7PgDLDYysxAjjUjwR7/eQ==",
 | 
				
			||||||
 | 
					            "dev": true,
 | 
				
			||||||
 | 
					            "license": "MIT",
 | 
				
			||||||
 | 
					            "funding": {
 | 
				
			||||||
 | 
					                "url": "https://github.com/saadeghi/daisyui?sponsor=1"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "node_modules/debug": {
 | 
					        "node_modules/debug": {
 | 
				
			||||||
            "version": "4.4.1",
 | 
					            "version": "4.4.1",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
 | 
				
			||||||
@ -3204,6 +3218,110 @@
 | 
				
			|||||||
                "node": ">= 0.8.0"
 | 
					                "node": ">= 0.8.0"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/prettier": {
 | 
				
			||||||
 | 
					            "version": "3.6.2",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
 | 
				
			||||||
 | 
					            "dev": true,
 | 
				
			||||||
 | 
					            "license": "MIT",
 | 
				
			||||||
 | 
					            "peer": true,
 | 
				
			||||||
 | 
					            "bin": {
 | 
				
			||||||
 | 
					                "prettier": "bin/prettier.cjs"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "engines": {
 | 
				
			||||||
 | 
					                "node": ">=14"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "funding": {
 | 
				
			||||||
 | 
					                "url": "https://github.com/prettier/prettier?sponsor=1"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/prettier-plugin-tailwindcss": {
 | 
				
			||||||
 | 
					            "version": "0.6.14",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.14.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg==",
 | 
				
			||||||
 | 
					            "dev": true,
 | 
				
			||||||
 | 
					            "license": "MIT",
 | 
				
			||||||
 | 
					            "engines": {
 | 
				
			||||||
 | 
					                "node": ">=14.21.3"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "peerDependencies": {
 | 
				
			||||||
 | 
					                "@ianvs/prettier-plugin-sort-imports": "*",
 | 
				
			||||||
 | 
					                "@prettier/plugin-hermes": "*",
 | 
				
			||||||
 | 
					                "@prettier/plugin-oxc": "*",
 | 
				
			||||||
 | 
					                "@prettier/plugin-pug": "*",
 | 
				
			||||||
 | 
					                "@shopify/prettier-plugin-liquid": "*",
 | 
				
			||||||
 | 
					                "@trivago/prettier-plugin-sort-imports": "*",
 | 
				
			||||||
 | 
					                "@zackad/prettier-plugin-twig": "*",
 | 
				
			||||||
 | 
					                "prettier": "^3.0",
 | 
				
			||||||
 | 
					                "prettier-plugin-astro": "*",
 | 
				
			||||||
 | 
					                "prettier-plugin-css-order": "*",
 | 
				
			||||||
 | 
					                "prettier-plugin-import-sort": "*",
 | 
				
			||||||
 | 
					                "prettier-plugin-jsdoc": "*",
 | 
				
			||||||
 | 
					                "prettier-plugin-marko": "*",
 | 
				
			||||||
 | 
					                "prettier-plugin-multiline-arrays": "*",
 | 
				
			||||||
 | 
					                "prettier-plugin-organize-attributes": "*",
 | 
				
			||||||
 | 
					                "prettier-plugin-organize-imports": "*",
 | 
				
			||||||
 | 
					                "prettier-plugin-sort-imports": "*",
 | 
				
			||||||
 | 
					                "prettier-plugin-style-order": "*",
 | 
				
			||||||
 | 
					                "prettier-plugin-svelte": "*"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "peerDependenciesMeta": {
 | 
				
			||||||
 | 
					                "@ianvs/prettier-plugin-sort-imports": {
 | 
				
			||||||
 | 
					                    "optional": true
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "@prettier/plugin-hermes": {
 | 
				
			||||||
 | 
					                    "optional": true
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "@prettier/plugin-oxc": {
 | 
				
			||||||
 | 
					                    "optional": true
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "@prettier/plugin-pug": {
 | 
				
			||||||
 | 
					                    "optional": true
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "@shopify/prettier-plugin-liquid": {
 | 
				
			||||||
 | 
					                    "optional": true
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "@trivago/prettier-plugin-sort-imports": {
 | 
				
			||||||
 | 
					                    "optional": true
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "@zackad/prettier-plugin-twig": {
 | 
				
			||||||
 | 
					                    "optional": true
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "prettier-plugin-astro": {
 | 
				
			||||||
 | 
					                    "optional": true
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "prettier-plugin-css-order": {
 | 
				
			||||||
 | 
					                    "optional": true
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "prettier-plugin-import-sort": {
 | 
				
			||||||
 | 
					                    "optional": true
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "prettier-plugin-jsdoc": {
 | 
				
			||||||
 | 
					                    "optional": true
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "prettier-plugin-marko": {
 | 
				
			||||||
 | 
					                    "optional": true
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "prettier-plugin-multiline-arrays": {
 | 
				
			||||||
 | 
					                    "optional": true
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "prettier-plugin-organize-attributes": {
 | 
				
			||||||
 | 
					                    "optional": true
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "prettier-plugin-organize-imports": {
 | 
				
			||||||
 | 
					                    "optional": true
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "prettier-plugin-sort-imports": {
 | 
				
			||||||
 | 
					                    "optional": true
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "prettier-plugin-style-order": {
 | 
				
			||||||
 | 
					                    "optional": true
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "prettier-plugin-svelte": {
 | 
				
			||||||
 | 
					                    "optional": true
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "node_modules/punycode": {
 | 
					        "node_modules/punycode": {
 | 
				
			||||||
            "version": "2.3.1",
 | 
					            "version": "2.3.1",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
 | 
				
			||||||
@ -3235,6 +3353,15 @@
 | 
				
			|||||||
                "react": "^19.1.0"
 | 
					                "react": "^19.1.0"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/react-icons": {
 | 
				
			||||||
 | 
					            "version": "5.5.0",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==",
 | 
				
			||||||
 | 
					            "license": "MIT",
 | 
				
			||||||
 | 
					            "peerDependencies": {
 | 
				
			||||||
 | 
					                "react": "*"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "node_modules/react-refresh": {
 | 
					        "node_modules/react-refresh": {
 | 
				
			||||||
            "version": "0.17.0",
 | 
					            "version": "0.17.0",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
 | 
				
			||||||
@ -3377,6 +3504,16 @@
 | 
				
			|||||||
                "node": ">=8"
 | 
					                "node": ">=8"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/sonner": {
 | 
				
			||||||
 | 
					            "version": "2.0.6",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.6.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-yHFhk8T/DK3YxjFQXIrcHT1rGEeTLliVzWbO0xN8GberVun2RiBnxAjXAYpZrqwEVHBG9asI/Li8TAAhN9m59Q==",
 | 
				
			||||||
 | 
					            "license": "MIT",
 | 
				
			||||||
 | 
					            "peerDependencies": {
 | 
				
			||||||
 | 
					                "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc",
 | 
				
			||||||
 | 
					                "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "node_modules/source-map-js": {
 | 
					        "node_modules/source-map-js": {
 | 
				
			||||||
            "version": "1.2.1",
 | 
					            "version": "1.2.1",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
 | 
				
			||||||
 | 
				
			|||||||
@ -13,7 +13,9 @@
 | 
				
			|||||||
        "@tailwindcss/vite": "^4.1.11",
 | 
					        "@tailwindcss/vite": "^4.1.11",
 | 
				
			||||||
        "react": "^19.1.0",
 | 
					        "react": "^19.1.0",
 | 
				
			||||||
        "react-dom": "^19.1.0",
 | 
					        "react-dom": "^19.1.0",
 | 
				
			||||||
 | 
					        "react-icons": "^5.5.0",
 | 
				
			||||||
        "react-router-dom": "^7.7.0",
 | 
					        "react-router-dom": "^7.7.0",
 | 
				
			||||||
 | 
					        "sonner": "^2.0.6",
 | 
				
			||||||
        "tailwindcss": "^4.1.11"
 | 
					        "tailwindcss": "^4.1.11"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "devDependencies": {
 | 
					    "devDependencies": {
 | 
				
			||||||
@ -22,10 +24,12 @@
 | 
				
			|||||||
        "@types/react-dom": "^19.1.6",
 | 
					        "@types/react-dom": "^19.1.6",
 | 
				
			||||||
        "@vitejs/plugin-react": "^4.7.0",
 | 
					        "@vitejs/plugin-react": "^4.7.0",
 | 
				
			||||||
        "@vitejs/plugin-react-swc": "^3.11.0",
 | 
					        "@vitejs/plugin-react-swc": "^3.11.0",
 | 
				
			||||||
 | 
					        "daisyui": "^5.0.46",
 | 
				
			||||||
        "eslint": "^9.31.0",
 | 
					        "eslint": "^9.31.0",
 | 
				
			||||||
        "eslint-plugin-react-hooks": "^5.2.0",
 | 
					        "eslint-plugin-react-hooks": "^5.2.0",
 | 
				
			||||||
        "eslint-plugin-react-refresh": "^0.4.20",
 | 
					        "eslint-plugin-react-refresh": "^0.4.20",
 | 
				
			||||||
        "globals": "^16.3.0",
 | 
					        "globals": "^16.3.0",
 | 
				
			||||||
 | 
					        "prettier-plugin-tailwindcss": "^0.6.14",
 | 
				
			||||||
        "vite": "^7.0.5"
 | 
					        "vite": "^7.0.5"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,28 +1,41 @@
 | 
				
			|||||||
import { useState, useEffect } from "react";
 | 
					import { useEffect, useState } from "react";
 | 
				
			||||||
import { HashRouter as Router, Routes, Route, Navigate } from "react-router-dom";
 | 
					import { Navigate, Route, HashRouter as Router, Routes } from "react-router-dom";
 | 
				
			||||||
import TopNav from "./components/topnav";
 | 
					import About from "./components/about";
 | 
				
			||||||
import SideNav from "./components/sidenav";
 | 
					import Container from "./components/container";
 | 
				
			||||||
import Results from "./components/results";
 | 
					import Results from "./components/results";
 | 
				
			||||||
import Settings from "./components/settings";
 | 
					import Settings from "./components/settings";
 | 
				
			||||||
 | 
					import TabNavigation from "./components/tabnavigation";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function App() {
 | 
					const App = () => {
 | 
				
			||||||
    const [isSideNavOpen, setIsSideNavOpen] = useState(false);
 | 
					    const [validConfig, setValidConfig] = useState(null);
 | 
				
			||||||
    const [validConfig, setValidConfig] = useState(null); // null = loading
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        chrome.storage.local.get("valid_config", (result) => {
 | 
					        // Fix: Access chrome API properly for browser extensions
 | 
				
			||||||
            if (chrome.runtime.lastError) {
 | 
					        if (typeof chrome !== "undefined" && chrome.storage) {
 | 
				
			||||||
                console.error("Error reading valid_config:", chrome.runtime.lastError);
 | 
					            chrome.storage.local.get("valid_config", (result) => {
 | 
				
			||||||
                setValidConfig(false); // fallback
 | 
					                if (chrome.runtime.lastError) {
 | 
				
			||||||
            } else {
 | 
					                    console.error("Error reading valid_config:", chrome.runtime.lastError);
 | 
				
			||||||
                setValidConfig(result.valid_config === true);
 | 
					                    setValidConfig(false);
 | 
				
			||||||
            }
 | 
					                } else {
 | 
				
			||||||
        });
 | 
					                    setValidConfig(result.valid_config === true);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // Fallback for development/testing
 | 
				
			||||||
 | 
					            setValidConfig(false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }, []);
 | 
					    }, []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const handleConfigSaved = () => {
 | 
				
			||||||
 | 
					        setValidConfig(true);
 | 
				
			||||||
 | 
					        // Navigate to main tab after config is saved
 | 
				
			||||||
 | 
					        window.location.hash = "#/results";
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (validConfig === null) {
 | 
					    if (validConfig === null) {
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <div className="flex items-center justify-center h-screen bg-black text-white">
 | 
					            <div className="flex h-screen items-center justify-center">
 | 
				
			||||||
 | 
					                <span className="loading loading-spinner loading-md ms-2"></span>
 | 
				
			||||||
                Loading...
 | 
					                Loading...
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
@ -30,20 +43,16 @@ function App() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Router>
 | 
					        <Router>
 | 
				
			||||||
            <div className="min-w-full min-h-full w-full h-full flex flex-grow bg-black/95 flex-col relative">
 | 
					            <div className="flex h-screen flex-col py-4">
 | 
				
			||||||
                <div className="w-full min-h-16 max-h-16 h-16 shrink-0 flex sticky top-0 z-20 border-b border-b-white bg-black overflow-x-hidden">
 | 
					                <Container>
 | 
				
			||||||
                    <TopNav onMenuClick={() => setIsSideNavOpen(true)} />
 | 
					                    <TabNavigation validConfig={validConfig} />
 | 
				
			||||||
                </div>
 | 
					                    <div className="divider"></div>
 | 
				
			||||||
 | 
					 | 
				
			||||||
                <div id="currentpagecontainer" className="w-full grow overflow-y-auto">
 | 
					 | 
				
			||||||
                    <Routes>
 | 
					                    <Routes>
 | 
				
			||||||
                        {!validConfig ? (
 | 
					                        {!validConfig ? (
 | 
				
			||||||
                            <>
 | 
					                            <>
 | 
				
			||||||
                                <Route
 | 
					                                <Route
 | 
				
			||||||
                                    path="/settings"
 | 
					                                    path="/settings"
 | 
				
			||||||
                                    element={
 | 
					                                    element={<Settings onConfigSaved={handleConfigSaved} />}
 | 
				
			||||||
                                        <Settings onConfigSaved={() => setValidConfig(true)} />
 | 
					 | 
				
			||||||
                                    }
 | 
					 | 
				
			||||||
                                />
 | 
					                                />
 | 
				
			||||||
                                <Route path="*" element={<Navigate to="/settings" replace />} />
 | 
					                                <Route path="*" element={<Navigate to="/settings" replace />} />
 | 
				
			||||||
                            </>
 | 
					                            </>
 | 
				
			||||||
@ -52,21 +61,14 @@ function App() {
 | 
				
			|||||||
                                <Route path="/" element={<Navigate to="/results" replace />} />
 | 
					                                <Route path="/" element={<Navigate to="/results" replace />} />
 | 
				
			||||||
                                <Route path="/results" element={<Results />} />
 | 
					                                <Route path="/results" element={<Results />} />
 | 
				
			||||||
                                <Route path="/settings" element={<Settings />} />
 | 
					                                <Route path="/settings" element={<Settings />} />
 | 
				
			||||||
 | 
					                                <Route path="/about" element={<About />} />
 | 
				
			||||||
                            </>
 | 
					                            </>
 | 
				
			||||||
                        )}
 | 
					                        )}
 | 
				
			||||||
                    </Routes>
 | 
					                    </Routes>
 | 
				
			||||||
                </div>
 | 
					                </Container>
 | 
				
			||||||
 | 
					 | 
				
			||||||
                <div
 | 
					 | 
				
			||||||
                    className={`fixed top-0 left-0 w-full h-full z-50 bg-black transform transition-transform duration-300 ease-in-out ${
 | 
					 | 
				
			||||||
                        isSideNavOpen ? "translate-x-0" : "-translate-x-full"
 | 
					 | 
				
			||||||
                    }`}
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                    <SideNav onClose={() => setIsSideNavOpen(false)} />
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </Router>
 | 
					        </Router>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default App;
 | 
					export default App;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								frontend/src/assets/fonts/InterVariable-Italic.woff2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								frontend/src/assets/fonts/InterVariable-Italic.woff2
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								frontend/src/assets/fonts/InterVariable.woff2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								frontend/src/assets/fonts/InterVariable.woff2
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										15
									
								
								frontend/src/assets/fonts/font-face.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								frontend/src/assets/fonts/font-face.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					@font-face {
 | 
				
			||||||
 | 
					    font-family: Inter;
 | 
				
			||||||
 | 
					    src: url("./InterVariable.woff2");
 | 
				
			||||||
 | 
					    font-style: normal;
 | 
				
			||||||
 | 
					    font-weight: 300 900;
 | 
				
			||||||
 | 
					    font-display: swap;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@font-face {
 | 
				
			||||||
 | 
					    font-family: Inter;
 | 
				
			||||||
 | 
					    src: url("./InterVariable-Italic.woff2");
 | 
				
			||||||
 | 
					    font-style: italic;
 | 
				
			||||||
 | 
					    font-weight: 300 900;
 | 
				
			||||||
 | 
					    font-display: swap;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										57
									
								
								frontend/src/components/about.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								frontend/src/components/about.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					import { FaDiscord, FaTelegram } from "react-icons/fa";
 | 
				
			||||||
 | 
					import { SiGitea } from "react-icons/si";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const AboutPage = () => {
 | 
				
			||||||
 | 
					    const socialLinks = [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            name: "Discord",
 | 
				
			||||||
 | 
					            icon: <FaDiscord className="text-4xl" />,
 | 
				
			||||||
 | 
					            url: "https://discord.cdrm-project.com/",
 | 
				
			||||||
 | 
					            description: "Join our Discord community",
 | 
				
			||||||
 | 
					            color: "hover:text-indigo-400",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            name: "Telegram",
 | 
				
			||||||
 | 
					            icon: <FaTelegram className="text-4xl" />,
 | 
				
			||||||
 | 
					            url: "https://telegram.cdrm-project.com/",
 | 
				
			||||||
 | 
					            description: "Follow us on Telegram",
 | 
				
			||||||
 | 
					            color: "hover:text-sky-400",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            name: "Gitea",
 | 
				
			||||||
 | 
					            icon: <SiGitea className="text-4xl" />,
 | 
				
			||||||
 | 
					            url: "https://cdm-project.com/tpd94/cdrm-project",
 | 
				
			||||||
 | 
					            description: "Check out our code",
 | 
				
			||||||
 | 
					            color: "hover:text-lime-400",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <div className="flex min-h-full flex-col items-center justify-center p-6">
 | 
				
			||||||
 | 
					            <div className="mb-8 text-center">
 | 
				
			||||||
 | 
					                <h2 className="mb-2 text-3xl font-bold">Connect with us</h2>
 | 
				
			||||||
 | 
					                <p className="text-base-content/70 text-lg">Join our community and stay updated</p>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <div className="grid w-full max-w-4xl grid-cols-1 gap-6 md:grid-cols-3">
 | 
				
			||||||
 | 
					                {socialLinks.map((link) => (
 | 
				
			||||||
 | 
					                    <a
 | 
				
			||||||
 | 
					                        key={link.name}
 | 
				
			||||||
 | 
					                        href={link.url}
 | 
				
			||||||
 | 
					                        target="_blank"
 | 
				
			||||||
 | 
					                        rel="noopener noreferrer"
 | 
				
			||||||
 | 
					                        className={`card bg-base-200 shadow-xl transition-all duration-300 hover:scale-105 hover:shadow-2xl ${link.color}`}
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
 | 
					                        <div className="card-body items-center text-center">
 | 
				
			||||||
 | 
					                            <div className="mb-2">{link.icon}</div>
 | 
				
			||||||
 | 
					                            <h3 className="card-title text-xl font-semibold">{link.name}</h3>
 | 
				
			||||||
 | 
					                            <p className="text-base-content/70">{link.description}</p>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    </a>
 | 
				
			||||||
 | 
					                ))}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default AboutPage;
 | 
				
			||||||
							
								
								
									
										9
									
								
								frontend/src/components/container.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								frontend/src/components/container.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					const Container = ({ children, className = "", ...props }) => {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <main className={`container mx-auto p-4 mb-5 ${className}`} {...props}>
 | 
				
			||||||
 | 
					            {children}
 | 
				
			||||||
 | 
					        </main>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Container;
 | 
				
			||||||
							
								
								
									
										72
									
								
								frontend/src/components/injectionmenu.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								frontend/src/components/injectionmenu.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					import { useEffect, useState } from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const InjectionMenu = () => {
 | 
				
			||||||
 | 
					    const [injectionType, setInjectionType] = useState("LICENSE");
 | 
				
			||||||
 | 
					    const [drmOverride, setDrmOverride] = useState("DISABLED");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    useEffect(() => {
 | 
				
			||||||
 | 
					        chrome.storage.local.get(["injection_type", "drm_override"], (result) => {
 | 
				
			||||||
 | 
					            if (result.injection_type !== undefined) {
 | 
				
			||||||
 | 
					                setInjectionType(result.injection_type);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (result.drm_override !== undefined) {
 | 
				
			||||||
 | 
					                setDrmOverride(result.drm_override);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }, []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const handleInjectionTypeChange = (type) => {
 | 
				
			||||||
 | 
					        chrome.storage.local.set({ injection_type: type }, () => {
 | 
				
			||||||
 | 
					            if (chrome.runtime.lastError) {
 | 
				
			||||||
 | 
					                console.error("Error updating injection_type:", chrome.runtime.lastError);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                setInjectionType(type);
 | 
				
			||||||
 | 
					                console.log(`Injection type updated to ${type}`);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const handleDrmOverrideChange = (type) => {
 | 
				
			||||||
 | 
					        chrome.storage.local.set({ drm_override: type }, () => {
 | 
				
			||||||
 | 
					            if (chrome.runtime.lastError) {
 | 
				
			||||||
 | 
					                console.error("Error updating drm_override:", chrome.runtime.lastError);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                setDrmOverride(type);
 | 
				
			||||||
 | 
					                console.log(`DRM Override updated to ${type}`);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <div className="flex flex-row">
 | 
				
			||||||
 | 
					            <div className="mr-2 ml-auto flex h-full flex-row items-center justify-center">
 | 
				
			||||||
 | 
					                <p className="mr-2 p-2 text-lg text-nowrap">Injection type:</p>
 | 
				
			||||||
 | 
					                <div role="tablist" className="tabs tabs-border">
 | 
				
			||||||
 | 
					                    <a
 | 
				
			||||||
 | 
					                        role="tab"
 | 
				
			||||||
 | 
					                        className={`tab ${injectionType === "LICENSE" ? "tab-active font-semibold" : ""}`}
 | 
				
			||||||
 | 
					                        onClick={() => handleInjectionTypeChange("LICENSE")}
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
 | 
					                        License
 | 
				
			||||||
 | 
					                    </a>
 | 
				
			||||||
 | 
					                    <a
 | 
				
			||||||
 | 
					                        role="tab"
 | 
				
			||||||
 | 
					                        className={`tab ${injectionType === "EME" ? "tab-active font-semibold" : ""}`}
 | 
				
			||||||
 | 
					                        onClick={() => handleInjectionTypeChange("EME")}
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
 | 
					                        EME
 | 
				
			||||||
 | 
					                    </a>
 | 
				
			||||||
 | 
					                    <a
 | 
				
			||||||
 | 
					                        role="tab"
 | 
				
			||||||
 | 
					                        className={`tab ${injectionType === "DISABLED" ? "tab-active font-semibold" : ""}`}
 | 
				
			||||||
 | 
					                        onClick={() => handleInjectionTypeChange("DISABLED")}
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
 | 
					                        Disabled
 | 
				
			||||||
 | 
					                    </a>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default InjectionMenu;
 | 
				
			||||||
@ -1,6 +1,9 @@
 | 
				
			|||||||
import React, { useEffect, useState } from "react";
 | 
					import React, { useEffect, useState } from "react";
 | 
				
			||||||
 | 
					import { IoCameraOutline, IoCopyOutline, IoSaveOutline } from "react-icons/io5";
 | 
				
			||||||
 | 
					import { toast } from "sonner";
 | 
				
			||||||
 | 
					import InjectionMenu from "./injectionmenu";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Results() {
 | 
					const Results = () => {
 | 
				
			||||||
    const [drmType, setDrmType] = useState("");
 | 
					    const [drmType, setDrmType] = useState("");
 | 
				
			||||||
    const [pssh, setPssh] = useState("");
 | 
					    const [pssh, setPssh] = useState("");
 | 
				
			||||||
    const [licenseUrl, setLicenseUrl] = useState("");
 | 
					    const [licenseUrl, setLicenseUrl] = useState("");
 | 
				
			||||||
@ -118,6 +121,11 @@ function Results() {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const handleCopyToClipboard = (text, label) => {
 | 
				
			||||||
 | 
					        navigator.clipboard.writeText(text);
 | 
				
			||||||
 | 
					        toast.success(`Copied ${label} to clipboard`);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Check if current tab is YouTube
 | 
					    // Check if current tab is YouTube
 | 
				
			||||||
    const isYouTube = () => {
 | 
					    const isYouTube = () => {
 | 
				
			||||||
        return currentTabUrl.includes("youtube.com") || currentTabUrl.includes("youtu.be");
 | 
					        return currentTabUrl.includes("youtube.com") || currentTabUrl.includes("youtu.be");
 | 
				
			||||||
@ -177,74 +185,137 @@ function Results() {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <div className="w-full grow flex h-full overflow-y-auto overflow-x-auto flex-col text-white p-4">
 | 
					        <div className="flex h-full w-full flex-col gap-4">
 | 
				
			||||||
            <button
 | 
					            <InjectionMenu />
 | 
				
			||||||
                onClick={handleCapture}
 | 
					            <button onClick={handleCapture} className="btn btn-primary">
 | 
				
			||||||
                className="w-full h-10 bg-sky-500 rounded-md p-2 mt-2 text-white cursor-pointer font-bold hover:bg-sky-600"
 | 
					                <IoCameraOutline className="h-5 w-5" />
 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
                Capture current tab
 | 
					                Capture current tab
 | 
				
			||||||
            </button>
 | 
					            </button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <p className="text-2xl mt-5">DRM Type</p>
 | 
					            <fieldset className="fieldset">
 | 
				
			||||||
            <input
 | 
					                <legend className="fieldset-legend text-base">DRM Type</legend>
 | 
				
			||||||
                type="text"
 | 
					                <input
 | 
				
			||||||
                value={drmType}
 | 
					                    type="text"
 | 
				
			||||||
                className="w-full h-10 bg-slate-800/50 rounded-md p-2 mt-2 text-white font-mono"
 | 
					                    value={drmType}
 | 
				
			||||||
                placeholder="[Not available]"
 | 
					                    className="input w-full font-mono"
 | 
				
			||||||
                disabled
 | 
					                    placeholder="[Not available]"
 | 
				
			||||||
            />
 | 
					                    disabled
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					            </fieldset>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <p className="text-2xl mt-5">Manifest URL</p>
 | 
					            <fieldset className="fieldset">
 | 
				
			||||||
            <input
 | 
					                <legend className="fieldset-legend text-base">Manifest URL</legend>
 | 
				
			||||||
                type="text"
 | 
					                <input
 | 
				
			||||||
                value={getManifestDisplayValue()}
 | 
					                    type="text"
 | 
				
			||||||
                className={`w-full h-10 bg-slate-800/50 rounded-md p-2 mt-2 font-mono ${
 | 
					                    value={getManifestDisplayValue()}
 | 
				
			||||||
                    isYouTube() && !manifestUrl ? "text-yellow-400" : "text-white"
 | 
					                    className={`input w-full font-mono ${
 | 
				
			||||||
                }`}
 | 
					                        isYouTube() && !manifestUrl ? "text-yellow-400" : ""
 | 
				
			||||||
                placeholder={getManifestPlaceholder()}
 | 
					                    }`}
 | 
				
			||||||
                disabled
 | 
					                    placeholder={getManifestPlaceholder()}
 | 
				
			||||||
            />
 | 
					                    disabled
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
            <p className="text-2xl mt-5">PSSH</p>
 | 
					                {!isYouTube() && manifestUrl && (
 | 
				
			||||||
            <input
 | 
					                    <p className="label flex justify-end">
 | 
				
			||||||
                type="text"
 | 
					                        <button
 | 
				
			||||||
                value={pssh}
 | 
					                            className="btn btn-link btn-sm text-info"
 | 
				
			||||||
                className="w-full h-10 bg-slate-800/50 rounded-md p-2 mt-2 text-white font-mono"
 | 
					                            onClick={() => handleCopyToClipboard(manifestUrl, "manifest URL")}
 | 
				
			||||||
                placeholder="[Not available]"
 | 
					                        >
 | 
				
			||||||
                disabled
 | 
					                            <IoCopyOutline className="h-5 w-5" />
 | 
				
			||||||
            />
 | 
					                            Copy manifest URL
 | 
				
			||||||
 | 
					                        </button>
 | 
				
			||||||
            <p className="text-2xl mt-5">License URL</p>
 | 
					                    </p>
 | 
				
			||||||
            <input
 | 
					 | 
				
			||||||
                type="text"
 | 
					 | 
				
			||||||
                value={licenseUrl}
 | 
					 | 
				
			||||||
                className="w-full h-10 bg-slate-800/50 rounded-md p-2 mt-2 text-white font-mono"
 | 
					 | 
				
			||||||
                placeholder="[Not available]"
 | 
					 | 
				
			||||||
                disabled
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <p className="text-2xl mt-5">Keys</p>
 | 
					 | 
				
			||||||
            <div className="w-full min-h-64 h-64 flex items-center justify-center text-center overflow-y-auto bg-slate-800/50 rounded-md p-2 mt-2 text-white whitespace-pre-line font-mono">
 | 
					 | 
				
			||||||
                {Array.isArray(keys) && keys.filter((k) => k.type !== "SIGNING").length > 0 ? (
 | 
					 | 
				
			||||||
                    keys
 | 
					 | 
				
			||||||
                        .filter((k) => k.type !== "SIGNING")
 | 
					 | 
				
			||||||
                        .map((k) => `${k.key_id || k.keyId}:${k.key}`)
 | 
					 | 
				
			||||||
                        .join("\n")
 | 
					 | 
				
			||||||
                ) : (
 | 
					 | 
				
			||||||
                    <span className="text-gray-400">[Not available]</span>
 | 
					 | 
				
			||||||
                )}
 | 
					                )}
 | 
				
			||||||
            </div>
 | 
					            </fieldset>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <fieldset className="fieldset">
 | 
				
			||||||
 | 
					                <legend className="fieldset-legend text-base">PSSH</legend>
 | 
				
			||||||
 | 
					                <input
 | 
				
			||||||
 | 
					                    type="text"
 | 
				
			||||||
 | 
					                    value={pssh}
 | 
				
			||||||
 | 
					                    className="input w-full font-mono"
 | 
				
			||||||
 | 
					                    placeholder="[Not available]"
 | 
				
			||||||
 | 
					                    disabled
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					                {pssh && (
 | 
				
			||||||
 | 
					                    <p className="label flex justify-end">
 | 
				
			||||||
 | 
					                        <button
 | 
				
			||||||
 | 
					                            className="btn btn-link btn-sm text-info"
 | 
				
			||||||
 | 
					                            onClick={() => handleCopyToClipboard(pssh, "PSSH")}
 | 
				
			||||||
 | 
					                        >
 | 
				
			||||||
 | 
					                            <IoCopyOutline className="h-5 w-5" />
 | 
				
			||||||
 | 
					                            Copy PSSH
 | 
				
			||||||
 | 
					                        </button>
 | 
				
			||||||
 | 
					                    </p>
 | 
				
			||||||
 | 
					                )}
 | 
				
			||||||
 | 
					            </fieldset>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <fieldset className="fieldset">
 | 
				
			||||||
 | 
					                <legend className="fieldset-legend text-base">License URL</legend>
 | 
				
			||||||
 | 
					                <input
 | 
				
			||||||
 | 
					                    type="text"
 | 
				
			||||||
 | 
					                    value={licenseUrl}
 | 
				
			||||||
 | 
					                    className="input w-full font-mono"
 | 
				
			||||||
 | 
					                    placeholder="[Not available]"
 | 
				
			||||||
 | 
					                    disabled
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					                {licenseUrl && (
 | 
				
			||||||
 | 
					                    <p className="label flex justify-end">
 | 
				
			||||||
 | 
					                        <button
 | 
				
			||||||
 | 
					                            className="btn btn-link btn-sm text-info"
 | 
				
			||||||
 | 
					                            onClick={() => handleCopyToClipboard(licenseUrl, "license URL")}
 | 
				
			||||||
 | 
					                        >
 | 
				
			||||||
 | 
					                            <IoCopyOutline className="h-5 w-5" />
 | 
				
			||||||
 | 
					                            Copy license URL
 | 
				
			||||||
 | 
					                        </button>
 | 
				
			||||||
 | 
					                    </p>
 | 
				
			||||||
 | 
					                )}
 | 
				
			||||||
 | 
					            </fieldset>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <fieldset className="fieldset">
 | 
				
			||||||
 | 
					                <legend className="fieldset-legend text-base">Keys</legend>
 | 
				
			||||||
 | 
					                <textarea
 | 
				
			||||||
 | 
					                    value={
 | 
				
			||||||
 | 
					                        Array.isArray(keys) && keys.filter((k) => k.type !== "SIGNING").length > 0
 | 
				
			||||||
 | 
					                            ? keys
 | 
				
			||||||
 | 
					                                  .filter((k) => k.type !== "SIGNING")
 | 
				
			||||||
 | 
					                                  .map((k) => `${k.key_id || k.keyId}:${k.key}`)
 | 
				
			||||||
 | 
					                                  .join("\n")
 | 
				
			||||||
 | 
					                            : "[Not available]"
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    className="textarea w-full font-mono"
 | 
				
			||||||
 | 
					                    disabled
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					                {hasData() &&
 | 
				
			||||||
 | 
					                    Array.isArray(keys) &&
 | 
				
			||||||
 | 
					                    keys.filter((k) => k.type !== "SIGNING").length > 0 && (
 | 
				
			||||||
 | 
					                        <p className="label flex justify-end">
 | 
				
			||||||
 | 
					                            <button
 | 
				
			||||||
 | 
					                                className="btn btn-link btn-sm text-info"
 | 
				
			||||||
 | 
					                                onClick={() =>
 | 
				
			||||||
 | 
					                                    handleCopyToClipboard(
 | 
				
			||||||
 | 
					                                        keys
 | 
				
			||||||
 | 
					                                            .filter((k) => k.type !== "SIGNING")
 | 
				
			||||||
 | 
					                                            .map((k) => `${k.key_id || k.keyId}:${k.key}`)
 | 
				
			||||||
 | 
					                                            .join("\n"),
 | 
				
			||||||
 | 
					                                        "keys"
 | 
				
			||||||
 | 
					                                    )
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            >
 | 
				
			||||||
 | 
					                                <IoCopyOutline className="h-5 w-5" />
 | 
				
			||||||
 | 
					                                Copy keys
 | 
				
			||||||
 | 
					                            </button>
 | 
				
			||||||
 | 
					                        </p>
 | 
				
			||||||
 | 
					                    )}
 | 
				
			||||||
 | 
					            </fieldset>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            {hasData() && (
 | 
					            {hasData() && (
 | 
				
			||||||
                <button
 | 
					                <button onClick={handleExportJSON} className="btn btn-success">
 | 
				
			||||||
                    onClick={handleExportJSON}
 | 
					                    <IoSaveOutline className="h-5 w-5" />
 | 
				
			||||||
                    className="w-full h-10 bg-green-500 rounded-md p-2 mt-5 text-white cursor-pointer font-bold hover:bg-green-600"
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                    Export as JSON
 | 
					                    Export as JSON
 | 
				
			||||||
                </button>
 | 
					                </button>
 | 
				
			||||||
            )}
 | 
					            )}
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Results;
 | 
					export default Results;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,11 @@
 | 
				
			|||||||
import { useState, useEffect } from "react";
 | 
					import { useEffect, useState } from "react";
 | 
				
			||||||
 | 
					import { IoSaveOutline } from "react-icons/io5";
 | 
				
			||||||
import { useNavigate } from "react-router-dom";
 | 
					import { useNavigate } from "react-router-dom";
 | 
				
			||||||
 | 
					import { toast } from "sonner";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Settings({ onConfigSaved }) {
 | 
					const Settings = ({ onConfigSaved }) => {
 | 
				
			||||||
    const [instanceUrl, setInstanceUrl] = useState("");
 | 
					    const [instanceUrl, setInstanceUrl] = useState("");
 | 
				
			||||||
    const [storedUrl, setStoredUrl] = useState(null);
 | 
					    const [storedUrl, setStoredUrl] = useState(null);
 | 
				
			||||||
    const [message, setMessage] = useState(null);
 | 
					 | 
				
			||||||
    const [messageType, setMessageType] = useState(null);
 | 
					 | 
				
			||||||
    const [loading, setLoading] = useState(false);
 | 
					    const [loading, setLoading] = useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const navigate = useNavigate();
 | 
					    const navigate = useNavigate();
 | 
				
			||||||
@ -13,6 +13,7 @@ function Settings({ onConfigSaved }) {
 | 
				
			|||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        chrome.storage.local.get("cdrm_instance", (result) => {
 | 
					        chrome.storage.local.get("cdrm_instance", (result) => {
 | 
				
			||||||
            if (chrome.runtime.lastError) {
 | 
					            if (chrome.runtime.lastError) {
 | 
				
			||||||
 | 
					                toast.error("Error fetching CDRM instance:", chrome.runtime.lastError);
 | 
				
			||||||
                console.error("Error fetching CDRM instance:", chrome.runtime.lastError);
 | 
					                console.error("Error fetching CDRM instance:", chrome.runtime.lastError);
 | 
				
			||||||
            } else if (result.cdrm_instance) {
 | 
					            } else if (result.cdrm_instance) {
 | 
				
			||||||
                setStoredUrl(result.cdrm_instance);
 | 
					                setStoredUrl(result.cdrm_instance);
 | 
				
			||||||
@ -23,14 +24,12 @@ function Settings({ onConfigSaved }) {
 | 
				
			|||||||
    const handleSave = async () => {
 | 
					    const handleSave = async () => {
 | 
				
			||||||
        const trimmedUrl = instanceUrl.trim().replace(/\/+$/, "");
 | 
					        const trimmedUrl = instanceUrl.trim().replace(/\/+$/, "");
 | 
				
			||||||
        if (!trimmedUrl) {
 | 
					        if (!trimmedUrl) {
 | 
				
			||||||
            setMessage("Please enter a valid URL.");
 | 
					            toast.error("Please enter a valid URL.");
 | 
				
			||||||
            setMessageType("error");
 | 
					 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const endpoint = trimmedUrl + "/api/extension";
 | 
					        const endpoint = trimmedUrl + "/api/extension";
 | 
				
			||||||
        setLoading(true);
 | 
					        setLoading(true);
 | 
				
			||||||
        setMessage(null);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            const response = await fetch(endpoint, {
 | 
					            const response = await fetch(endpoint, {
 | 
				
			||||||
@ -43,15 +42,24 @@ function Settings({ onConfigSaved }) {
 | 
				
			|||||||
            const data = await response.json();
 | 
					            const data = await response.json();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (data.status === true) {
 | 
					            if (data.status === true) {
 | 
				
			||||||
                setMessage("Successfully connected to CDRM Instance.");
 | 
					                toast.success("Successfully connected to a CDRM instance");
 | 
				
			||||||
                setMessageType("success");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                const widevineRes = await fetch(`${trimmedUrl}/remotecdm/widevine/deviceinfo`);
 | 
					                const widevineRes = await fetch(`${trimmedUrl}/remotecdm/widevine/deviceinfo`);
 | 
				
			||||||
                if (!widevineRes.ok) throw new Error("Failed to fetch Widevine device info");
 | 
					                if (!widevineRes.ok) {
 | 
				
			||||||
 | 
					                    toast.error(
 | 
				
			||||||
 | 
					                        `Failed to fetch Widevine device info. Reason: ${widevineRes.statusText}`
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                const widevineData = await widevineRes.json();
 | 
					                const widevineData = await widevineRes.json();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                const playreadyRes = await fetch(`${trimmedUrl}/remotecdm/playready/deviceinfo`);
 | 
					                const playreadyRes = await fetch(`${trimmedUrl}/remotecdm/playready/deviceinfo`);
 | 
				
			||||||
                if (!playreadyRes.ok) throw new Error("Failed to fetch PlayReady device info");
 | 
					                if (!playreadyRes.ok) {
 | 
				
			||||||
 | 
					                    toast.error(
 | 
				
			||||||
 | 
					                        `Failed to fetch PlayReady device info. Reason: ${playreadyRes.statusText}`
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                const playreadyData = await playreadyRes.json();
 | 
					                const playreadyData = await playreadyRes.json();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                chrome.storage.local.set(
 | 
					                chrome.storage.local.set(
 | 
				
			||||||
@ -79,10 +87,11 @@ function Settings({ onConfigSaved }) {
 | 
				
			|||||||
                                "Error saving to chrome.storage:",
 | 
					                                "Error saving to chrome.storage:",
 | 
				
			||||||
                                chrome.runtime.lastError
 | 
					                                chrome.runtime.lastError
 | 
				
			||||||
                            );
 | 
					                            );
 | 
				
			||||||
                            setMessage("Error saving configuration.");
 | 
					                            toast.error(
 | 
				
			||||||
                            setMessageType("error");
 | 
					                                `Error saving configuration. Reason: ${chrome.runtime.lastError}`
 | 
				
			||||||
 | 
					                            );
 | 
				
			||||||
                        } else {
 | 
					                        } else {
 | 
				
			||||||
                            console.log("Configuration saved.");
 | 
					                            console.log("Configuration saved");
 | 
				
			||||||
                            setStoredUrl(trimmedUrl);
 | 
					                            setStoredUrl(trimmedUrl);
 | 
				
			||||||
                            setInstanceUrl("");
 | 
					                            setInstanceUrl("");
 | 
				
			||||||
                            if (onConfigSaved) onConfigSaved();
 | 
					                            if (onConfigSaved) onConfigSaved();
 | 
				
			||||||
@ -91,54 +100,56 @@ function Settings({ onConfigSaved }) {
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                throw new Error("Invalid response from endpoint.");
 | 
					                toast.error("Invalid response from endpoint.");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } catch (err) {
 | 
					        } catch (err) {
 | 
				
			||||||
            console.error("Connection error:", err);
 | 
					            console.error("Connection error:", err);
 | 
				
			||||||
            setMessage("Invalid endpoint or device info could not be retrieved.");
 | 
					            toast.error(
 | 
				
			||||||
            setMessageType("error");
 | 
					                `Invalid endpoint or device info could not be retrieved. Reason: ${err.message}`
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
        } finally {
 | 
					        } finally {
 | 
				
			||||||
            setLoading(false);
 | 
					            setLoading(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <div className="w-full h-full overflow-y-auto overflow-x-auto flex flex-col p-4">
 | 
					        <div className="flex h-full w-full flex-col gap-4">
 | 
				
			||||||
            {storedUrl && (
 | 
					            {storedUrl && (
 | 
				
			||||||
                <p className="text-gray-300 mb-2">
 | 
					                <p className="mb-2 text-base">
 | 
				
			||||||
                    Current instance: <span className="text-white font-semibold">{storedUrl}</span>
 | 
					                    Current instance: <span className="font-mono font-semibold">{storedUrl}</span>
 | 
				
			||||||
                </p>
 | 
					                </p>
 | 
				
			||||||
            )}
 | 
					            )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <p className="mt-3 text-white">New instance URL:</p>
 | 
					            <fieldset className="fieldset">
 | 
				
			||||||
            <input
 | 
					                <legend className="fieldset-legend text-base">New instance URL</legend>
 | 
				
			||||||
                type="text"
 | 
					                <input
 | 
				
			||||||
                value={instanceUrl}
 | 
					                    type="text"
 | 
				
			||||||
                onChange={(e) => setInstanceUrl(e.target.value)}
 | 
					                    value={instanceUrl}
 | 
				
			||||||
                placeholder="https://cdrm-project.com/, http://127.0.0.1:5000/"
 | 
					                    onChange={(e) => setInstanceUrl(e.target.value)}
 | 
				
			||||||
                className="w-full p-4 text-lg bg-gray-800 text-white border border-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 mt-4 font-mono"
 | 
					                    placeholder="https://cdrm-project.com/, http://127.0.0.1:5000/"
 | 
				
			||||||
            />
 | 
					                    className="input w-full font-mono"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					            </fieldset>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <button
 | 
					            <button
 | 
				
			||||||
 | 
					                type="button"
 | 
				
			||||||
                onClick={handleSave}
 | 
					                onClick={handleSave}
 | 
				
			||||||
                disabled={loading}
 | 
					                disabled={loading}
 | 
				
			||||||
                className={`mt-4 p-2 font-bold ${
 | 
					                className="btn btn-primary btn-block"
 | 
				
			||||||
                    loading ? "bg-blue-400" : "bg-blue-600 hover:bg-blue-700"
 | 
					 | 
				
			||||||
                } text-white rounded-md transition duration-300`}
 | 
					 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
                {loading ? "Connecting..." : "Save settings"}
 | 
					                {loading ? (
 | 
				
			||||||
 | 
					                    <>
 | 
				
			||||||
 | 
					                        <span className="loading loading-spinner loading-sm"></span> Connecting...
 | 
				
			||||||
 | 
					                    </>
 | 
				
			||||||
 | 
					                ) : (
 | 
				
			||||||
 | 
					                    <>
 | 
				
			||||||
 | 
					                        <IoSaveOutline className="h-5 w-5" />
 | 
				
			||||||
 | 
					                        Save settings
 | 
				
			||||||
 | 
					                    </>
 | 
				
			||||||
 | 
					                )}
 | 
				
			||||||
            </button>
 | 
					            </button>
 | 
				
			||||||
 | 
					 | 
				
			||||||
            {message && (
 | 
					 | 
				
			||||||
                <p
 | 
					 | 
				
			||||||
                    className={`mt-2 text-sm text-center ${
 | 
					 | 
				
			||||||
                        messageType === "success" ? "text-green-400" : "text-red-400"
 | 
					 | 
				
			||||||
                    }`}
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                    {message}
 | 
					 | 
				
			||||||
                </p>
 | 
					 | 
				
			||||||
            )}
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Settings;
 | 
					export default Settings;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,48 +0,0 @@
 | 
				
			|||||||
import { NavLink } from "react-router-dom";
 | 
					 | 
				
			||||||
import homeIcon from "../assets/home.svg";
 | 
					 | 
				
			||||||
import settingsIcon from "../assets/settings.svg";
 | 
					 | 
				
			||||||
import closeIcon from "../assets/close.svg";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function SideNav({ onClose }) {
 | 
					 | 
				
			||||||
    return (
 | 
					 | 
				
			||||||
        <div className="w-full h-full overflow-y-auto overflow-x-auto flex flex-col bg-black">
 | 
					 | 
				
			||||||
            <div className="w-full min-h-16 max-h-16 h-16 shrink-0 flex sticky top-0 z-20 border-b border-b-white bg-black">
 | 
					 | 
				
			||||||
                <button onClick={onClose} className="h-full ml-auto p-3 hover:cursor-pointer">
 | 
					 | 
				
			||||||
                    <img src={closeIcon} alt="Close" className="h-full" />
 | 
					 | 
				
			||||||
                </button>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <div className="w-full h-16 flex items-center justify-center mt-2">
 | 
					 | 
				
			||||||
                <NavLink
 | 
					 | 
				
			||||||
                    to="/results"
 | 
					 | 
				
			||||||
                    onClick={onClose}
 | 
					 | 
				
			||||||
                    className="text-white text-2xl font-bold flex flex-row items-center border-l-white hover:border-l-1 w-full hover:bg-black/50 transition duration-300 ease-in-out p-2"
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                    <img
 | 
					 | 
				
			||||||
                        src={homeIcon}
 | 
					 | 
				
			||||||
                        alt="Home"
 | 
					 | 
				
			||||||
                        className="h-full w-16 p-2 flex items-center cursor-pointer"
 | 
					 | 
				
			||||||
                    />
 | 
					 | 
				
			||||||
                    Home
 | 
					 | 
				
			||||||
                </NavLink>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <div className="w-full h-16 flex items-center justify-center mt-2">
 | 
					 | 
				
			||||||
                <NavLink
 | 
					 | 
				
			||||||
                    to="/settings"
 | 
					 | 
				
			||||||
                    onClick={onClose}
 | 
					 | 
				
			||||||
                    className="text-white text-2xl font-bold flex flex-row items-center hover:border-l-1 border-l-white w-full hover:bg-black/50 transition duration-300 ease-in-out p-2"
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                    <img
 | 
					 | 
				
			||||||
                        src={settingsIcon}
 | 
					 | 
				
			||||||
                        alt="Settings"
 | 
					 | 
				
			||||||
                        className="h-full w-16 p-2 flex items-center cursor-pointer"
 | 
					 | 
				
			||||||
                    />
 | 
					 | 
				
			||||||
                    Settings
 | 
					 | 
				
			||||||
                </NavLink>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default SideNav;
 | 
					 | 
				
			||||||
							
								
								
									
										51
									
								
								frontend/src/components/tabnavigation.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								frontend/src/components/tabnavigation.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					import { IoIosInformationCircleOutline } from "react-icons/io";
 | 
				
			||||||
 | 
					import { IoHomeOutline, IoSettingsOutline } from "react-icons/io5";
 | 
				
			||||||
 | 
					import { NavLink, useLocation } from "react-router-dom";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const TabNavigation = ({ validConfig }) => {
 | 
				
			||||||
 | 
					    const location = useLocation();
 | 
				
			||||||
 | 
					    const activeTab =
 | 
				
			||||||
 | 
					        location.pathname === "/settings"
 | 
				
			||||||
 | 
					            ? "settings"
 | 
				
			||||||
 | 
					            : location.pathname === "/about"
 | 
				
			||||||
 | 
					              ? "about"
 | 
				
			||||||
 | 
					              : "main";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <div className="mb-4 flex items-center justify-center">
 | 
				
			||||||
 | 
					            <div role="tablist" className="tabs tabs-box">
 | 
				
			||||||
 | 
					                <NavLink
 | 
				
			||||||
 | 
					                    role="tab"
 | 
				
			||||||
 | 
					                    to="/results"
 | 
				
			||||||
 | 
					                    className={`tab ${!validConfig ? "cursor-not-allowed" : activeTab === "main" ? "tab-active font-semibold" : ""}`}
 | 
				
			||||||
 | 
					                    onClick={(e) => {
 | 
				
			||||||
 | 
					                        if (!validConfig) {
 | 
				
			||||||
 | 
					                            e.preventDefault();
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                    <IoHomeOutline className="mr-1 h-5 w-5" />
 | 
				
			||||||
 | 
					                    Main
 | 
				
			||||||
 | 
					                </NavLink>
 | 
				
			||||||
 | 
					                <NavLink
 | 
				
			||||||
 | 
					                    role="tab"
 | 
				
			||||||
 | 
					                    to="/settings"
 | 
				
			||||||
 | 
					                    className={`tab ${activeTab === "settings" ? "tab-active font-semibold" : ""}`}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                    <IoSettingsOutline className="mr-1 h-5 w-5" />
 | 
				
			||||||
 | 
					                    Settings
 | 
				
			||||||
 | 
					                </NavLink>
 | 
				
			||||||
 | 
					                <NavLink
 | 
				
			||||||
 | 
					                    role="tab"
 | 
				
			||||||
 | 
					                    to="/about"
 | 
				
			||||||
 | 
					                    className={`tab ${activeTab === "about" ? "tab-active font-semibold" : ""}`}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                    <IoIosInformationCircleOutline className="mr-1 h-5 w-5" />
 | 
				
			||||||
 | 
					                    About
 | 
				
			||||||
 | 
					                </NavLink>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default TabNavigation;
 | 
				
			||||||
@ -1,82 +0,0 @@
 | 
				
			|||||||
import { useEffect, useState } from "react";
 | 
					 | 
				
			||||||
import hamburgerIcon from "../assets/hamburger.svg";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function TopNav({ onMenuClick }) {
 | 
					 | 
				
			||||||
    const [injectionType, setInjectionType] = useState("LICENSE");
 | 
					 | 
				
			||||||
    const [drmOverride, setDrmOverride] = useState("DISABLED");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    useEffect(() => {
 | 
					 | 
				
			||||||
        chrome.storage.local.get(["injection_type", "drm_override"], (result) => {
 | 
					 | 
				
			||||||
            if (result.injection_type !== undefined) {
 | 
					 | 
				
			||||||
                setInjectionType(result.injection_type);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (result.drm_override !== undefined) {
 | 
					 | 
				
			||||||
                setDrmOverride(result.drm_override);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }, []);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const handleInjectionTypeChange = (type) => {
 | 
					 | 
				
			||||||
        chrome.storage.local.set({ injection_type: type }, () => {
 | 
					 | 
				
			||||||
            if (chrome.runtime.lastError) {
 | 
					 | 
				
			||||||
                console.error("Error updating injection_type:", chrome.runtime.lastError);
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                setInjectionType(type);
 | 
					 | 
				
			||||||
                console.log(`Injection type updated to ${type}`);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const handleDrmOverrideChange = (type) => {
 | 
					 | 
				
			||||||
        chrome.storage.local.set({ drm_override: type }, () => {
 | 
					 | 
				
			||||||
            if (chrome.runtime.lastError) {
 | 
					 | 
				
			||||||
                console.error("Error updating drm_override:", chrome.runtime.lastError);
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                setDrmOverride(type);
 | 
					 | 
				
			||||||
                console.log(`DRM Override updated to ${type}`);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return (
 | 
					 | 
				
			||||||
        <div className="w-full h-full flex flex-row overflow-x-hidden">
 | 
					 | 
				
			||||||
            <img
 | 
					 | 
				
			||||||
                src={hamburgerIcon}
 | 
					 | 
				
			||||||
                alt="Menu"
 | 
					 | 
				
			||||||
                className="h-full w-16 p-2 flex items-center cursor-pointer"
 | 
					 | 
				
			||||||
                onClick={onMenuClick}
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
            <div className="flex flex-row h-full justify-center items-center ml-auto mr-2">
 | 
					 | 
				
			||||||
                <p className="text-white text-lg p-2 mr-2 border-r-2 border-r-white text-nowrap">
 | 
					 | 
				
			||||||
                    Injection Type:
 | 
					 | 
				
			||||||
                </p>
 | 
					 | 
				
			||||||
                <button
 | 
					 | 
				
			||||||
                    onClick={() => handleInjectionTypeChange("LICENSE")}
 | 
					 | 
				
			||||||
                    className={`text-white text-lg p-2 rounded-md m-1 cursor-pointer ${
 | 
					 | 
				
			||||||
                        injectionType === "LICENSE" ? "bg-sky-500/70" : "bg-black"
 | 
					 | 
				
			||||||
                    }`}
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                    License
 | 
					 | 
				
			||||||
                </button>
 | 
					 | 
				
			||||||
                <button
 | 
					 | 
				
			||||||
                    onClick={() => handleInjectionTypeChange("EME")}
 | 
					 | 
				
			||||||
                    className={`text-white text-lg p-2 rounded-md m-1 cursor-pointer ${
 | 
					 | 
				
			||||||
                        injectionType === "EME" ? "bg-green-500/70" : "bg-black"
 | 
					 | 
				
			||||||
                    }`}
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                    EME
 | 
					 | 
				
			||||||
                </button>
 | 
					 | 
				
			||||||
                <button
 | 
					 | 
				
			||||||
                    onClick={() => handleInjectionTypeChange("DISABLED")}
 | 
					 | 
				
			||||||
                    className={`text-white text-lg p-2 rounded-md m-1 cursor-pointer ${
 | 
					 | 
				
			||||||
                        injectionType === "DISABLED" ? "bg-red-500/70" : "bg-black"
 | 
					 | 
				
			||||||
                    }`}
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                    Disabled
 | 
					 | 
				
			||||||
                </button>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default TopNav;
 | 
					 | 
				
			||||||
@ -1,10 +1,35 @@
 | 
				
			|||||||
@import "tailwindcss";
 | 
					@import "tailwindcss";
 | 
				
			||||||
 | 
					@plugin "daisyui";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@plugin "daisyui/theme" {
 | 
				
			||||||
 | 
					    name: "dim";
 | 
				
			||||||
 | 
					    default: true;
 | 
				
			||||||
 | 
					    prefersdark: true;
 | 
				
			||||||
 | 
					    color-scheme: "dark";
 | 
				
			||||||
 | 
					    --radius-selector: 0.5rem;
 | 
				
			||||||
 | 
					    --radius-field: 0.5rem;
 | 
				
			||||||
 | 
					    --radius-box: 0.5rem;
 | 
				
			||||||
 | 
					    --size-selector: 0.25rem;
 | 
				
			||||||
 | 
					    --size-field: 0.25rem;
 | 
				
			||||||
 | 
					    --depth: 0;
 | 
				
			||||||
 | 
					    --noise: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					:root {
 | 
				
			||||||
 | 
					    --font-default-sans:
 | 
				
			||||||
 | 
					        "Inter", system-ui, -apple-system, Roboto, "Segoe UI", "Helvetica Neue", "Noto Sans",
 | 
				
			||||||
 | 
					        Oxygen, Ubuntu, Cantarell, "Open Sans", Arial, sans-serif;
 | 
				
			||||||
 | 
					    font-family: var(--font-default-sans);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Force Sonner toast to use Inter first */
 | 
				
			||||||
 | 
					[data-sonner-toast],
 | 
				
			||||||
 | 
					.sonner-toast,
 | 
				
			||||||
 | 
					:where([data-sonner-toast]) :where([data-title]) :where([data-description]) {
 | 
				
			||||||
 | 
					    font-family: var(--font-default-sans) !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
html,
 | 
					html,
 | 
				
			||||||
body,
 | 
					body {
 | 
				
			||||||
#root {
 | 
					    font-family: var(--font-default-sans);
 | 
				
			||||||
    height: 100%;
 | 
					 | 
				
			||||||
    width: 100%;
 | 
					 | 
				
			||||||
    margin: 0;
 | 
					 | 
				
			||||||
    padding: 0;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,19 @@
 | 
				
			|||||||
import { StrictMode } from "react";
 | 
					import { StrictMode } from "react";
 | 
				
			||||||
import { createRoot } from "react-dom/client";
 | 
					import { createRoot } from "react-dom/client";
 | 
				
			||||||
import "./index.css";
 | 
					import { Toaster } from "sonner";
 | 
				
			||||||
import App from "./App.jsx";
 | 
					import App from "./App.jsx";
 | 
				
			||||||
 | 
					import "./assets/fonts/font-face.css";
 | 
				
			||||||
 | 
					import "./index.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
createRoot(document.getElementById("root")).render(
 | 
					createRoot(document.getElementById("root")).render(
 | 
				
			||||||
    <StrictMode>
 | 
					    <StrictMode>
 | 
				
			||||||
        <App />
 | 
					        <App />
 | 
				
			||||||
 | 
					        <Toaster
 | 
				
			||||||
 | 
					            richColors
 | 
				
			||||||
 | 
					            className="flex justify-center"
 | 
				
			||||||
 | 
					            position="bottom-center"
 | 
				
			||||||
 | 
					            duration="7000"
 | 
				
			||||||
 | 
					            theme="dark"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
    </StrictMode>
 | 
					    </StrictMode>
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
				
			|||||||
@ -291,7 +291,12 @@ class RemoteCDMBase {
 | 
				
			|||||||
        xhr.setRequestHeader("Content-Type", "application/json");
 | 
					        xhr.setRequestHeader("Content-Type", "application/json");
 | 
				
			||||||
        xhr.send(JSON.stringify(body));
 | 
					        xhr.send(JSON.stringify(body));
 | 
				
			||||||
        const jsonData = JSON.parse(xhr.responseText);
 | 
					        const jsonData = JSON.parse(xhr.responseText);
 | 
				
			||||||
        if (xhr.status === 200 || jsonData.status === "Success" || jsonData.message?.includes("parsed and loaded")) {
 | 
					        if (
 | 
				
			||||||
 | 
					            xhr.status === 200 ||
 | 
				
			||||||
 | 
					            jsonData.status === "Success" ||
 | 
				
			||||||
 | 
					            jsonData.status === 200 ||
 | 
				
			||||||
 | 
					            jsonData.message?.includes("parsed and loaded")
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
            logWithPrefix("License response parsed successfully");
 | 
					            logWithPrefix("License response parsed successfully");
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
@ -390,7 +395,7 @@ class remoteWidevineCDM extends RemoteCDMBase {
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
        xhr.send(JSON.stringify(body));
 | 
					        xhr.send(JSON.stringify(body));
 | 
				
			||||||
        const jsonData = JSON.parse(xhr.responseText);
 | 
					        const jsonData = JSON.parse(xhr.responseText);
 | 
				
			||||||
        if (xhr.status === 200 || jsonData.status === "Success") {
 | 
					        if (xhr.status === 200 || jsonData.status === "Success" || jsonData.status === 200) {
 | 
				
			||||||
            logWithPrefix("Service certificate set successfully");
 | 
					            logWithPrefix("Service certificate set successfully");
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            console.error("Failed to set service certificate:", jsonData.message);
 | 
					            console.error("Failed to set service certificate:", jsonData.message);
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user