diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..ee55b50 --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +# GitHub OAuth Secrets for LeetHub-3.0 +GITHUB_CLIENT_ID=your_client_id_here +GITHUB_CLIENT_SECRET=your_client_secret_here diff --git a/eslint.config.js b/eslint.config.js index d602196..2bcd839 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -7,6 +7,9 @@ import reactHooks from 'eslint-plugin-react-hooks'; import globals from 'globals'; export default [ + { + ignores: ['node_modules/**', 'dist/**', 'build/**', 'src/js/static/**', 'Original/**'], + }, js.configs.recommended, prettierConfig, { @@ -22,7 +25,6 @@ export default [ ...globals.browser, ...globals.node, ...globals.webextensions, - ...globals.jquery, }, }, rules: { @@ -39,6 +41,5 @@ export default [ version: 'detect', }, }, - ignores: ['node_modules/', 'dist/', 'build/'], }, ]; diff --git a/manifest.json b/manifest.json index 650391c..0dc6b24 100644 --- a/manifest.json +++ b/manifest.json @@ -21,6 +21,12 @@ "unlimitedStorage", "storage" ], + "host_permissions": [ + "https://api.github.com/*", + "https://github.com/*", + "https://leetcode.com/*", + "https://leetcode.cn/*" + ], "content_scripts": [ { "matches": [ diff --git a/package.json b/package.json index 0e2d7e0..abf720c 100644 --- a/package.json +++ b/package.json @@ -5,11 +5,12 @@ "private": true, "description": "Automatically integrate your code with LeetCode and GitHub", "scripts": { - "setup": "npm i", + "setup": "npm i && node setup-env.js", + "config": "node setup-env.js", "format": "prettier --write **/*.{js,jsx,ts,tsx,css,html}", "format-test": "prettier --check **/*.{js,jsx,ts,tsx,css,html}", - "lint": "eslint . --fix --ignore-pattern 'src/js/static/jquery-3.3.1.min.js' --ignore-pattern 'src/js/static/semantic-2.4.1.min.js'", - "lint-test": "eslint . --color --ignore-pattern 'src/js/static/jquery-3.3.1.min.js' --ignore-pattern 'src/js/static/semantic-2.4.1.min.js'" + "lint": "eslint . --fix", + "lint-test": "eslint . --color" }, "devDependencies": { "@eslint/js": "^9.23.0", diff --git a/setup-env.js b/setup-env.js new file mode 100644 index 0000000..29b3bf1 --- /dev/null +++ b/setup-env.js @@ -0,0 +1,102 @@ +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const ENV_PATH = path.join(__dirname, '.env'); +const ENV_EXAMPLE_PATH = path.join(__dirname, '.env.example'); +const AUTHORIZE_JS_PATH = path.join(__dirname, 'src', 'js', 'authorize.js'); +const OAUTH2_JS_PATH = path.join(__dirname, 'src', 'js', 'oauth2.js'); + +// Parse a .env file content +function parseEnv(content) { + const env = {}; + const lines = content.split('\n'); + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('#')) continue; + const index = trimmed.indexOf('='); + if (index === -1) continue; + const key = trimmed.substring(0, index).trim(); + let value = trimmed.substring(index + 1).trim(); + // Remove surrounding quotes if present + if ( + (value.startsWith('"') && value.endsWith('"')) || + (value.startsWith("'") && value.endsWith("'")) + ) { + value = value.substring(1, value.length - 1); + } + env[key] = value; + } + return env; +} + +// Extract current secrets from authorize.js +function extractSecrets() { + if (!fs.existsSync(AUTHORIZE_JS_PATH)) { + return { clientId: '', clientSecret: '' }; + } + const content = fs.readFileSync(AUTHORIZE_JS_PATH, 'utf8'); + const clientIdMatch = content.match(/this\.CLIENT_ID\s*=\s*['"]([^'"]*)['"]/); + const clientSecretMatch = content.match(/this\.CLIENT_SECRET\s*=\s*['"]([^'"]*)['"]/); + return { + clientId: clientIdMatch ? clientIdMatch[1] : '', + clientSecret: clientSecretMatch ? clientSecretMatch[1] : '', + }; +} + +// Update authorize.js and oauth2.js with secrets from env +function updateSecrets(clientId, clientSecret) { + if (fs.existsSync(AUTHORIZE_JS_PATH)) { + let content = fs.readFileSync(AUTHORIZE_JS_PATH, 'utf8'); + content = content.replace(/(this\.CLIENT_ID\s*=\s*['"])[^'"]*(['"])/, `$1${clientId}$2`); + content = content.replace( + /(this\.CLIENT_SECRET\s*=\s*['"])[^'"]*(['"])/, + `$1${clientSecret}$2`, + ); + fs.writeFileSync(AUTHORIZE_JS_PATH, content, 'utf8'); + console.log('Updated src/js/authorize.js'); + } + + if (fs.existsSync(OAUTH2_JS_PATH)) { + let content = fs.readFileSync(OAUTH2_JS_PATH, 'utf8'); + content = content.replace(/(const\s+CLIENT_ID\s*=\s*['"])[^'"]*(['"])/, `$1${clientId}$2`); + fs.writeFileSync(OAUTH2_JS_PATH, content, 'utf8'); + console.log('Updated src/js/oauth2.js'); + } +} + +function main() { + const current = extractSecrets(); + + // Create .env.example if it doesn't exist + if (!fs.existsSync(ENV_EXAMPLE_PATH)) { + const exampleContent = `# GitHub OAuth Secrets for LeetHub-3.0 +GITHUB_CLIENT_ID=your_client_id_here +GITHUB_CLIENT_SECRET=your_client_secret_here +`; + fs.writeFileSync(ENV_EXAMPLE_PATH, exampleContent, 'utf8'); + console.log('Created .env.example'); + } + + // Create .env with current values if it doesn't exist + if (!fs.existsSync(ENV_PATH)) { + const envContent = `# GitHub OAuth Secrets for LeetHub-3.0 +GITHUB_CLIENT_ID=${current.clientId || 'your_client_id_here'} +GITHUB_CLIENT_SECRET=${current.clientSecret || 'your_client_secret_here'} +`; + fs.writeFileSync(ENV_PATH, envContent, 'utf8'); + console.log('Created .env with existing credentials'); + } else { + // If it exists, read it and update files + const envContent = fs.readFileSync(ENV_PATH, 'utf8'); + const env = parseEnv(envContent); + const clientId = env.GITHUB_CLIENT_ID || current.clientId; + const clientSecret = env.GITHUB_CLIENT_SECRET || current.clientSecret; + updateSecrets(clientId, clientSecret); + } +} + +main(); diff --git a/src/css/popup.css b/src/css/popup.css index e928c03..9d8bcef 100644 --- a/src/css/popup.css +++ b/src/css/popup.css @@ -1,123 +1,634 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); + +/* ── Reset & Base ──────────────────────────────────────────── */ +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + body { - width: 350px; - background-color: #dfd9d99d !important; - padding: 10px; + width: 380px; + min-height: 200px; + background: #12131e; + color: #e2e4ea; + font-family: + 'Inter', + -apple-system, + BlinkMacSystemFont, + sans-serif; + font-size: 13px; + line-height: 1.5; + -webkit-font-smoothing: antialiased; +} + +/* ── Header ────────────────────────────────────────────────── */ +.popup-header { text-align: center; - transition: background-color 0.75s ease; + padding: 20px 20px 14px; + background: linear-gradient(135deg, #1a1b2e 0%, #0d1117 100%); + border-bottom: 1px solid rgba(255, 255, 255, 0.06); +} + +.popup-logo { + font-size: 26px; + font-weight: 700; + letter-spacing: -0.5px; + color: #ffffff; +} + +.popup-logo .accent { + color: #ff6c0a; +} + +.popup-tagline { + font-size: 12px; + color: #6b7280; + margin-top: 2px; +} + +/* ── User Info Bar ─────────────────────────────────────────── */ +.user-bar { + display: flex; + align-items: center; + gap: 10px; + padding: 12px 20px; + background: rgba(255, 255, 255, 0.03); + border-bottom: 1px solid rgba(255, 255, 255, 0.06); +} + +.user-avatar { + width: 32px; + height: 32px; + border-radius: 50%; + border: 2px solid #30363d; + object-fit: cover; + background: #21262d; +} + +.user-info { + flex: 1; + min-width: 0; +} + +.user-name { font-size: 13px; - font-family: 'Helvetica Neue', 'Lucida Grande', sans-serif; + font-weight: 600; + color: #e2e4ea; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } -#title { - font-family: 'Norwester', 'Helvetica Neue', 'Lucida Grande', sans-serif; - font-size: 30px; - font-weight: bold; - margin-bottom: 0px; - margin-top: 10px; +.user-repo { + font-size: 11px; + color: #58a6ff; + text-decoration: none; + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } -#caption { - font-family: 'Norwester', 'Helvetica Neue', 'Lucida Grande', sans-serif; - font-size: 14px; - font-weight: normal; - margin-bottom: 0px; +.user-repo:hover { + text-decoration: underline; } -.onboarding { - font-family: sans-serif; - border-top: solid black 1px; +.status-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: #3fb950; + box-shadow: 0 0 6px rgba(63, 185, 80, 0.5); + flex-shrink: 0; +} + +/* ── Stats Row ─────────────────────────────────────────────── */ +.stats-row { + display: flex; + gap: 8px; + padding: 14px 20px; + border-bottom: 1px solid rgba(255, 255, 255, 0.06); +} + +.stat-card { + flex: 1; + text-align: center; + padding: 10px 6px; + border-radius: 10px; + background: rgba(255, 255, 255, 0.04); + border: 1px solid rgba(255, 255, 255, 0.06); + transition: all 0.2s ease; +} + +.stat-card:hover { + background: rgba(255, 255, 255, 0.07); + transform: translateY(-1px); +} + +.stat-number { + font-size: 20px; + font-weight: 700; + display: block; +} + +.stat-label { + font-size: 10px; font-weight: 500; - padding-top: 2%; - font-size: medium; + text-transform: uppercase; + letter-spacing: 0.5px; + opacity: 0.7; + margin-top: 2px; + display: block; } -.collapsible-icon { +.stat-card.total .stat-number { + color: #e2e4ea; +} +.stat-card.easy .stat-number { + color: #3fb950; +} +.stat-card.medium .stat-number { + color: #d29922; +} +.stat-card.hard .stat-number { + color: #f85149; +} + +.stat-card.easy { + border-color: rgba(63, 185, 80, 0.15); +} +.stat-card.medium { + border-color: rgba(210, 153, 34, 0.15); +} +.stat-card.hard { + border-color: rgba(248, 81, 73, 0.15); +} + +/* ── Settings Section ──────────────────────────────────────── */ +.settings-section { + padding: 10px 20px 6px; +} + +.settings-title { font-size: 10px; - width: 20px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1px; + color: #6b7280; + margin-bottom: 8px; +} + +/* ── Setting Row ───────────────────────────────────────────── */ +.setting-row { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 12px; + margin-bottom: 4px; + border-radius: 8px; + background: rgba(255, 255, 255, 0.03); + border: 1px solid rgba(255, 255, 255, 0.04); + transition: background 0.15s ease; + cursor: default; +} + +.setting-row:hover { + background: rgba(255, 255, 255, 0.06); +} + +.setting-label { + display: flex; + align-items: center; + gap: 8px; + font-size: 12.5px; + font-weight: 500; + color: #c9d1d9; +} + +.setting-label .icon { + font-size: 14px; +} + +/* ── Custom Toggle Switch ──────────────────────────────────── */ +.toggle-switch { + position: relative; + width: 38px; height: 20px; - background-color: transparent; - transition: transform 0.3s ease; - display: inline-block; + flex-shrink: 0; +} + +.toggle-switch input { + opacity: 0; + width: 0; + height: 0; +} + +.toggle-slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: #30363d; + border-radius: 20px; + transition: all 0.25s ease; +} + +.toggle-slider::before { + content: ''; + position: absolute; + height: 14px; + width: 14px; + left: 3px; + bottom: 3px; + background: #e2e4ea; + border-radius: 50%; + transition: transform 0.25s ease; +} + +.toggle-switch input:checked + .toggle-slider { + background: #ff6c0a; +} + +.toggle-switch input:checked + .toggle-slider::before { + transform: translateX(18px); +} + +/* ── Commit Message Accordion ──────────────────────────────── */ +.commit-section { + padding: 6px 20px 10px; +} + +.accordion-trigger { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + padding: 10px 12px; + border-radius: 8px; + background: rgba(255, 255, 255, 0.03); + border: 1px solid rgba(255, 255, 255, 0.04); + color: #c9d1d9; + font-size: 12.5px; + font-weight: 500; cursor: pointer; + transition: background 0.15s ease; + font-family: inherit; } -.collapsible-icon:hover { - background-color: #cfc9c980 !important; +.accordion-trigger:hover { + background: rgba(255, 255, 255, 0.06); } -.collapsible-icon.open { +.accordion-chevron { + font-size: 10px; + transition: transform 0.25s ease; + color: #6b7280; +} + +.accordion-trigger.open .accordion-chevron { transform: rotate(90deg); } -.collapsible-container { - margin-top: 1em; - margin-bottom: 1em; - border: 1px dotted black; - padding: 1em; +.accordion-content { + display: none; + padding: 12px; + margin-top: 6px; + border-radius: 8px; + background: rgba(255, 255, 255, 0.03); + border: 1px solid rgba(255, 255, 255, 0.06); + animation: slideDown 0.2s ease; +} + +@keyframes slideDown { + from { + opacity: 0; + transform: translateY(-6px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.accordion-content.open { + display: block; +} + +.variables-label { + font-size: 10px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + color: #6b7280; + margin-bottom: 6px; +} + +.variables-row { display: flex; - flex-direction: column; - gap: 1em; + flex-wrap: wrap; + gap: 4px; + margin-bottom: 10px; } -#customize-btn { - margin-top: 1em; - border: none; - background-color: transparent; +.var-pill { + padding: 3px 10px; + font-size: 11px; + font-family: inherit; + border-radius: 20px; + border: 1px solid rgba(88, 166, 255, 0.25); + background: rgba(88, 166, 255, 0.08); + color: #58a6ff; + cursor: pointer; + transition: all 0.15s ease; +} + +.var-pill:hover { + background: rgba(88, 166, 255, 0.18); + border-color: rgba(88, 166, 255, 0.4); } -.setting-btns { +.commit-textarea { + width: 100%; + padding: 8px 10px; + font-family: 'Inter', monospace; + font-size: 12px; + color: #e2e4ea; + background: #0d1117; + border: 1px solid #30363d; + border-radius: 6px; + resize: none; + outline: none; + transition: border-color 0.2s ease; +} + +.commit-textarea:focus { + border-color: #58a6ff; +} + +.commit-textarea::placeholder { + color: #484f58; +} + +.commit-actions { + display: flex; + gap: 8px; + margin-top: 8px; align-items: center; - background-color: #fff; - border: 1px solid #000; - box-sizing: border-box; - color: #000; +} + +.btn-sm { + padding: 4px 14px; + font-size: 11px; + font-weight: 600; + font-family: inherit; + border-radius: 6px; cursor: pointer; + transition: all 0.15s ease; + border: 1px solid; +} + +.btn-reset { + background: transparent; + border-color: #30363d; + color: #8b949e; +} + +.btn-reset:hover { + background: rgba(255, 255, 255, 0.05); + color: #c9d1d9; +} + +.btn-save { + background: #ff6c0a; + border-color: #ff6c0a; + color: #fff; +} + +.btn-save:hover { + background: #e55e00; + border-color: #e55e00; +} + +.save-feedback { + font-size: 11px; + color: #3fb950; + display: none; + margin-left: auto; +} + +/* ── Auth Mode ─────────────────────────────────────────────── */ +.auth-section { + padding: 20px; +} + +.auth-prompt { + font-size: 13px; + color: #8b949e; + margin-bottom: 16px; + line-height: 1.6; +} + +.btn-github { display: inline-flex; - fill: #000; + align-items: center; + gap: 8px; + padding: 10px 24px; + font-size: 14px; + font-weight: 600; + font-family: inherit; + border-radius: 8px; + border: 1px solid #30363d; + background: #21262d; + color: #e2e4ea; + cursor: pointer; + transition: all 0.2s ease; + width: 100%; justify-content: center; - letter-spacing: -0.8px; - outline: 0; - padding: 0 17px; - text-align: center; - text-decoration: none; - transition: all 0.3s; - user-select: none; - -webkit-user-select: none; - touch-action: manipulation; } -#custom-commit-msg { - width: 100%; - padding: 12px 20px; - box-sizing: border-box; - border: 2px solid #ccc; - border-radius: 4px; - background-color: #f8f8f8; - font-size: 16px; - resize: none; +.btn-github:hover { + background: #30363d; + border-color: #8b949e; } -.commit-variable { - box-sizing: border-box; - background-color: #a8dadc; +.btn-github svg { + width: 18px; + height: 18px; + fill: currentColor; +} + +.divider { + display: flex; + align-items: center; + gap: 12px; + margin: 16px 0; + color: #484f58; + font-size: 11px; + text-transform: uppercase; + letter-spacing: 1px; +} + +.divider::before, +.divider::after { + content: ''; + flex: 1; + height: 1px; + background: #30363d; +} + +.pat-group { + text-align: left; +} + +.pat-label { + font-size: 11px; + font-weight: 500; + color: #8b949e; + margin-bottom: 6px; + display: block; +} + +.pat-input-row { + display: flex; + gap: 8px; +} + +.pat-input { + flex: 1; + padding: 8px 12px; + font-size: 13px; + font-family: inherit; + background: #0d1117; + border: 1px solid #30363d; + border-radius: 6px; + color: #e2e4ea; + outline: none; + transition: border-color 0.2s ease; +} + +.pat-input:focus { + border-color: #58a6ff; +} + +.pat-input::placeholder { + color: #484f58; +} + +.btn-connect { + padding: 8px 18px; + font-size: 13px; + font-weight: 600; + font-family: inherit; + border-radius: 6px; border: none; + background: linear-gradient(135deg, #ff6c0a, #ff8c3a); + color: #fff; + cursor: pointer; + transition: all 0.2s ease; + white-space: nowrap; +} + +.btn-connect:hover { + background: linear-gradient(135deg, #e55e00, #ff6c0a); + transform: translateY(-1px); +} + +.btn-connect:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none; +} + +.pat-error { + font-size: 11px; + color: #f85149; + margin-top: 6px; + display: none; +} + +/* ── Hook Mode ─────────────────────────────────────────────── */ +.hook-section { + padding: 20px; text-align: center; +} + +.hook-prompt { + font-size: 13px; + color: #8b949e; + margin-bottom: 16px; +} + +.btn-setup-hook { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 10px 24px; + font-size: 14px; + font-weight: 600; + font-family: inherit; + border-radius: 8px; + border: none; + background: linear-gradient(135deg, #ff6c0a, #ff8c3a); + color: #fff; + cursor: pointer; text-decoration: none; - display: inline-block; - border-radius: 30px; + transition: all 0.2s ease; +} + +.btn-setup-hook:hover { + background: linear-gradient(135deg, #e55e00, #ff6c0a); + transform: translateY(-1px); +} + +.btn-setup-hook svg { + width: 16px; + height: 16px; + fill: currentColor; +} + +/* ── Footer ────────────────────────────────────────────────── */ +.popup-footer { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 20px 14px; + border-top: 1px solid rgba(255, 255, 255, 0.04); +} + +.btn-logout { + padding: 5px 14px; + font-size: 11px; + font-weight: 500; + font-family: inherit; + border-radius: 6px; + border: 1px solid rgba(248, 81, 73, 0.3); + background: rgba(248, 81, 73, 0.08); + color: #f85149; cursor: pointer; - transition: background-color 0.3s ease; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); - border: 1px solid black; - margin: 2px; + transition: all 0.15s ease; } -.commit-variable:hover { - background-color: #457b9d; +.btn-logout:hover { + background: rgba(248, 81, 73, 0.15); + border-color: rgba(248, 81, 73, 0.5); } -#success-message { - color: green; - font-size: 14px; - display: none; +.version-text { + font-size: 10px; + color: #484f58; +} + +/* ── Utility ───────────────────────────────────────────────── */ +.hidden { + display: none !important; +} + +.centered-toggle { + display: flex; + justify-content: center; } diff --git a/src/css/welcome.css b/src/css/welcome.css index 09c0daf..f721a38 100644 --- a/src/css/welcome.css +++ b/src/css/welcome.css @@ -1,62 +1,311 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap'); + +/* ── Reset & Base ──────────────────────────────────────────── */ +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + body { - background-color: #000000 !important; - padding: 10px; + min-height: 100vh; + background: #0d1117; + color: #e2e4ea; + font-family: + 'Inter', + -apple-system, + BlinkMacSystemFont, + sans-serif; + -webkit-font-smoothing: antialiased; + overflow-x: hidden; +} + +/* ── Animated Background ───────────────────────────────────── */ +body::before { + content: ''; + position: fixed; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: + radial-gradient(ellipse at 20% 50%, rgba(88, 40, 180, 0.08) 0%, transparent 50%), + radial-gradient(ellipse at 80% 20%, rgba(255, 108, 10, 0.06) 0%, transparent 50%), + radial-gradient(ellipse at 50% 80%, rgba(88, 166, 255, 0.05) 0%, transparent 50%); + animation: bgFloat 20s ease-in-out infinite; + z-index: -1; +} + +@keyframes bgFloat { + 0%, + 100% { + transform: translate(0, 0); + } + 50% { + transform: translate(-2%, -1%); + } +} + +/* ── Container ─────────────────────────────────────────────── */ +.welcome-container { + max-width: 640px; + margin: 0 auto; + padding: 60px 20px; text-align: center; - transition: background-color 0.75s ease; - font-family: 'Helvetica Neue', 'Lucida Grande', sans-serif; } -#title { - font-family: 'Norwester', 'Helvetica Neue', 'Lucida Grande', sans-serif; - font-size: 5em; - font-weight: bold; - margin-bottom: 0px; - margin-top: 10px; +/* ── Logo ──────────────────────────────────────────────────── */ +.welcome-logo { + font-size: clamp(3rem, 8vw, 5rem); + font-weight: 800; + letter-spacing: -2px; + color: #ffffff; + margin-bottom: 8px; } -.caption { - font-family: 'Norwester', 'Helvetica Neue', 'Lucida Grande', sans-serif; - font-size: 1.5em; - font-weight: normal !important; - margin-top: 3px !important; - margin-bottom: 0px; +.welcome-logo .accent { + color: #ff6c0a; } +.welcome-tagline { + font-size: 1.1rem; + color: #6b7280; + font-weight: 400; + margin-bottom: 48px; +} + +/* ── Messages ──────────────────────────────────────────────── */ #error { - font-family: 'Norwester', 'Helvetica Neue', 'Lucida Grande', sans-serif; - font-weight: normal !important; - color: red !important; - margin-bottom: 0px; + background: rgba(248, 81, 73, 0.1); + border: 1px solid rgba(248, 81, 73, 0.3); + color: #f85149; + padding: 12px 16px; + border-radius: 10px; + font-size: 13px; + margin-bottom: 16px; + display: none; +} + +#error a { + color: #f85149; + text-decoration: underline; } #success { - font-family: 'Norwester', 'Helvetica Neue', 'Lucida Grande', sans-serif; - font-size: 1.1em; - font-weight: normal !important; - color: green !important; - margin-bottom: 0px; + background: rgba(63, 185, 80, 0.1); + border: 1px solid rgba(63, 185, 80, 0.3); + color: #3fb950; + padding: 12px 16px; + border-radius: 10px; + font-size: 13px; + margin-bottom: 16px; + display: none; +} + +#success a { + color: #58a6ff; + text-decoration: underline; } #unlink { - font-family: 'Norwester', 'Helvetica Neue', 'Lucida Grande', sans-serif; - font-size: 1.1em; - font-weight: normal !important; - color: green !important; - margin-bottom: 0px; + font-size: 13px; + color: #8b949e; + margin-bottom: 16px; + display: none; +} + +#unlink a { + color: #58a6ff; + cursor: pointer; + text-decoration: underline; +} + +/* ── Glass Card ────────────────────────────────────────────── */ +.glass-card { + background: rgba(255, 255, 255, 0.03); + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 16px; + padding: 32px; + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); +} + +/* ── Hook Mode (Setup) ─────────────────────────────────────── */ +#hook_mode { + display: none; +} + +.setup-heading { + font-size: 1.2rem; + font-weight: 600; + margin-bottom: 24px; + color: #e2e4ea; +} + +.form-group { + margin-bottom: 16px; + text-align: left; +} + +.form-label { + display: block; + font-size: 12px; + font-weight: 500; + color: #8b949e; + margin-bottom: 6px; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.form-select, +.form-input { + width: 100%; + padding: 10px 14px; + font-size: 14px; + font-family: 'Inter', sans-serif; + background: #0d1117; + border: 1px solid #30363d; + border-radius: 8px; + color: #e2e4ea; + outline: none; + transition: border-color 0.2s ease; + appearance: none; + -webkit-appearance: none; +} + +.form-select { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%236b7280' viewBox='0 0 16 16'%3E%3Cpath d='M8 11L3 6h10z'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 12px center; + padding-right: 36px; +} + +.form-select:focus, +.form-input:focus { + border-color: #58a6ff; } -p { - color: white !important; +.form-select option { + background: #161b22; + color: #e2e4ea; } -.onboarding { - font-family: sans-serif; - border-top: solid white 1px; +.btn-start { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 12px 32px; + font-size: 15px; + font-weight: 600; + font-family: 'Inter', sans-serif; + border-radius: 10px; + border: none; + background: linear-gradient(135deg, #ff6c0a, #ff8c3a); + color: #fff; + cursor: pointer; + transition: all 0.2s ease; + margin-top: 8px; +} + +.btn-start:hover:not(:disabled) { + background: linear-gradient(135deg, #e55e00, #ff6c0a); + transform: translateY(-2px); + box-shadow: 0 8px 24px rgba(255, 108, 10, 0.25); +} + +.btn-start:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +/* ── Commit Mode (Stats) ───────────────────────────────────── */ +#commit_mode { + display: none; +} + +.stats-grid { + display: flex; + gap: 12px; + margin-bottom: 24px; +} + +.stats-card { + flex: 1; + padding: 20px 12px; + border-radius: 12px; + text-align: center; + background: rgba(255, 255, 255, 0.04); + border: 1px solid rgba(255, 255, 255, 0.06); + transition: all 0.2s ease; +} + +.stats-card:hover { + transform: translateY(-2px); + background: rgba(255, 255, 255, 0.07); +} + +.stats-number { + font-size: 2rem; + font-weight: 700; + display: block; +} + +.stats-label { + font-size: 11px; font-weight: 500; - font-size: medium; + text-transform: uppercase; + letter-spacing: 0.8px; + color: #6b7280; + margin-top: 4px; + display: block; } -a { - color: white !important; +.stats-card.total .stats-number { + color: #e2e4ea; +} +.stats-card.easy .stats-number { + color: #3fb950; +} +.stats-card.easy { + border-color: rgba(63, 185, 80, 0.15); +} +.stats-card.medium .stats-number { + color: #d29922; +} +.stats-card.medium { + border-color: rgba(210, 153, 34, 0.15); +} +.stats-card.hard .stats-number { + color: #f85149; +} +.stats-card.hard { + border-color: rgba(248, 81, 73, 0.15); +} + +.sync-link { + font-size: 12px; + color: #58a6ff; cursor: pointer; + text-decoration: none; + transition: opacity 0.2s; +} + +.sync-link:hover { + opacity: 0.8; + text-decoration: underline; +} + +/* ── Utility ───────────────────────────────────────────────── */ +.hidden { + display: none !important; +} + +.mt-16 { + margin-top: 16px; +} + +.text-center { + text-align: center; } diff --git a/src/html/popup.html b/src/html/popup.html index f8cadb7..bbbd7dd 100644 --- a/src/html/popup.html +++ b/src/html/popup.html @@ -1,200 +1,169 @@ - + + + LeetHub-3.0 - - - -
-
-

LeetHub-3.0

-

Sync your code from LeetCode to GitHub

-
- -