+2
-2
| { | ||
| "description": "ZeroX - Security Toolkit & Burp Suite Proxy Suite", | ||
| "description": "ZeroX", | ||
| "manifest_version": 2, | ||
| "name": "ZeroX", | ||
| "version": "2.0.1", | ||
| "version": "2.0.2", | ||
| "icons": { | ||
@@ -7,0 +7,0 @@ "48": "icons/icon-48.png", |
+9
-6
| { | ||
| "name": "zerox-firefox-extension", | ||
| "version": "2.0.1", | ||
| "version": "2.0.2", | ||
| "description": "ZeroX Firefox Extension - Security Toolkit", | ||
| "scripts": { | ||
| "dev": "web-ext run --reload", | ||
| "prebuild": "node -e \"try{require('fs').rmSync('web-ext-artifacts',{recursive:true,force:true})}catch(e){}\"", | ||
| "build": "web-ext build --overwrite-dest", | ||
| "lint": "web-ext lint", | ||
| "sign": "web-ext sign" | ||
| "dev": "node ./node_modules/web-ext/bin/web-ext.js run --reload", | ||
| "prebuild": "node -e \"const fs=require('fs');try{fs.rmSync('web-ext-artifacts',{recursive:true,force:true})}catch(e){}\"", | ||
| "build": "node ./node_modules/web-ext/bin/web-ext.js build --overwrite-dest", | ||
| "lint": "node ./node_modules/web-ext/bin/web-ext.js lint", | ||
| "sign": "node ./node_modules/web-ext/bin/web-ext.js sign" | ||
| }, | ||
@@ -16,1 +16,4 @@ "devDependencies": { | ||
| } | ||
+5
-3
@@ -0,1 +1,3 @@ | ||
| /** | ||
@@ -7,3 +9,3 @@ * ZeroX Configuration & Fast In-Memory Storage Cache | ||
| enabled: false, | ||
| useBurpProxyAll: false, | ||
| useBurpProxyAll: true, | ||
| useBurpProxyContainer: false, | ||
@@ -61,3 +63,3 @@ addContainerHeader: true, | ||
| } catch (e) { | ||
| return defaultConfig[key]; | ||
| return _configCache[key] ?? defaultConfig[key]; | ||
| } | ||
@@ -72,3 +74,3 @@ }, | ||
| } catch (e) { | ||
| return { ...defaultConfig }; | ||
| return { ..._configCache }; | ||
| } | ||
@@ -75,0 +77,0 @@ }, |
+246
-126
@@ -0,25 +1,63 @@ | ||
| /* ========================================================================== | ||
| ZeroX DevTools PostMessage Inspector - Clean Light Theme | ||
| ========================================================================== */ | ||
| :root { | ||
| --bg-base: #090b12; | ||
| --bg-surface: #121626; | ||
| --bg-toolbar: #181d30; | ||
| --border-color: rgba(255, 255, 255, 0.08); | ||
| --border-hover: rgba(168, 85, 247, 0.4); | ||
| --text-main: #f1f5f9; | ||
| --text-dim: #94a3b8; | ||
| --accent: #a855f7; | ||
| --bg-base: #f8fafc; | ||
| --bg-surface: #ffffff; | ||
| --bg-card: #ffffff; | ||
| --bg-card-hover: #f1f5f9; | ||
| --bg-header: #ffffff; | ||
| --text-main: #0f172a; | ||
| --text-dim: #475569; | ||
| --text-muted: #64748b; | ||
| --text-faint: #94a3b8; | ||
| --accent-purple: #7c3aed; | ||
| --accent-purple-glow: rgba(124, 58, 237, 0.2); | ||
| --accent-cyan: #0284c7; | ||
| --accent-emerald: #059669; | ||
| --accent-amber: #d97706; | ||
| --border-subtle: #e2e8f0; | ||
| --border-row: #f1f5f9; | ||
| --border-active: #7c3aed; | ||
| --font-sans: 'Inter', -apple-system, sans-serif; | ||
| --font-mono: 'JetBrains Mono', 'Fira Code', ui-monospace, monospace; | ||
| } | ||
| html, | ||
| body { | ||
| * { | ||
| box-sizing: border-box; | ||
| margin: 0; | ||
| padding: 0; | ||
| margin: 0; | ||
| -webkit-font-smoothing: antialiased; | ||
| } | ||
| html, body { | ||
| height: 100%; | ||
| width: 100%; | ||
| background-color: var(--bg-base); | ||
| color: var(--text-main); | ||
| background-color: var(--bg-base); | ||
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Inter", monospace; | ||
| font-family: var(--font-sans); | ||
| font-size: 12px; | ||
| overflow: hidden; | ||
| } | ||
| * { | ||
| box-sizing: border-box; | ||
| /* Custom Scrollbar */ | ||
| ::-webkit-scrollbar { | ||
| width: 6px; | ||
| height: 6px; | ||
| } | ||
| ::-webkit-scrollbar-track { | ||
| background: transparent; | ||
| } | ||
| ::-webkit-scrollbar-thumb { | ||
| background: #cbd5e1; | ||
| border-radius: 99px; | ||
| } | ||
| ::-webkit-scrollbar-thumb:hover { | ||
| background: #94a3b8; | ||
| } | ||
@@ -30,13 +68,19 @@ main { | ||
| grid-template-columns: 1fr; | ||
| grid-template-rows: auto 1fr; | ||
| grid-template-rows: 42px 1fr; | ||
| grid-template-areas: "toolbar" "list"; | ||
| align-items: start; | ||
| height: 100vh; | ||
| width: 100vw; | ||
| overflow: hidden; | ||
| transition: grid-template-columns 0.25s cubic-bezier(0.16, 1, 0.3, 1); | ||
| } | ||
| main.dual { | ||
| grid-template-columns: auto 450px; | ||
| grid-template-columns: 1fr 440px; | ||
| grid-template-areas: "toolbar filter" "list filter"; | ||
| } | ||
| /* ========================================================================== | ||
| Toolbar | ||
| ========================================================================== */ | ||
| #message-toolbar { | ||
@@ -46,18 +90,52 @@ grid-area: toolbar; | ||
| align-items: center; | ||
| column-gap: 8px; | ||
| background: var(--bg-toolbar); | ||
| border-bottom: 1px solid var(--border-color); | ||
| padding: 8px 12px; | ||
| width: 100%; | ||
| gap: 8px; | ||
| background: var(--bg-header); | ||
| border-bottom: 1px solid var(--border-subtle); | ||
| padding: 0 12px; | ||
| z-index: 20; | ||
| user-select: none; | ||
| } | ||
| #message-toolbar button { | ||
| border: 1px solid var(--border-color); | ||
| .toolbar-brand { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 8px; | ||
| padding-right: 12px; | ||
| border-right: 1px solid var(--border-subtle); | ||
| } | ||
| .brand-icon { | ||
| width: 22px; | ||
| height: 22px; | ||
| border-radius: 6px; | ||
| white-space: nowrap; | ||
| user-select: none; | ||
| background: rgba(255, 255, 255, 0.06); | ||
| background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%); | ||
| color: #ffffff; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| } | ||
| .brand-name { | ||
| font-weight: 800; | ||
| font-size: 13px; | ||
| color: var(--text-main); | ||
| padding: 4px 10px; | ||
| font-size: 11px; | ||
| letter-spacing: -0.02em; | ||
| } | ||
| .toolbar-actions { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 4px; | ||
| } | ||
| .tool-btn { | ||
| display: inline-flex; | ||
| align-items: center; | ||
| gap: 5px; | ||
| background: #f1f5f9; | ||
| border: 1px solid var(--border-subtle); | ||
| border-radius: 6px; | ||
| padding: 5px 9px; | ||
| color: var(--text-dim); | ||
| font-size: 11.5px; | ||
| font-weight: 600; | ||
@@ -68,144 +146,186 @@ cursor: pointer; | ||
| #message-toolbar button:hover { | ||
| background: rgba(168, 85, 247, 0.2); | ||
| border-color: var(--border-hover); | ||
| .tool-btn:hover { | ||
| background: #e2e8f0; | ||
| color: var(--text-main); | ||
| } | ||
| .tool-btn.danger:hover { | ||
| background: #fee2e2; | ||
| border-color: #fca5a5; | ||
| color: #dc2626; | ||
| } | ||
| .tool-btn.active { | ||
| background: var(--accent-purple); | ||
| border-color: var(--accent-purple); | ||
| color: #ffffff; | ||
| } | ||
| #toggleDual { | ||
| padding: 4px; | ||
| .search-box { | ||
| margin-left: auto; | ||
| position: relative; | ||
| display: flex; | ||
| align-items: center; | ||
| } | ||
| .search-icon { | ||
| position: absolute; | ||
| left: 8px; | ||
| color: var(--text-faint); | ||
| pointer-events: none; | ||
| } | ||
| #filter-input { | ||
| background: #ffffff; | ||
| border: 1px solid var(--border-subtle); | ||
| border-radius: 6px; | ||
| cursor: pointer; | ||
| color: var(--text-dim); | ||
| padding: 5px 8px 5px 28px; | ||
| color: var(--text-main); | ||
| font-size: 11.5px; | ||
| outline: none; | ||
| width: 180px; | ||
| transition: all 0.15s ease; | ||
| } | ||
| #toggleDual:hover { | ||
| color: var(--accent); | ||
| background: rgba(255, 255, 255, 0.06); | ||
| #filter-input:focus { | ||
| border-color: var(--accent-purple); | ||
| width: 220px; | ||
| box-shadow: 0 0 0 2px rgba(124, 58, 237, 0.12); | ||
| } | ||
| /* ========================================================================== | ||
| Table List | ||
| ========================================================================== */ | ||
| #message-list { | ||
| display: contents; | ||
| grid-area: list; | ||
| overflow: auto; | ||
| background: var(--bg-surface); | ||
| } | ||
| #message-table { | ||
| grid-area: list; | ||
| overflow-y: auto; | ||
| overflow-x: hidden; | ||
| height: 100%; | ||
| display: flex; | ||
| flex-direction: column; | ||
| justify-content: start; | ||
| counter-reset: message-id; | ||
| table { | ||
| width: 100%; | ||
| border-collapse: collapse; | ||
| table-layout: fixed; | ||
| } | ||
| #message-table > .row { | ||
| thead { | ||
| position: sticky; | ||
| top: 0; | ||
| position: sticky; | ||
| background: #f8fafc; | ||
| border-bottom: 1px solid var(--border-subtle); | ||
| z-index: 10; | ||
| background: var(--bg-surface); | ||
| border-bottom: 1px solid var(--border-color); | ||
| } | ||
| th { | ||
| padding: 8px 12px; | ||
| text-align: left; | ||
| font-size: 10.5px; | ||
| font-weight: 700; | ||
| color: var(--text-dim); | ||
| color: var(--text-muted); | ||
| letter-spacing: 0.05em; | ||
| text-transform: uppercase; | ||
| font-size: 10.5px; | ||
| letter-spacing: 0.5px; | ||
| } | ||
| .spacer { | ||
| flex-grow: 1; | ||
| } | ||
| th.th-idx { width: 50px; } | ||
| th.th-origin { width: 22%; } | ||
| th.th-dest { width: 22%; } | ||
| th.th-data { width: 50%; } | ||
| .row { | ||
| display: grid; | ||
| grid-template-columns: 45px 1.2fr 1.2fr 4fr 85px; | ||
| line-height: 20px; | ||
| font-size: 11.5px; | ||
| font-family: ui-monospace, SFMono-Regular, monospace; | ||
| border-bottom: 1px solid var(--border-color); | ||
| background: var(--bg-base); | ||
| tbody tr { | ||
| border-bottom: 1px solid var(--border-row); | ||
| transition: background 0.1s ease; | ||
| cursor: pointer; | ||
| } | ||
| .row:hover { | ||
| background: rgba(255, 255, 255, 0.04); | ||
| tbody tr:hover { | ||
| background: #f8fafc; | ||
| } | ||
| .row span { | ||
| position: relative; | ||
| tbody tr.selected { | ||
| background: #f5f3ff; | ||
| } | ||
| td { | ||
| padding: 8px 12px; | ||
| vertical-align: middle; | ||
| white-space: nowrap; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| padding: 4px 8px; | ||
| font-size: 11.5px; | ||
| } | ||
| #message-list .row span:first-child:before { | ||
| counter-increment: message-id; | ||
| content: counter(message-id, decimal-leading-zero); | ||
| color: var(--text-dim); | ||
| .td-idx { | ||
| font-family: var(--font-mono); | ||
| color: var(--text-faint); | ||
| font-size: 10.5px; | ||
| } | ||
| #message-filter { | ||
| display: none; | ||
| border-left: 1px solid var(--border-color); | ||
| background: var(--bg-surface); | ||
| .origin-tag { | ||
| display: inline-block; | ||
| padding: 2px 6px; | ||
| border-radius: 4px; | ||
| background: #f0f9ff; | ||
| color: var(--accent-cyan); | ||
| border: 1px solid #bae6fd; | ||
| font-family: var(--font-mono); | ||
| font-size: 10.5px; | ||
| max-width: 100%; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| } | ||
| .dual #message-filter { | ||
| grid-area: filter; | ||
| display: grid; | ||
| grid-template-rows: 36px auto; | ||
| height: 100%; | ||
| .dest-tag { | ||
| display: inline-block; | ||
| padding: 2px 6px; | ||
| border-radius: 4px; | ||
| background: #ecfdf5; | ||
| color: var(--accent-emerald); | ||
| border: 1px solid #a7f3d0; | ||
| font-family: var(--font-mono); | ||
| font-size: 10.5px; | ||
| max-width: 100%; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| } | ||
| #message-filter h2 { | ||
| text-align: center; | ||
| margin: 0; | ||
| line-height: 36px; | ||
| font-size: 12px; | ||
| font-weight: 700; | ||
| color: var(--text-dim); | ||
| border-bottom: 1px solid var(--border-color); | ||
| .data-preview { | ||
| font-family: var(--font-mono); | ||
| color: var(--text-main); | ||
| font-size: 11.5px; | ||
| } | ||
| textarea { | ||
| padding: 10px; | ||
| /* ========================================================================== | ||
| Side Filter Panel | ||
| ========================================================================== */ | ||
| #message-filter { | ||
| grid-area: filter; | ||
| background: #ffffff; | ||
| border-left: 1px solid var(--border-subtle); | ||
| display: none; | ||
| flex-direction: column; | ||
| height: 100%; | ||
| display: block; | ||
| resize: none; | ||
| background: var(--bg-base); | ||
| color: #e2e8f0; | ||
| border: none; | ||
| outline: none; | ||
| font-family: ui-monospace, SFMono-Regular, monospace; | ||
| font-size: 12px; | ||
| overflow: hidden; | ||
| } | ||
| .message-details-content { | ||
| padding: 12px 16px; | ||
| display: grid; | ||
| grid-template-columns: 100px 1fr; | ||
| row-gap: 8px; | ||
| column-gap: 12px; | ||
| background: #06070c; | ||
| border-bottom: 1px solid var(--border-color); | ||
| font-family: ui-monospace, SFMono-Regular, monospace; | ||
| font-size: 11.5px; | ||
| main.dual #message-filter { | ||
| display: flex; | ||
| } | ||
| .message-details-content span:nth-child(odd) { | ||
| color: var(--accent); | ||
| font-weight: 600; | ||
| .filter-header { | ||
| height: 42px; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| padding: 0 12px; | ||
| background: #f8fafc; | ||
| border-bottom: 1px solid var(--border-subtle); | ||
| } | ||
| .message-details-content span:nth-child(even) { | ||
| white-space: pre-wrap; | ||
| word-break: break-all; | ||
| color: #cbd5e1; | ||
| } | ||
| summary { | ||
| cursor: pointer; | ||
| outline: 0; | ||
| .filter-title { | ||
| font-size: 11.5px; | ||
| font-weight: 700; | ||
| color: var(--text-main); | ||
| letter-spacing: -0.01em; | ||
| } |
@@ -6,7 +6,11 @@ <!DOCTYPE html> | ||
| <meta charset="utf8"> | ||
| <title>ZeroX DevTools PostMessage Inspector</title> | ||
| <link rel="preconnect" href="https://fonts.googleapis.com"> | ||
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | ||
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet"> | ||
| <link rel="stylesheet" href="./panel.css"> | ||
| <link rel="stylesheet" href="/src/fileSelection.css"> | ||
| <script src="/src/config.js"></script> | ||
| <script src="/src/fileSelection.js"></script> | ||
| <script src="./panel.js"></script> | ||
| <link rel="stylesheet" href="/src/fileSelection.css"> | ||
| </head> | ||
@@ -16,71 +20,97 @@ | ||
| <main> | ||
| <!-- Toolbar --> | ||
| <div id="message-toolbar"> | ||
| <button id="btn-clear">Clear</button> | ||
| <button id="btn-shrink">Shrink all</button> | ||
| <button id="btn-expand">Expand all</button> | ||
| <div class="toolbar-brand"> | ||
| <span class="toolbar-logo">ZX</span> | ||
| <span class="toolbar-title">postMessage Inspector</span> | ||
| </div> | ||
| <div class="toolbar-actions"> | ||
| <button id="btn-clear" class="tb-btn" title="Clear all captured messages"> | ||
| <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"> | ||
| <circle cx="12" cy="12" r="10"/> | ||
| <line x1="4.93" y1="4.93" x2="19.07" y2="19.07"/> | ||
| </svg> | ||
| <span>Clear</span> | ||
| </button> | ||
| <button id="btn-shrink" class="tb-btn" title="Collapse all message details"> | ||
| <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"> | ||
| <polyline points="4 14 10 14 10 20"/> | ||
| <polyline points="20 10 14 10 14 4"/> | ||
| <line x1="14" y1="10" x2="21" y2="3"/> | ||
| <line x1="3" y1="21" x2="10" y2="14"/> | ||
| </svg> | ||
| <span>Collapse All</span> | ||
| </button> | ||
| <button id="btn-expand" class="tb-btn" title="Expand all message details"> | ||
| <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"> | ||
| <polyline points="15 3 21 3 21 9"/> | ||
| <polyline points="9 21 3 21 3 15"/> | ||
| <line x1="21" y1="3" x2="14" y2="10"/> | ||
| <line x1="3" y1="21" x2="10" y2="14"/> | ||
| </svg> | ||
| <span>Expand All</span> | ||
| </button> | ||
| </div> | ||
| <div class="spacer"></div> | ||
| <svg id="toggleDual" height="20px" viewBox="0 0 16 16" color="inherit" xmlns="http://www.w3.org/2000/svg" | ||
| fill="context-fill #b1b1b3"> | ||
| <path d=" | ||
| M0 4a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3h-10a3 3 0 0 1-3-3z | ||
| M2 4v8a1 1 0 0 0 1 1h7v-10h-7a1 1 0 0 0-1 1z | ||
| M11 3v10h2a1 1 0 0 0 1-1v-8a1 1 0 0 0-1-1z" /> | ||
| </svg> | ||
| <button id="toggleDual" class="tb-btn dual-toggle-btn" title="Toggle Filter Script Editor Panel"> | ||
| <svg width="15" height="15" viewBox="0 0 16 16" fill="currentColor"> | ||
| <path d="M0 4a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3h-10a3 3 0 0 1-3-3zM2 4v8a1 1 0 0 0 1 1h7v-10h-7a1 1 0 0 0-1 1zM11 3v10h2a1 1 0 0 0 1-1v-8a1 1 0 0 0-1-1z"/> | ||
| </svg> | ||
| <span>Filter Editor</span> | ||
| </button> | ||
| </div> | ||
| <!-- Message Table --> | ||
| <div id="message-table"> | ||
| <div class="row"> | ||
| <span>Id</span> | ||
| <span>Origin</span> | ||
| <span>Destination</span> | ||
| <span>Message</span> | ||
| <span>Time</span> | ||
| <div class="row header-row"> | ||
| <span class="col-id">#</span> | ||
| <span class="col-ori">ORIGIN</span> | ||
| <span class="col-dest">DESTINATION</span> | ||
| <span class="col-msg">PAYLOAD (DATA)</span> | ||
| <span class="col-time">TIME</span> | ||
| </div> | ||
| <div id="message-list"> | ||
| <!-- Dynamically added messages --> | ||
| </div> | ||
| </div> | ||
| <!-- Filter Code Panel --> | ||
| <div id="message-filter"> | ||
| <h2>Filter function</h2> | ||
| <div class="filter-header"> | ||
| <h2>Message Filter Function</h2> | ||
| <span class="filter-badge">JS HOOK</span> | ||
| </div> | ||
| <div id="savedMessageFunc" class="file-list"> | ||
| <div class="file-list-head"> | ||
| <select> | ||
| <select class="custom-select"> | ||
| </select> | ||
| <input type="file" accept="application/javscript"> | ||
| <button class="file-list-new" title="New"> | ||
| <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"> | ||
| <path fill="context-fill" | ||
| d="M14 7H9V2a1 1 0 0 0-2 0v5H2a1 1 0 1 0 0 2h5v5a1 1 0 0 0 2 0V9h5a1 1 0 0 0 0-2z"></path> | ||
| <input type="file" accept="application/javascript"> | ||
| <button class="file-list-new" title="New Function"> | ||
| <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 16 16" fill="currentColor"> | ||
| <path d="M14 7H9V2a1 1 0 0 0-2 0v5H2a1 1 0 1 0 0 2h5v5a1 1 0 0 0 2 0V9h5a1 1 0 0 0 0-2z"/> | ||
| </svg> | ||
| </button> | ||
| <button class="file-list-edit" title="Rename Function"> | ||
| <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 16 16" fill="currentColor"> | ||
| <path d="M14.354 2.353l-.708-.707a2.007 2.007 0 0 0-2.828 0l-.379.379a.5.5 0 0 0 0 .707l2.829 2.829a.5.5 0 0 0 .707 0l.379-.379a2.008 2.008 0 0 0 0-2.829zM9.732 3.439a.5.5 0 0 0-.707 0L3.246 9.218a1.986 1.986 0 0 0-.452.712l-1.756 4.39A.5.5 0 0 0 1.5 15a.5.5 0 0 0 .188-.037l4.382-1.752a1.966 1.966 0 0 0 .716-.454l5.779-5.778a.5.5 0 0 0 0-.707zM5.161 12.5l-2.549 1.02a.1.1 0 0 1-.13-.13L3.5 10.831a.1.1 0 0 1 .16-.031l1.54 1.535a.1.1 0 0 1-.039.165z"/> | ||
| </svg> | ||
| </button> | ||
| <button class="file-list-edit" title="Rename"> | ||
| <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"> | ||
| <path fill="context-fill" | ||
| d="M14.354 2.353l-.708-.707a2.007 2.007 0 0 0-2.828 0l-.379.379a.5.5 0 0 0 0 .707l2.829 2.829a.5.5 0 0 0 .707 0l.379-.379a2.008 2.008 0 0 0 0-2.829zM9.732 3.439a.5.5 0 0 0-.707 0L3.246 9.218a1.986 1.986 0 0 0-.452.712l-1.756 4.39A.5.5 0 0 0 1.5 15a.5.5 0 0 0 .188-.037l4.382-1.752a1.966 1.966 0 0 0 .716-.454l5.779-5.778a.5.5 0 0 0 0-.707zM5.161 12.5l-2.549 1.02a.1.1 0 0 1-.13-.13L3.5 10.831a.1.1 0 0 1 .16-.031l1.54 1.535a.1.1 0 0 1-.039.165z"> | ||
| </path> | ||
| <button class="file-list-save" title="Save Function"> | ||
| <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 16 16" fill="currentColor"> | ||
| <path d="M14 3h-2v2h2v8H2V5h7V3h-.849L6.584 1.538A2 2 0 0 0 5.219 1H2a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zM2 3h3.219l1.072 1H2z"/> | ||
| <path fill="#10b981" d="M8.146 6.146a.5.5 0 0 0 0 .707l2 2a.5.5 0 0 0 .707 0l2-2a.5.5 0 1 0-.707-.707L11 7.293V.5a.5.5 0 0 0-1 0v6.793L8.854 6.146a.5.5 0 0 0-.708 0z"/> | ||
| </svg> | ||
| </button> | ||
| <button class="file-list-save" title="Save"> | ||
| <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"> | ||
| <path fill="context-fill" | ||
| d="M14 3h-2v2h2v8H2V5h7V3h-.849L6.584 1.538A2 2 0 0 0 5.219 1H2a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zM2 3h3.219l1.072 1H2z"> | ||
| </path> | ||
| <path fill="red" | ||
| d="M8.146 6.146a.5.5 0 0 0 0 .707l2 2a.5.5 0 0 0 .707 0l2-2a.5.5 0 1 0-.707-.707L11 7.293V.5a.5.5 0 0 0-1 0v6.793L8.854 6.146a.5.5 0 0 0-.708 0z"> | ||
| </path> | ||
| <button class="file-list-delete" title="Delete Function"> | ||
| <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 16 16" fill="currentColor"> | ||
| <path d="M6.5 12a.5.5 0 0 0 .5-.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 0 .5.5zm2 0a.5.5 0 0 0 .5-.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 0 .5.5zm2 0a.5.5 0 0 0 .5-.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 0 .5.5z"/> | ||
| <path d="M14 2h-3.05a2.5 2.5 0 0 0-4.9 0H3a1 1 0 0 0 0 2v9a3 3 0 0 0 3 3h5a3 3 0 0 0 3-3V4a1 1 0 0 0 0-2zM8.5 1a1.489 1.489 0 0 1 1.391 1H7.109A1.489 1.489 0 0 1 8.5 1zM12 13a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4h7z"/> | ||
| </svg> | ||
| <button class="file-list-delete" title="Delete"> | ||
| <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"> | ||
| <path fill="context-fill" | ||
| d="M6.5 12a.5.5 0 0 0 .5-.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 0 .5.5zm2 0a.5.5 0 0 0 .5-.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 0 .5.5zm2 0a.5.5 0 0 0 .5-.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 0 .5.5z"> | ||
| </path> | ||
| <path fill="context-fill" | ||
| d="M14 2h-3.05a2.5 2.5 0 0 0-4.9 0H3a1 1 0 0 0 0 2v9a3 3 0 0 0 3 3h5a3 3 0 0 0 3-3V4a1 1 0 0 0 0-2zM8.5 1a1.489 1.489 0 0 1 1.391 1H7.109A1.489 1.489 0 0 1 8.5 1zM12 13a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4h7z"> | ||
| </path> | ||
| </svg> | ||
| </button> | ||
| </button> | ||
| </div> | ||
| <div class="file-list-body"> | ||
| <textarea disabled spellcheck="false" autocomplete="false"></textarea> | ||
| <textarea disabled spellcheck="false" autocomplete="off"></textarea> | ||
| </div> | ||
@@ -91,4 +121,2 @@ </div> | ||
| </body> | ||
| </html> |
+80
-39
@@ -39,11 +39,12 @@ class Feature { | ||
| // If neither is checked, go direct | ||
| // If neither is checked, default to direct | ||
| if (!useAll && !useContainers) return { type: "direct" }; | ||
| // If 'Containers Only' is checked, and we are in default container, go direct | ||
| if (useContainers && !useAll && e.cookieStoreId === 'firefox-default') | ||
| const cookieStoreId = (e && e.cookieStoreId) ? e.cookieStoreId : "firefox-default"; | ||
| if (useContainers && !useAll && cookieStoreId === 'firefox-default') | ||
| return { type: "direct" }; | ||
| const host = config.getCached("burpProxyHost") || "127.0.0.1"; | ||
| const portStr = config.getCached("burpProxyPort") || "8080"; | ||
| const host = (config.getCached("burpProxyHost") || "127.0.0.1").trim(); | ||
| const portStr = (config.getCached("burpProxyPort") || "8080").toString().trim(); | ||
| const port = parseInt(portStr, 10) || 8080; | ||
@@ -53,4 +54,4 @@ | ||
| type: "http", | ||
| host, | ||
| port | ||
| host: host || "127.0.0.1", | ||
| port: port | ||
| }; | ||
@@ -67,5 +68,7 @@ } | ||
| // Also listen to specific toggle changes | ||
| // Listen to proxy toggle and port changes | ||
| config.onChange('useBurpProxyAll', () => this.maybeStart()) | ||
| config.onChange('useBurpProxyContainer', () => this.maybeStart()) | ||
| config.onChange('burpProxyHost', () => this.maybeStart()) | ||
| config.onChange('burpProxyPort', () => this.maybeStart()) | ||
| } | ||
@@ -75,11 +78,18 @@ | ||
| super.start() | ||
| // Ensure we remove before adding to avoid "already has a listener" errors | ||
| if (browser.proxy.onRequest.hasListener(this.proxy)) { | ||
| browser.proxy.onRequest.removeListener(this.proxy) | ||
| try { | ||
| if (browser.proxy.onRequest.hasListener(this.proxy)) { | ||
| browser.proxy.onRequest.removeListener(this.proxy) | ||
| } | ||
| browser.proxy.onRequest.addListener(this.proxy, { urls: ["<all_urls>"] }) | ||
| } catch (err) { | ||
| console.error("ZeroX Proxy Listener error:", err) | ||
| } | ||
| browser.proxy.onRequest.addListener(this.proxy, { urls: ["<all_urls>"] }) | ||
| } | ||
| stop() { | ||
| browser.proxy.onRequest.removeListener(this.proxy) | ||
| try { | ||
| if (browser.proxy.onRequest.hasListener(this.proxy)) { | ||
| browser.proxy.onRequest.removeListener(this.proxy) | ||
| } | ||
| } catch (err) {} | ||
| super.stop() | ||
@@ -92,7 +102,5 @@ } | ||
| async function colorHeaderHandler(e) { | ||
| if (e.tabId < 0) return | ||
| if (!e || e.tabId < 0) return | ||
| // Use the same color names as in popup.js container colors | ||
| const colorMap = { | ||
@@ -104,2 +112,3 @@ red: "red", | ||
| turquoise: "cyan", | ||
| cyan: "cyan", | ||
| blue: "blue", | ||
@@ -109,13 +118,23 @@ purple: "purple", | ||
| } | ||
| const { cookieStoreId } = await browser.tabs.get(e.tabId) | ||
| if (cookieStoreId === "firefox-default") { | ||
| const cookieStoreId = e.cookieStoreId | ||
| if (!cookieStoreId || cookieStoreId === "firefox-default") { | ||
| return {} | ||
| } | ||
| const identity = await browser.contextualIdentities.get(cookieStoreId) | ||
| if (identity.name.startsWith("ZeroX-")) { | ||
| const name = "X-ZeroX-Color" | ||
| const value = colorMap[identity.color] | ||
| if (value) { | ||
| e.requestHeaders.push({ name, value }) | ||
| try { | ||
| const identity = await browser.contextualIdentities.get(cookieStoreId) | ||
| if (identity && identity.name) { | ||
| const lowerName = identity.name.toLowerCase() | ||
| if (lowerName.startsWith("zerox-")) { | ||
| const colorVal = colorMap[identity.color] || identity.color | ||
| if (colorVal && e.requestHeaders) { | ||
| const filtered = e.requestHeaders.filter(h => h.name.toLowerCase() !== "x-zerox-color") | ||
| filtered.push({ name: "X-ZeroX-Color", value: colorVal }) | ||
| return { requestHeaders: filtered } | ||
| } | ||
| } | ||
| } | ||
| } catch (err) { | ||
| // Ignore contextual identity query errors | ||
| } | ||
@@ -134,10 +153,22 @@ return { requestHeaders: e.requestHeaders } | ||
| browser.webRequest.onBeforeSendHeaders.addListener(colorHeaderHandler, | ||
| { urls: ["<all_urls>"] }, | ||
| ["blocking", "requestHeaders"] | ||
| ); | ||
| try { | ||
| if (browser.webRequest.onBeforeSendHeaders.hasListener(colorHeaderHandler)) { | ||
| browser.webRequest.onBeforeSendHeaders.removeListener(colorHeaderHandler) | ||
| } | ||
| browser.webRequest.onBeforeSendHeaders.addListener( | ||
| colorHeaderHandler, | ||
| { urls: ["<all_urls>"] }, | ||
| ["blocking", "requestHeaders"] | ||
| ) | ||
| } catch (err) { | ||
| console.error("ZeroX AddContainerHeader error:", err) | ||
| } | ||
| } | ||
| stop() { | ||
| browser.webRequest.onBeforeSendHeaders.removeListener(colorHeaderHandler) | ||
| try { | ||
| if (browser.webRequest.onBeforeSendHeaders.hasListener(colorHeaderHandler)) { | ||
| browser.webRequest.onBeforeSendHeaders.removeListener(colorHeaderHandler) | ||
| } | ||
| } catch (err) {} | ||
| super.stop() | ||
@@ -150,11 +181,12 @@ } | ||
| function removeHeaders(response) { | ||
| if (!response || !response.responseHeaders) return {} | ||
| const { responseHeaders: origHeaders } = response | ||
| const blacklistedHeaders = [ | ||
| "Content-Security-Policy", | ||
| "X-XSS-Protection", | ||
| "X-Frame-Options", | ||
| "X-Content-Type-Options" | ||
| "content-security-policy", | ||
| "x-xss-protection", | ||
| "x-frame-options", | ||
| "x-content-type-options" | ||
| ] | ||
| const newHeaders = origHeaders.filter(({ name }) => { | ||
| return !blacklistedHeaders.includes(name) | ||
| return !blacklistedHeaders.includes((name || "").toLowerCase()) | ||
| }) | ||
@@ -174,11 +206,21 @@ return { responseHeaders: newHeaders } | ||
| browser.webRequest.onHeadersReceived.addListener(removeHeaders, | ||
| { urls: ["<all_urls>"] }, | ||
| ["blocking", "responseHeaders"] | ||
| ); | ||
| try { | ||
| if (browser.webRequest.onHeadersReceived.hasListener(removeHeaders)) { | ||
| browser.webRequest.onHeadersReceived.removeListener(removeHeaders) | ||
| } | ||
| browser.webRequest.onHeadersReceived.addListener( | ||
| removeHeaders, | ||
| { urls: ["<all_urls>"] }, | ||
| ["blocking", "responseHeaders"] | ||
| ) | ||
| } catch (err) {} | ||
| } | ||
| stop() { | ||
| try { | ||
| if (browser.webRequest.onHeadersReceived.hasListener(removeHeaders)) { | ||
| browser.webRequest.onHeadersReceived.removeListener(removeHeaders) | ||
| } | ||
| } catch (err) {} | ||
| super.stop() | ||
| browser.webRequest.onHeadersReceived.removeListener(removeHeaders) | ||
| } | ||
@@ -201,3 +243,2 @@ } | ||
| window.addEventListener("message", logMessage); | ||
| } | ||
@@ -204,0 +245,0 @@ |
+74
-17
@@ -0,5 +1,9 @@ | ||
| /* ========================================================================== | ||
| ZeroX DevTools Code Editor & File Selection - Clean Light Theme | ||
| ========================================================================== */ | ||
| .file-list { | ||
| display: flex; | ||
| flex-direction: column; | ||
| justify-items: stretch; | ||
| height: calc(100% - 42px); | ||
| width: 100%; | ||
@@ -10,3 +14,8 @@ } | ||
| display: grid; | ||
| grid-template-columns: 1fr repeat(4, auto); | ||
| grid-template-columns: 1fr repeat(4, 34px); | ||
| background: #f8fafc; | ||
| border-bottom: 1px solid #e2e8f0; | ||
| padding: 6px 10px; | ||
| gap: 6px; | ||
| align-items: center; | ||
| } | ||
@@ -18,22 +27,56 @@ | ||
| .file-list-head select { | ||
| background: #ffffff; | ||
| border: 1px solid #cbd5e1; | ||
| border-radius: 6px; | ||
| padding: 5px 8px; | ||
| color: #0f172a; | ||
| font-family: 'JetBrains Mono', monospace; | ||
| font-size: 11.5px; | ||
| outline: none; | ||
| cursor: pointer; | ||
| transition: all 0.15s ease; | ||
| } | ||
| .file-list-head select:focus { | ||
| border-color: #7c3aed; | ||
| box-shadow: 0 0 0 2px rgba(124, 58, 237, 0.12); | ||
| } | ||
| .file-list-head button { | ||
| background: inherit; | ||
| background: #ffffff; | ||
| border: 1px solid #cbd5e1; | ||
| border-radius: 6px; | ||
| height: 28px; | ||
| width: 100%; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| color: inherit; | ||
| width: 50px; | ||
| color: #475569; | ||
| cursor: pointer; | ||
| transition: all 0.15s ease; | ||
| } | ||
| .file-list-head button:hover { | ||
| filter: invert(50%); | ||
| background: #f1f5f9; | ||
| color: #0f172a; | ||
| border-color: #94a3b8; | ||
| } | ||
| .file-list-head select { | ||
| background: inherit; | ||
| line-height: 1.2em; | ||
| padding: 5px; | ||
| color: inherit; | ||
| .file-list-head button:active { | ||
| transform: scale(0.95); | ||
| } | ||
| .file-list-save:hover { | ||
| color: #059669 !important; | ||
| border-color: #059669 !important; | ||
| background: #ecfdf5 !important; | ||
| } | ||
| .file-list-delete:hover { | ||
| color: #dc2626 !important; | ||
| border-color: #dc2626 !important; | ||
| background: #fef2f2 !important; | ||
| } | ||
| .file-list svg path { | ||
@@ -45,18 +88,32 @@ fill: currentColor; | ||
| display: flex; | ||
| justify-items: stretch; | ||
| flex: 1; | ||
| height: 100%; | ||
| position: relative; | ||
| background: #ffffff; | ||
| } | ||
| .file-list-body textarea { | ||
| flex-grow: 1; | ||
| flex: 1; | ||
| width: 100%; | ||
| height: 100%; | ||
| margin: 0; | ||
| height: 100%; | ||
| padding: 10px; | ||
| padding: 14px 16px; | ||
| background: #ffffff; | ||
| color: #0f172a; | ||
| font-family: 'JetBrains Mono', 'Fira Code', ui-monospace, monospace; | ||
| font-size: 12px; | ||
| line-height: 1.6; | ||
| border: none; | ||
| outline: none; | ||
| resize: none; | ||
| tab-size: 2; | ||
| -webkit-appearance: none; | ||
| } | ||
| .file-list-body textarea:not([disabled]) { | ||
| color: #0369a1; | ||
| } | ||
| .file-list-body textarea.changed { | ||
| box-shadow: 0 0 13px 0px blue inset; | ||
| box-shadow: inset 0 0 0 2px #7c3aed, inset 0 0 8px rgba(124, 58, 237, 0.1); | ||
| } |
+4
-5
@@ -28,11 +28,11 @@ /* Icon */ | ||
| context.beginPath(); | ||
| context.arc(8, 40, 7, 0, 2 * Math.PI, false); | ||
| context.arc(8, 40, 6, 0, 2 * Math.PI, false); | ||
| context.fillStyle = dotColor; | ||
| context.fill(); | ||
| context.lineWidth = 2; | ||
| context.strokeStyle = "black"; | ||
| context.lineWidth = 2.5; | ||
| context.strokeStyle = "#ffffff"; | ||
| context.stroke(); | ||
| /* fox don't cry */ | ||
| if (dotColor === "#ff0000") { | ||
| if (dotColor === "#ff0000" || dotColor === "#ef4444") { | ||
| context.beginPath(); | ||
@@ -44,5 +44,4 @@ context.ellipse(33, 28, .1, .5, 0, 0, 2 * Math.PI); | ||
| } | ||
| } | ||
| return [canvas, context.getImageData(0, 0, width, height)] | ||
| } |
+245
-502
| /* ========================================================================== | ||
| ZeroX Security Toolkit - Obsidian Cyber Luxury Theme (Refined & Minimalist) | ||
| ZeroX - Ultra Clean Minimalist Light UI | ||
| ========================================================================== */ | ||
| :root { | ||
| --bg-base: #090b14; | ||
| --bg-surface: rgba(17, 22, 38, 0.75); | ||
| --bg-surface-hover: rgba(26, 33, 56, 0.88); | ||
| --bg-card: rgba(22, 28, 48, 0.65); | ||
| --bg-input: rgba(10, 13, 24, 0.85); | ||
| --bg-base: #ffffff; | ||
| --bg-subtle: #f8fafc; | ||
| --bg-hover: #f1f5f9; | ||
| --border-subtle: rgba(255, 255, 255, 0.07); | ||
| --border-card: rgba(255, 255, 255, 0.09); | ||
| --border-focus: rgba(168, 85, 247, 0.6); | ||
| --text-primary: #f8fafc; | ||
| --text-secondary: #94a3b8; | ||
| --text-main: #0f172a; | ||
| --text-muted: #64748b; | ||
| --text-faint: #94a3b8; | ||
| --accent-purple: #a855f7; | ||
| --accent-purple-dark: #7c3aed; | ||
| --accent-purple-glow: rgba(168, 85, 247, 0.4); | ||
| --accent-purple: #7c3aed; | ||
| --accent-purple-light: #f5f3ff; | ||
| --accent-purple-border: #ddd6fe; | ||
| --accent-cyan: #06b6d4; | ||
| --accent-emerald: #10b981; | ||
| --accent-rose: #f43f5e; | ||
| --accent-green: #10b981; | ||
| --accent-green-bg: #ecfdf5; | ||
| --font-stack: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Inter", "Helvetica Neue", Arial, sans-serif; | ||
| --transition-fast: 0.15s cubic-bezier(0.4, 0, 0.2, 1); | ||
| --transition-smooth: 0.25s cubic-bezier(0.16, 1, 0.3, 1); | ||
| --accent-blue: #0284c7; | ||
| --accent-blue-bg: #f0f9ff; | ||
| --accent-blue-border: #bae6fd; | ||
| --radius-sm: 8px; | ||
| --radius-md: 12px; | ||
| --radius-lg: 16px; | ||
| --radius-full: 9999px; | ||
| --border: #e2e8f0; | ||
| --border-hover: #cbd5e1; | ||
| --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | ||
| --font-mono: 'JetBrains Mono', monospace; | ||
| --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04); | ||
| } | ||
@@ -43,46 +39,26 @@ | ||
| -webkit-font-smoothing: antialiased; | ||
| -moz-osx-font-smoothing: grayscale; | ||
| } | ||
| html, body { | ||
| width: 380px; | ||
| body { | ||
| width: 320px; | ||
| background-color: var(--bg-base); | ||
| background-image: | ||
| radial-gradient(circle at 15% 0%, rgba(168, 85, 247, 0.15) 0%, transparent 45%), | ||
| radial-gradient(circle at 85% 100%, rgba(59, 130, 246, 0.1) 0%, transparent 45%), | ||
| radial-gradient(circle at 50% 50%, rgba(15, 23, 42, 0.3) 0%, transparent 100%); | ||
| color: var(--text-primary); | ||
| font-family: var(--font-stack); | ||
| color: var(--text-main); | ||
| font-family: var(--font-sans); | ||
| font-size: 13px; | ||
| line-height: 1.4; | ||
| user-select: none; | ||
| overflow-x: hidden; | ||
| overflow-y: auto; | ||
| scrollbar-width: none; | ||
| user-select: none; | ||
| } | ||
| ::-webkit-scrollbar { | ||
| display: none; | ||
| width: 0; | ||
| height: 0; | ||
| } | ||
| .app-container { | ||
| padding: 16px 16px 14px 16px; | ||
| padding: 14px 16px 12px 16px; | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 12px; | ||
| min-height: 100%; | ||
| } | ||
| /* ========================================================================== | ||
| Clean & Simple Header | ||
| Header | ||
| ========================================================================== */ | ||
| .app-header { | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 10px; | ||
| } | ||
| .header-main { | ||
| header { | ||
| display: flex; | ||
@@ -93,3 +69,3 @@ align-items: center; | ||
| .brand { | ||
| .header-left { | ||
| display: flex; | ||
@@ -103,384 +79,212 @@ align-items: center; | ||
| height: 32px; | ||
| border-radius: 9px; | ||
| background: linear-gradient(135deg, rgba(168, 85, 247, 0.22) 0%, rgba(59, 130, 246, 0.15) 100%); | ||
| border: 1px solid rgba(168, 85, 247, 0.35); | ||
| border-radius: 8px; | ||
| background: #7c3aed; | ||
| color: #ffffff; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| color: #c084fc; | ||
| box-shadow: 0 0 12px rgba(168, 85, 247, 0.2); | ||
| flex-shrink: 0; | ||
| transition: transform var(--transition-smooth); | ||
| transition: all 0.2s ease; | ||
| } | ||
| .logo-box:hover { | ||
| transform: scale(1.06); | ||
| .logo-box.inactive { | ||
| background: #94a3b8; | ||
| } | ||
| .brand-info { | ||
| .brand-text { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 8px; | ||
| flex-direction: column; | ||
| gap: 1px; | ||
| } | ||
| .brand-name { | ||
| font-size: 17px; | ||
| font-size: 15px; | ||
| font-weight: 700; | ||
| letter-spacing: -0.3px; | ||
| color: #ffffff; | ||
| color: var(--text-main); | ||
| letter-spacing: -0.02em; | ||
| line-height: 1.1; | ||
| } | ||
| .status-badge { | ||
| display: inline-flex; | ||
| .status-row { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 5px; | ||
| background: rgba(244, 63, 94, 0.1); | ||
| border: 1px solid rgba(244, 63, 94, 0.25); | ||
| padding: 2px 7px; | ||
| border-radius: 6px; | ||
| transition: var(--transition-smooth); | ||
| } | ||
| .status-dot { | ||
| width: 5px; | ||
| height: 5px; | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 50%; | ||
| background-color: var(--accent-rose); | ||
| box-shadow: 0 0 6px rgba(244, 63, 94, 0.6); | ||
| transition: var(--transition-smooth); | ||
| background: var(--accent-green); | ||
| transition: all 0.2s ease; | ||
| } | ||
| .status-dot.inactive { | ||
| background: #94a3b8; | ||
| } | ||
| .status-text { | ||
| font-size: 10.5px; | ||
| font-size: 11px; | ||
| font-weight: 600; | ||
| letter-spacing: 0.2px; | ||
| color: #fda4af; | ||
| transition: var(--transition-smooth); | ||
| color: var(--text-muted); | ||
| } | ||
| /* Master Active State */ | ||
| .status-badge.active { | ||
| background: rgba(16, 185, 129, 0.12); | ||
| border-color: rgba(16, 185, 129, 0.35); | ||
| .status-text.active { | ||
| color: #059669; | ||
| } | ||
| .status-badge.active .status-dot { | ||
| background-color: var(--accent-emerald); | ||
| box-shadow: 0 0 6px rgba(16, 185, 129, 0.8); | ||
| animation: pulse-dot 2s infinite ease-in-out; | ||
| } | ||
| .status-badge.active .status-text { | ||
| color: #6ee7b7; | ||
| } | ||
| @keyframes pulse-dot { | ||
| 0%, 100% { transform: scale(1); opacity: 1; } | ||
| 50% { transform: scale(1.25); opacity: 0.8; } | ||
| } | ||
| /* Master Toggle Switch */ | ||
| .switch-master { | ||
| /* Toggle Switch */ | ||
| .toggle-switch { | ||
| width: 40px; | ||
| height: 22px; | ||
| position: relative; | ||
| display: inline-block; | ||
| width: 46px; | ||
| height: 24px; | ||
| cursor: pointer; | ||
| display: block; | ||
| } | ||
| .switch-master input { | ||
| .toggle-switch input { | ||
| opacity: 0; | ||
| width: 0; | ||
| height: 0; | ||
| position: absolute; | ||
| } | ||
| .switch-slider { | ||
| .slider { | ||
| position: absolute; | ||
| top: 0; left: 0; right: 0; bottom: 0; | ||
| background: rgba(255, 255, 255, 0.08); | ||
| border: 1px solid var(--border-card); | ||
| border-radius: var(--radius-full); | ||
| transition: var(--transition-smooth); | ||
| inset: 0; | ||
| background: #e2e8f0; | ||
| border-radius: 99px; | ||
| transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1); | ||
| } | ||
| .switch-slider:before { | ||
| .slider:before { | ||
| content: ""; | ||
| position: absolute; | ||
| content: ""; | ||
| height: 18px; | ||
| width: 18px; | ||
| left: 2px; | ||
| bottom: 2px; | ||
| background: #64748b; | ||
| height: 16px; | ||
| width: 16px; | ||
| left: 3px; | ||
| bottom: 3px; | ||
| background: #ffffff; | ||
| border-radius: 50%; | ||
| transition: var(--transition-smooth); | ||
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.35); | ||
| transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1); | ||
| box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15); | ||
| } | ||
| .switch-master input:checked + .switch-slider { | ||
| background: linear-gradient(135deg, #10b981 0%, #059669 100%); | ||
| border-color: rgba(16, 185, 129, 0.5); | ||
| box-shadow: 0 0 14px rgba(16, 185, 129, 0.45); | ||
| input:checked + .slider { | ||
| background: #7c3aed; | ||
| } | ||
| .switch-master input:checked + .switch-slider:before { | ||
| transform: translateX(22px); | ||
| background: #ffffff; | ||
| input:checked + .slider:before { | ||
| transform: translateX(18px); | ||
| } | ||
| /* ========================================================================== | ||
| Burp Suite Target Card | ||
| Burp Bar | ||
| ========================================================================== */ | ||
| .target-card { | ||
| background: var(--bg-surface); | ||
| border: 1px solid var(--border-card); | ||
| border-radius: var(--radius-md); | ||
| padding: 9px 12px; | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 7px; | ||
| backdrop-filter: blur(14px); | ||
| box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25); | ||
| transition: var(--transition-smooth); | ||
| } | ||
| .target-card:hover { | ||
| border-color: rgba(168, 85, 247, 0.3); | ||
| } | ||
| .target-card-header { | ||
| .burp-bar { | ||
| background: var(--bg-subtle); | ||
| border: 1px solid var(--border); | ||
| border-radius: 8px; | ||
| padding: 6px 10px; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| cursor: pointer; | ||
| transition: all 0.15s ease; | ||
| } | ||
| .target-title-area { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 6px; | ||
| font-size: 10.5px; | ||
| font-weight: 700; | ||
| color: var(--text-secondary); | ||
| letter-spacing: 0.6px; | ||
| text-transform: uppercase; | ||
| .burp-bar:hover { | ||
| background: #ffffff; | ||
| border-color: var(--border-hover); | ||
| } | ||
| .icon-burp { | ||
| color: var(--accent-purple); | ||
| } | ||
| .preset-chips { | ||
| .burp-label-group { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 4px; | ||
| } | ||
| .port-preset { | ||
| background: rgba(255, 255, 255, 0.05); | ||
| border: 1px solid var(--border-subtle); | ||
| border-radius: 6px; | ||
| color: var(--text-secondary); | ||
| font-family: ui-monospace, SFMono-Regular, monospace; | ||
| font-size: 10.5px; | ||
| gap: 6px; | ||
| font-size: 11.5px; | ||
| font-weight: 600; | ||
| padding: 2px 6px; | ||
| cursor: pointer; | ||
| transition: var(--transition-fast); | ||
| color: var(--text-main); | ||
| } | ||
| .port-preset:hover { | ||
| background: rgba(168, 85, 247, 0.2); | ||
| border-color: rgba(168, 85, 247, 0.4); | ||
| color: #ffffff; | ||
| transform: translateY(-1px); | ||
| .burp-icon { | ||
| color: #ea580c; | ||
| } | ||
| .port-preset.active { | ||
| background: rgba(168, 85, 247, 0.35); | ||
| border-color: var(--accent-purple); | ||
| color: #ffffff; | ||
| } | ||
| .icon-btn { | ||
| background: transparent; | ||
| border: none; | ||
| color: var(--text-secondary); | ||
| padding: 3px; | ||
| border-radius: 6px; | ||
| cursor: pointer; | ||
| .address-box { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| transition: var(--transition-fast); | ||
| gap: 4px; | ||
| } | ||
| .icon-btn:hover { | ||
| color: #ffffff; | ||
| background: rgba(255, 255, 255, 0.08); | ||
| } | ||
| /* Address Chip */ | ||
| .address-chip { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| background: var(--bg-input); | ||
| border: 1px solid var(--border-subtle); | ||
| border-radius: var(--radius-sm); | ||
| padding: 6px 10px; | ||
| cursor: pointer; | ||
| transition: var(--transition-fast); | ||
| } | ||
| .address-chip:hover { | ||
| border-color: var(--border-focus); | ||
| background: rgba(15, 19, 34, 0.95); | ||
| } | ||
| .address-chip-left { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 8px; | ||
| flex: 1; | ||
| } | ||
| .pulse-indicator { | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 50%; | ||
| background: var(--accent-cyan); | ||
| box-shadow: 0 0 6px var(--accent-cyan); | ||
| flex-shrink: 0; | ||
| } | ||
| .address-val { | ||
| font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; | ||
| font-size: 12px; | ||
| #address-text { | ||
| font-family: var(--font-mono); | ||
| font-size: 11px; | ||
| font-weight: 600; | ||
| color: #e2e8f0; | ||
| letter-spacing: 0.3px; | ||
| color: var(--accent-blue); | ||
| background: var(--accent-blue-bg); | ||
| padding: 1px 6px; | ||
| border-radius: 4px; | ||
| border: 1px solid var(--accent-blue-border); | ||
| } | ||
| .edit-icon { | ||
| color: var(--text-muted); | ||
| opacity: 0.6; | ||
| transition: var(--transition-fast); | ||
| color: var(--text-faint); | ||
| } | ||
| .address-chip:hover .edit-icon { | ||
| color: var(--accent-purple); | ||
| opacity: 1; | ||
| } | ||
| .address-input { | ||
| flex: 1; | ||
| background: transparent; | ||
| border: none; | ||
| background: #ffffff; | ||
| border: 1px solid var(--accent-purple); | ||
| border-radius: 4px; | ||
| color: var(--text-main); | ||
| font-size: 11px; | ||
| font-family: var(--font-mono); | ||
| font-weight: 600; | ||
| padding: 1px 5px; | ||
| width: 120px; | ||
| outline: none; | ||
| color: #ffffff; | ||
| font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; | ||
| font-size: 12px; | ||
| font-weight: 600; | ||
| width: 100%; | ||
| text-align: right; | ||
| } | ||
| /* ========================================================================== | ||
| Section Headers | ||
| ========================================================================= */ | ||
| .section-header { | ||
| Options List | ||
| ========================================================================== */ | ||
| main { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| margin-top: 2px; | ||
| margin-bottom: 1px; | ||
| flex-direction: column; | ||
| gap: 12px; | ||
| transition: opacity 0.2s ease; | ||
| } | ||
| .section-title { | ||
| font-size: 10px; | ||
| font-weight: 700; | ||
| letter-spacing: 0.8px; | ||
| color: var(--text-muted); | ||
| main.disabled { | ||
| opacity: 0.45; | ||
| pointer-events: none; | ||
| } | ||
| .section-badge { | ||
| font-size: 9px; | ||
| font-weight: 700; | ||
| letter-spacing: 0.5px; | ||
| color: var(--text-secondary); | ||
| background: rgba(255, 255, 255, 0.06); | ||
| padding: 1px 5px; | ||
| border-radius: 4px; | ||
| } | ||
| /* ========================================================================== | ||
| Feature Cards & Toggles | ||
| ========================================================================== */ | ||
| .feature-cards { | ||
| .options-list { | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 7px; | ||
| gap: 4px; | ||
| } | ||
| .feature-card { | ||
| background: var(--bg-card); | ||
| border: 1px solid var(--border-card); | ||
| border-radius: var(--radius-md); | ||
| padding: 9px 12px; | ||
| .option-item { | ||
| padding: 7px 10px; | ||
| border-radius: 7px; | ||
| border: 1px solid var(--border); | ||
| background: #ffffff; | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 11px; | ||
| justify-content: space-between; | ||
| cursor: pointer; | ||
| transition: var(--transition-smooth); | ||
| position: relative; | ||
| overflow: hidden; | ||
| transition: all 0.15s ease; | ||
| } | ||
| .feature-card:hover { | ||
| background: var(--bg-surface-hover); | ||
| border-color: rgba(168, 85, 247, 0.35); | ||
| transform: translateY(-1px); | ||
| box-shadow: 0 4px 14px rgba(0, 0, 0, 0.28); | ||
| .option-item:hover { | ||
| background: var(--bg-subtle); | ||
| border-color: var(--border-hover); | ||
| } | ||
| .feature-icon { | ||
| width: 30px; | ||
| height: 30px; | ||
| border-radius: 9px; | ||
| .option-info { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| flex-shrink: 0; | ||
| transition: transform var(--transition-fast); | ||
| } | ||
| .feature-card:hover .feature-icon { | ||
| transform: scale(1.06); | ||
| } | ||
| .bg-purple { | ||
| background: rgba(168, 85, 247, 0.15); | ||
| color: #c084fc; | ||
| border: 1px solid rgba(168, 85, 247, 0.3); | ||
| } | ||
| .bg-blue { | ||
| background: rgba(59, 130, 246, 0.15); | ||
| color: #60a5fa; | ||
| border: 1px solid rgba(59, 130, 246, 0.3); | ||
| } | ||
| .bg-emerald { | ||
| background: rgba(16, 185, 129, 0.15); | ||
| color: #34d399; | ||
| border: 1px solid rgba(16, 185, 129, 0.3); | ||
| } | ||
| .bg-rose { | ||
| background: rgba(244, 63, 94, 0.15); | ||
| color: #fb7185; | ||
| border: 1px solid rgba(244, 63, 94, 0.3); | ||
| } | ||
| .feature-info { | ||
| flex: 1; | ||
| display: flex; | ||
| flex-direction: column; | ||
@@ -490,24 +294,34 @@ gap: 1px; | ||
| .feature-name { | ||
| .option-name { | ||
| font-size: 12px; | ||
| font-weight: 600; | ||
| color: #ffffff; | ||
| letter-spacing: -0.2px; | ||
| color: var(--text-main); | ||
| } | ||
| .feature-desc { | ||
| font-size: 10px; | ||
| color: var(--text-secondary); | ||
| .option-hint { | ||
| font-size: 10.5px; | ||
| color: var(--text-muted); | ||
| } | ||
| /* Feature Toggle Control */ | ||
| .toggle-control { | ||
| .option-hint code { | ||
| font-family: var(--font-mono); | ||
| font-size: 9.5px; | ||
| background: var(--bg-subtle); | ||
| padding: 0 3px; | ||
| border-radius: 3px; | ||
| color: var(--accent-purple); | ||
| } | ||
| /* Checkbox */ | ||
| .checkbox-box { | ||
| position: relative; | ||
| width: 36px; | ||
| height: 20px; | ||
| width: 16px; | ||
| height: 16px; | ||
| flex-shrink: 0; | ||
| } | ||
| .toggle-input { | ||
| .custom-checkbox { | ||
| position: absolute; | ||
| opacity: 0; | ||
| cursor: pointer; | ||
| width: 0; | ||
@@ -517,73 +331,64 @@ height: 0; | ||
| .toggle-slider { | ||
| .checkbox-mark { | ||
| position: absolute; | ||
| top: 0; left: 0; right: 0; bottom: 0; | ||
| background: rgba(255, 255, 255, 0.1); | ||
| border: 1px solid var(--border-subtle); | ||
| border-radius: var(--radius-full); | ||
| transition: var(--transition-fast); | ||
| inset: 0; | ||
| background: #ffffff; | ||
| border: 1.5px solid #cbd5e1; | ||
| border-radius: 4px; | ||
| transition: all 0.15s ease; | ||
| } | ||
| .toggle-slider:before { | ||
| position: absolute; | ||
| content: ""; | ||
| height: 14px; | ||
| width: 14px; | ||
| left: 2px; | ||
| bottom: 2px; | ||
| background: #94a3b8; | ||
| border-radius: 50%; | ||
| transition: var(--transition-fast); | ||
| .option-item:hover .checkbox-mark { | ||
| border-color: var(--accent-purple); | ||
| } | ||
| .toggle-input:checked + .toggle-slider { | ||
| background: var(--accent-purple); | ||
| border-color: var(--accent-purple); | ||
| box-shadow: 0 0 10px var(--accent-purple-glow); | ||
| .custom-checkbox:checked ~ .checkbox-mark { | ||
| background: #7c3aed; | ||
| border-color: #7c3aed; | ||
| } | ||
| .toggle-input:checked + .toggle-slider:before { | ||
| transform: translateX(16px); | ||
| background: #ffffff; | ||
| .checkbox-mark:after { | ||
| content: ""; | ||
| position: absolute; | ||
| display: none; | ||
| left: 4.5px; | ||
| top: 1.5px; | ||
| width: 3.5px; | ||
| height: 7px; | ||
| border: solid white; | ||
| border-width: 0 1.5px 1.5px 0; | ||
| transform: rotate(45deg); | ||
| } | ||
| .custom-checkbox:checked ~ .checkbox-mark:after { | ||
| display: block; | ||
| } | ||
| /* ========================================================================== | ||
| Container Launcher Grid | ||
| Containers Grid | ||
| ========================================================================== */ | ||
| .containers-grid { | ||
| display: grid; | ||
| grid-template-columns: repeat(4, 1fr); | ||
| gap: 7px; | ||
| margin-top: 3px; | ||
| } | ||
| .container-btn { | ||
| background: var(--bg-card); | ||
| border: 1px solid var(--border-card); | ||
| border-radius: var(--radius-md); | ||
| padding: 7px 4px; | ||
| .containers-section { | ||
| display: flex; | ||
| flex-direction: column; | ||
| align-items: center; | ||
| justify-content: center; | ||
| gap: 4px; | ||
| cursor: pointer; | ||
| transition: var(--transition-fast); | ||
| position: relative; | ||
| overflow: hidden; | ||
| gap: 6px; | ||
| } | ||
| .container-btn:hover { | ||
| background: var(--bg-surface-hover); | ||
| transform: translateY(-2px); | ||
| box-shadow: 0 5px 14px rgba(0, 0, 0, 0.35); | ||
| .section-title { | ||
| font-size: 9.5px; | ||
| font-weight: 700; | ||
| color: var(--text-muted); | ||
| letter-spacing: 0.06em; | ||
| } | ||
| .container-btn:active { | ||
| transform: scale(0.95); | ||
| .containers-grid { | ||
| display: grid; | ||
| grid-template-columns: repeat(8, 1fr); | ||
| gap: 5px; | ||
| } | ||
| .container-swatch { | ||
| width: 20px; | ||
| height: 20px; | ||
| .identity { | ||
| height: 28px; | ||
| background: #ffffff; | ||
| border: 1px solid var(--border); | ||
| border-radius: 6px; | ||
@@ -593,55 +398,30 @@ display: flex; | ||
| justify-content: center; | ||
| box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); | ||
| transition: transform var(--transition-fast); | ||
| cursor: pointer; | ||
| transition: all 0.15s ease; | ||
| } | ||
| .container-btn:hover .container-swatch { | ||
| transform: scale(1.12); | ||
| .identity:hover { | ||
| background: var(--bg-subtle); | ||
| border-color: var(--border-hover); | ||
| transform: translateY(-1px); | ||
| } | ||
| .container-label { | ||
| font-size: 9.5px; | ||
| font-weight: 600; | ||
| color: var(--text-secondary); | ||
| text-transform: capitalize; | ||
| letter-spacing: 0.2px; | ||
| .identity:active { | ||
| transform: scale(0.92); | ||
| } | ||
| .container-btn:hover .container-label { | ||
| color: #ffffff; | ||
| .identity-glyph { | ||
| width: 10px; | ||
| height: 10px; | ||
| border-radius: 50%; | ||
| } | ||
| /* Chromatic Variants */ | ||
| .container-btn[data-color="red"] .container-swatch { | ||
| background: linear-gradient(135deg, #f87171, #ef4444); | ||
| box-shadow: 0 0 10px rgba(239, 68, 68, 0.45); | ||
| } | ||
| .container-btn[data-color="orange"] .container-swatch { | ||
| background: linear-gradient(135deg, #fb923c, #f97316); | ||
| box-shadow: 0 0 10px rgba(249, 115, 22, 0.45); | ||
| } | ||
| .container-btn[data-color="yellow"] .container-swatch { | ||
| background: linear-gradient(135deg, #fde047, #eab308); | ||
| box-shadow: 0 0 10px rgba(234, 179, 8, 0.45); | ||
| } | ||
| .container-btn[data-color="green"] .container-swatch { | ||
| background: linear-gradient(135deg, #4ade80, #22c55e); | ||
| box-shadow: 0 0 10px rgba(34, 197, 94, 0.45); | ||
| } | ||
| .container-btn[data-color="cyan"] .container-swatch { | ||
| background: linear-gradient(135deg, #22d3ee, #06b6d4); | ||
| box-shadow: 0 0 10px rgba(6, 182, 212, 0.45); | ||
| } | ||
| .container-btn[data-color="blue"] .container-swatch { | ||
| background: linear-gradient(135deg, #60a5fa, #3b82f6); | ||
| box-shadow: 0 0 10px rgba(59, 130, 246, 0.45); | ||
| } | ||
| .container-btn[data-color="purple"] .container-swatch { | ||
| background: linear-gradient(135deg, #c084fc, #a855f7); | ||
| box-shadow: 0 0 10px rgba(168, 85, 247, 0.45); | ||
| } | ||
| .container-btn[data-color="pink"] .container-swatch { | ||
| background: linear-gradient(135deg, #f472b6, #ec4899); | ||
| box-shadow: 0 0 10px rgba(236, 72, 153, 0.45); | ||
| } | ||
| .identity.red .identity-glyph { background: #ef4444; } | ||
| .identity.orange .identity-glyph { background: #f97316; } | ||
| .identity.yellow .identity-glyph { background: #eab308; } | ||
| .identity.green .identity-glyph { background: #10b981; } | ||
| .identity.cyan .identity-glyph { background: #06b6d4; } | ||
| .identity.blue .identity-glyph { background: #3b82f6; } | ||
| .identity.purple .identity-glyph { background: #8b5cf6; } | ||
| .identity.pink .identity-glyph { background: #ec4899; } | ||
@@ -651,3 +431,4 @@ /* ========================================================================== | ||
| ========================================================================== */ | ||
| .app-footer { | ||
| footer { | ||
| display: flex; | ||
@@ -657,69 +438,31 @@ align-items: center; | ||
| padding-top: 8px; | ||
| border-top: 1px solid var(--border-subtle); | ||
| margin-top: 2px; | ||
| border-top: 1px solid #f1f5f9; | ||
| } | ||
| .version-tag { | ||
| font-size: 10px; | ||
| font-size: 10.5px; | ||
| font-family: var(--font-mono); | ||
| color: var(--text-faint); | ||
| font-weight: 600; | ||
| color: var(--text-muted); | ||
| letter-spacing: 0.2px; | ||
| } | ||
| .footer-link-btn { | ||
| background: transparent; | ||
| border: none; | ||
| color: var(--text-secondary); | ||
| font-size: 11px; | ||
| font-weight: 600; | ||
| display: flex; | ||
| .settings-btn { | ||
| display: inline-flex; | ||
| align-items: center; | ||
| gap: 4px; | ||
| cursor: pointer; | ||
| padding: 3px 6px; | ||
| border-radius: var(--radius-sm); | ||
| transition: var(--transition-fast); | ||
| } | ||
| .footer-link-btn:hover { | ||
| color: #ffffff; | ||
| background: rgba(255, 255, 255, 0.08); | ||
| } | ||
| /* ========================================================================== | ||
| Disabled State (When Master Switch is OFF) | ||
| ========================================================================== */ | ||
| .app-main.disabled { | ||
| opacity: 0.45; | ||
| filter: grayscale(0.4); | ||
| pointer-events: none; | ||
| transition: opacity var(--transition-smooth), filter var(--transition-smooth); | ||
| } | ||
| /* ========================================================================== | ||
| Toast Notification | ||
| ========================================================================== */ | ||
| .toast { | ||
| position: fixed; | ||
| bottom: 12px; | ||
| left: 50%; | ||
| transform: translateX(-50%) translateY(40px); | ||
| background: rgba(15, 23, 42, 0.95); | ||
| border: 1px solid rgba(168, 85, 247, 0.5); | ||
| color: #ffffff; | ||
| font-size: 11px; | ||
| color: var(--text-muted); | ||
| text-decoration: none; | ||
| font-weight: 600; | ||
| padding: 6px 12px; | ||
| border-radius: var(--radius-full); | ||
| box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5), 0 0 12px rgba(168, 85, 247, 0.3); | ||
| pointer-events: none; | ||
| opacity: 0; | ||
| transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.25s ease; | ||
| z-index: 100; | ||
| white-space: nowrap; | ||
| padding: 3px 8px; | ||
| border-radius: 5px; | ||
| background: var(--bg-subtle); | ||
| border: 1px solid var(--border); | ||
| transition: all 0.15s ease; | ||
| } | ||
| .toast.show { | ||
| transform: translateX(-50%) translateY(0); | ||
| opacity: 1; | ||
| .settings-btn:hover { | ||
| color: var(--accent-purple); | ||
| background: var(--accent-purple-light); | ||
| border-color: var(--accent-purple-border); | ||
| } |
+86
-148
@@ -6,186 +6,124 @@ <!DOCTYPE html> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <title>ZeroX Security Toolkit</title> | ||
| <title>ZeroX</title> | ||
| <link rel="stylesheet" href="popup.css"> | ||
| <link rel="preconnect" href="https://fonts.googleapis.com"> | ||
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | ||
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@500;600&display=swap" rel="stylesheet"> | ||
| </head> | ||
| <body> | ||
| <div class="app-container"> | ||
| <!-- Sleek Minimalist Header --> | ||
| <header class="app-header"> | ||
| <div class="header-main"> | ||
| <div class="brand"> | ||
| <div class="logo-box" id="brand-logo" title="ZeroX Security Toolkit"> | ||
| <svg class="logo-icon" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"> | ||
| <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/> | ||
| <path d="M9.5 9.5l5 5m0-5l-5 5"/> | ||
| </svg> | ||
| <!-- Header --> | ||
| <header> | ||
| <div class="header-left"> | ||
| <div class="logo-box" id="logo-box"> | ||
| <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"> | ||
| <path d="M12 2L3 7v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V7l-9-5z"/> | ||
| <path d="M12 8v8"/> | ||
| <path d="M8 12h8"/> | ||
| </svg> | ||
| </div> | ||
| <div class="brand-text"> | ||
| <h1 class="brand-name">ZeroX</h1> | ||
| <div class="status-row"> | ||
| <span class="status-dot" id="status-dot"></span> | ||
| <span class="status-text" id="status-text">Active</span> | ||
| </div> | ||
| <div class="brand-info"> | ||
| <span class="brand-name">ZeroX</span> | ||
| <div class="status-badge" id="status-pill"> | ||
| <span class="status-dot"></span> | ||
| <span class="status-text" id="status-text">Standby</span> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| <!-- Master Toggle Switch --> | ||
| <div class="master-toggle-wrapper"> | ||
| <label class="switch-master" title="Toggle ZeroX Interception"> | ||
| <input id="option-enabled" type="checkbox"> | ||
| <span class="switch-slider"></span> | ||
| </label> | ||
| </div> | ||
| </div> | ||
| <!-- Burp Suite Target Card --> | ||
| <div class="target-card" id="burp-target-card"> | ||
| <div class="target-card-header"> | ||
| <div class="target-title-area"> | ||
| <svg class="icon-burp" viewBox="0 0 24 24" width="13" height="13" fill="currentColor"> | ||
| <path d="M20 7h-4V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2H4a2 2 0 0 0-2 2v11a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2zM10 5h4v2h-4V5zM4 9h16v3h-16V9zm0 11v-6h16v6H4z"/> | ||
| </svg> | ||
| <span>Burp Proxy</span> | ||
| </div> | ||
| <div class="preset-chips"> | ||
| <button type="button" class="port-preset" data-port="8080" title="Set Port 8080">8080</button> | ||
| <button type="button" class="port-preset" data-port="8081" title="Set Port 8081">8081</button> | ||
| <button type="button" class="port-preset" data-port="8888" title="Set Port 8888">8888</button> | ||
| <button type="button" class="icon-btn" id="btn-copy-address" title="Copy Address"> | ||
| <svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2"> | ||
| <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect> | ||
| <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path> | ||
| </svg> | ||
| </button> | ||
| </div> | ||
| </div> | ||
| <label class="toggle-switch" title="Toggle Extension"> | ||
| <input id="option-enabled" type="checkbox"> | ||
| <span class="slider"></span> | ||
| </label> | ||
| </header> | ||
| <div class="address-chip" id="address-chip" title="Click to edit IP:Port"> | ||
| <div class="address-chip-left"> | ||
| <span class="pulse-indicator"></span> | ||
| <span class="address-val" id="address-text">127.0.0.1:8080</span> | ||
| <input type="text" id="address-input" class="address-input" placeholder="127.0.0.1:8080" spellcheck="false" style="display: none;"> | ||
| </div> | ||
| <svg class="edit-icon" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2"> | ||
| <path d="M12 20h9"></path> | ||
| <path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path> | ||
| </svg> | ||
| </div> | ||
| <!-- Burp Target Bar --> | ||
| <div class="burp-bar" id="burp-status-bar" title="Click to edit Burp Proxy address"> | ||
| <div class="burp-label-group"> | ||
| <svg class="burp-icon" viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | ||
| <rect x="2" y="7" width="20" height="14" rx="2" ry="2"/> | ||
| <path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"/> | ||
| </svg> | ||
| <span>Burp Proxy</span> | ||
| </div> | ||
| </header> | ||
| <div class="address-box"> | ||
| <span id="address-text">127.0.0.1:8080</span> | ||
| <input type="text" id="address-input" class="address-input" style="display: none;" placeholder="127.0.0.1:8080" spellcheck="false"> | ||
| <svg class="edit-icon" viewBox="0 0 24 24" width="10" height="10" fill="currentColor"> | ||
| <path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/> | ||
| </svg> | ||
| </div> | ||
| </div> | ||
| <!-- Main Content --> | ||
| <main class="app-main" id="main-content"> | ||
| <!-- Interception Rules Section --> | ||
| <div class="section-header"> | ||
| <span class="section-title">ROUTING & INTERCEPTION</span> | ||
| </div> | ||
| <div class="feature-cards"> | ||
| <!-- Option 1: All Tabs --> | ||
| <label class="feature-card"> | ||
| <div class="feature-icon bg-purple"> | ||
| <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2"> | ||
| <circle cx="12" cy="12" r="10"></circle> | ||
| <line x1="2" y1="12" x2="22" y2="12"></line> | ||
| <path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path> | ||
| </svg> | ||
| <main id="main-content"> | ||
| <div class="options-list"> | ||
| <label class="option-item"> | ||
| <div class="option-info"> | ||
| <span class="option-name">All Tabs</span> | ||
| <span class="option-hint">Route all browser HTTP/HTTPS traffic to Burp</span> | ||
| </div> | ||
| <div class="feature-info"> | ||
| <div class="feature-name">All Tabs Proxy</div> | ||
| <div class="feature-desc">Route all browser traffic via Burp</div> | ||
| <div class="checkbox-box"> | ||
| <input id="option-useBurpProxyAll" type="checkbox" class="custom-checkbox"> | ||
| <span class="checkbox-mark"></span> | ||
| </div> | ||
| <div class="toggle-control"> | ||
| <input id="option-useBurpProxyAll" type="checkbox" class="toggle-input"> | ||
| <span class="toggle-slider"></span> | ||
| </div> | ||
| </label> | ||
| <!-- Option 2: Containers Only --> | ||
| <label class="feature-card"> | ||
| <div class="feature-icon bg-blue"> | ||
| <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2"> | ||
| <polygon points="12 2 2 7 12 12 22 7 12 2"></polygon> | ||
| <polyline points="2 17 12 22 22 17"></polyline> | ||
| <polyline points="2 12 12 17 22 12"></polyline> | ||
| </svg> | ||
| <label class="option-item"> | ||
| <div class="option-info"> | ||
| <span class="option-name">Containers Only</span> | ||
| <span class="option-hint">Proxy ZeroX container tabs only</span> | ||
| </div> | ||
| <div class="feature-info"> | ||
| <div class="feature-name">Containers Only</div> | ||
| <div class="feature-desc">Route only ZeroX container tabs</div> | ||
| <div class="checkbox-box"> | ||
| <input id="option-useBurpProxyContainer" type="checkbox" class="custom-checkbox"> | ||
| <span class="checkbox-mark"></span> | ||
| </div> | ||
| <div class="toggle-control"> | ||
| <input id="option-useBurpProxyContainer" type="checkbox" class="toggle-input"> | ||
| <span class="toggle-slider"></span> | ||
| </div> | ||
| </label> | ||
| <!-- Option 3: Color Header --> | ||
| <label class="feature-card"> | ||
| <div class="feature-icon bg-emerald"> | ||
| <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2"> | ||
| <path d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z"></path> | ||
| <line x1="7" y1="7" x2="7.01" y2="7"></line> | ||
| </svg> | ||
| <label class="option-item"> | ||
| <div class="option-info"> | ||
| <span class="option-name">Color Header</span> | ||
| <span class="option-hint">Inject <code>X-ZeroX-Color</code> for Montoya sync</span> | ||
| </div> | ||
| <div class="feature-info"> | ||
| <div class="feature-name">Inject Color Header</div> | ||
| <div class="feature-desc">Adds X-ZeroX-Color header tag</div> | ||
| <div class="checkbox-box"> | ||
| <input id="option-addContainerHeader" type="checkbox" class="custom-checkbox"> | ||
| <span class="checkbox-mark"></span> | ||
| </div> | ||
| <div class="toggle-control"> | ||
| <input id="option-addContainerHeader" type="checkbox" class="toggle-input"> | ||
| <span class="toggle-slider"></span> | ||
| </div> | ||
| </label> | ||
| <!-- Option 4: Remove Security Headers --> | ||
| <label class="feature-card"> | ||
| <div class="feature-icon bg-rose"> | ||
| <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2"> | ||
| <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path> | ||
| <line x1="9" y1="9" x2="15" y2="15"></line> | ||
| <line x1="15" y1="9" x2="9" y2="15"></line> | ||
| </svg> | ||
| <label class="option-item"> | ||
| <div class="option-info"> | ||
| <span class="option-name">Strip Security Headers</span> | ||
| <span class="option-hint">Remove CSP, XFO & XSS headers</span> | ||
| </div> | ||
| <div class="feature-info"> | ||
| <div class="feature-name">Strip Security Headers</div> | ||
| <div class="feature-desc">Removes CSP, X-Frame-Options, XSS</div> | ||
| <div class="checkbox-box"> | ||
| <input id="option-removeSecurityHeaders" type="checkbox" class="custom-checkbox"> | ||
| <span class="checkbox-mark"></span> | ||
| </div> | ||
| <div class="toggle-control"> | ||
| <input id="option-removeSecurityHeaders" type="checkbox" class="toggle-input"> | ||
| <span class="toggle-slider"></span> | ||
| </div> | ||
| </label> | ||
| </div> | ||
| <!-- Container Tabs Launcher Section --> | ||
| <div class="section-header"> | ||
| <span class="section-title">LAUNCH CONTAINER TAB</span> | ||
| <span class="section-badge">8 PROFILES</span> | ||
| <!-- Containers Grid --> | ||
| <div class="containers-section"> | ||
| <div class="section-title">LAUNCH CONTAINER TAB</div> | ||
| <div id="identities" class="containers-grid"> | ||
| <!-- Injected by popup.js --> | ||
| </div> | ||
| </div> | ||
| <div id="identities" class="containers-grid"> | ||
| <!-- Container buttons dynamically generated --> | ||
| </div> | ||
| </main> | ||
| <!-- Footer --> | ||
| <footer class="app-footer"> | ||
| <div class="footer-left"> | ||
| <span class="version-tag">ZeroX Security • v2.0.1</span> | ||
| </div> | ||
| <div class="footer-right"> | ||
| <button type="button" id="btn-settings" class="footer-link-btn" title="Open Settings"> | ||
| <svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2"> | ||
| <circle cx="12" cy="12" r="3"></circle> | ||
| <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path> | ||
| </svg> | ||
| <span>Settings</span> | ||
| </button> | ||
| </div> | ||
| <footer> | ||
| <span class="version-tag">ZeroX v2.0.2</span> | ||
| <a id="settings" href="#" class="settings-btn"> | ||
| <svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2"> | ||
| <circle cx="12" cy="12" r="3"/> | ||
| <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/> | ||
| </svg> | ||
| <span>Settings</span> | ||
| </a> | ||
| </footer> | ||
| <!-- Toast Feedback --> | ||
| <div id="toast" class="toast">Address copied!</div> | ||
| </div> | ||
| <script src="/src/icon.js"></script> | ||
| <script src="/src/config.js"></script> | ||
@@ -192,0 +130,0 @@ <script src="/src/popup/popup.js"></script> |
+128
-211
@@ -1,250 +0,167 @@ | ||
| /** | ||
| * ZeroX Firefox Extension - Fast, High-Performance Popup Controller | ||
| */ | ||
| /* Container Identities Definition */ | ||
| const CONTAINER_COLORS = [ | ||
| { name: "red", firefox: "red", symbol: "●" }, | ||
| { name: "orange", firefox: "orange", symbol: "●" }, | ||
| { name: "yellow", firefox: "yellow", symbol: "●" }, | ||
| { name: "green", firefox: "green", symbol: "●" }, | ||
| { name: "cyan", firefox: "turquoise", symbol: "●" }, | ||
| { name: "blue", firefox: "blue", symbol: "●" }, | ||
| { name: "purple", firefox: "purple", symbol: "★" }, | ||
| { name: "pink", firefox: "pink", symbol: "♥" } | ||
| ]; | ||
| let toastTimer = null; | ||
| function showToast(message) { | ||
| const toast = document.getElementById("toast"); | ||
| if (!toast) return; | ||
| toast.textContent = message; | ||
| toast.classList.add("show"); | ||
| clearTimeout(toastTimer); | ||
| toastTimer = setTimeout(() => { | ||
| toast.classList.remove("show"); | ||
| }, 2000); | ||
| } | ||
| /* Contextual Identities Helper */ | ||
| /* Containers Identity */ | ||
| async function getOrCreateIdentity(color) { | ||
| const name = `ZeroX-${color}`; | ||
| const icon = "fingerprint"; | ||
| const [identity] = await browser.contextualIdentities.query({ name }); | ||
| const name = `ZeroX-${color}` | ||
| const icon = "fingerprint" | ||
| const [identity] = await browser.contextualIdentities.query({ name }) | ||
| if (identity !== undefined) { | ||
| return identity; | ||
| return identity | ||
| } | ||
| return await browser.contextualIdentities.create({ name, color, icon }); | ||
| return await browser.contextualIdentities.create({ name, color, icon }) | ||
| } | ||
| async function createContainerTab(color, colorName) { | ||
| try { | ||
| const identity = await getOrCreateIdentity(color); | ||
| const { cookieStoreId } = identity; | ||
| await browser.tabs.create({ cookieStoreId }); | ||
| showToast(`Opened ${colorName} container`); | ||
| } catch (err) { | ||
| console.error("Failed to create container tab:", err); | ||
| } | ||
| async function createContainerTab(color) { | ||
| const identity = await getOrCreateIdentity(color) | ||
| const { cookieStoreId } = identity | ||
| return browser.tabs.create({ cookieStoreId }) | ||
| } | ||
| /* Render Container Launcher Grid */ | ||
| function renderContainerGrid() { | ||
| const container = document.getElementById("identities"); | ||
| if (!container) return; | ||
| container.innerHTML = ""; | ||
| async function bindCheckboxToConfig(selector, config, configName) { | ||
| const checkbox = document.querySelector(selector) | ||
| checkbox.checked = await config.get(configName) | ||
| checkbox.addEventListener("change", () => config.set(configName, checkbox.checked)) | ||
| } | ||
| CONTAINER_COLORS.forEach(({ name, firefox, symbol }) => { | ||
| const btn = document.createElement("div"); | ||
| btn.className = "container-btn"; | ||
| btn.dataset.color = name; | ||
| btn.title = `Launch isolated ${name} container tab`; | ||
| const swatch = document.createElement("div"); | ||
| swatch.className = "container-swatch"; | ||
| const label = document.createElement("span"); | ||
| label.className = "container-label"; | ||
| label.textContent = name; | ||
| btn.appendChild(swatch); | ||
| btn.appendChild(label); | ||
| btn.addEventListener("click", () => { | ||
| createContainerTab(firefox, name); | ||
| }); | ||
| container.appendChild(btn); | ||
| }); | ||
| function createContainerTabButtons() { | ||
| const colorMap = [ | ||
| { name: "red", firefox: "red" }, | ||
| { name: "orange", firefox: "orange" }, | ||
| { name: "yellow", firefox: "yellow" }, | ||
| { name: "green", firefox: "green" }, | ||
| { name: "cyan", firefox: "turquoise" }, | ||
| { name: "blue", firefox: "blue" }, | ||
| { name: "purple", firefox: "purple" }, | ||
| { name: "pink", firefox: "pink" } | ||
| ] | ||
| const container = document.querySelector("#identities") | ||
| if (!container) return | ||
| container.innerHTML = "" | ||
| colorMap.forEach(({ name, firefox }) => { | ||
| const div = document.createElement("div") | ||
| div.classList.add("identity", name) | ||
| div.title = `Launch ${name.toUpperCase()} Container Tab` | ||
| const glyph = document.createElement("div") | ||
| glyph.classList.add("identity-glyph") | ||
| div.appendChild(glyph) | ||
| div.addEventListener("click", () => { | ||
| createContainerTab(firefox) | ||
| }) | ||
| container.appendChild(div) | ||
| }) | ||
| } | ||
| /* Update Master Status UI */ | ||
| function updateMasterStatus(enabled) { | ||
| const statusPill = document.getElementById("status-pill"); | ||
| const statusText = document.getElementById("status-text"); | ||
| const mainContent = document.getElementById("main-content"); | ||
| const masterCheckbox = document.getElementById("option-enabled"); | ||
| async function toggleZeroX(enabled) { | ||
| const logoBox = document.getElementById("logo-box") | ||
| const main = document.getElementById("main-content") | ||
| const statusDot = document.getElementById("status-dot") | ||
| const statusText = document.getElementById("status-text") | ||
| if (masterCheckbox && masterCheckbox.checked !== enabled) { | ||
| masterCheckbox.checked = enabled; | ||
| } | ||
| if (enabled) { | ||
| if (statusPill) statusPill.classList.add("active"); | ||
| if (statusText) statusText.textContent = "Active"; | ||
| if (mainContent) mainContent.classList.remove("disabled"); | ||
| if (!enabled) { | ||
| main?.classList.add('disabled') | ||
| if (statusText) { | ||
| statusText.textContent = "Inactive" | ||
| statusText.classList.remove('active') | ||
| } | ||
| statusDot?.classList.add('inactive') | ||
| logoBox?.classList.add('inactive') | ||
| } else { | ||
| if (statusPill) statusPill.classList.remove("active"); | ||
| if (statusText) statusText.textContent = "Standby"; | ||
| if (mainContent) mainContent.classList.add("disabled"); | ||
| main?.classList.remove('disabled') | ||
| if (statusText) { | ||
| statusText.textContent = "Active" | ||
| statusText.classList.add('active') | ||
| } | ||
| statusDot?.classList.remove('inactive') | ||
| logoBox?.classList.remove('inactive') | ||
| } | ||
| } | ||
| /* Address & Port Controls */ | ||
| async function setupAddressControls(currentHost, currentPort) { | ||
| const addressChip = document.getElementById("address-chip"); | ||
| const addressText = document.getElementById("address-text"); | ||
| const addressInput = document.getElementById("address-input"); | ||
| const copyBtn = document.getElementById("btn-copy-address"); | ||
| const presetButtons = document.querySelectorAll(".port-preset"); | ||
| async function setupInlineAddressEdit() { | ||
| const statusBar = document.getElementById("burp-status-bar") | ||
| const addressText = document.getElementById("address-text") | ||
| const addressInput = document.getElementById("address-input") | ||
| const refreshAddressDisplay = (host, port) => { | ||
| const fullAddr = `${host}:${port}`; | ||
| if (addressText) addressText.textContent = fullAddr; | ||
| if (addressInput) addressInput.value = fullAddr; | ||
| if (!statusBar || !addressText || !addressInput) return | ||
| // Highlight matching preset | ||
| presetButtons.forEach(btn => { | ||
| if (btn.dataset.port === String(port)) { | ||
| btn.classList.add("active"); | ||
| } else { | ||
| btn.classList.remove("active"); | ||
| } | ||
| }); | ||
| }; | ||
| const updateDisplay = async () => { | ||
| const host = await config.get("burpProxyHost") || "127.0.0.1" | ||
| const port = await config.get("burpProxyPort") || "8080" | ||
| addressText.textContent = `${host}:${port}` | ||
| } | ||
| refreshAddressDisplay(currentHost, currentPort); | ||
| await updateDisplay() | ||
| // Click to inline edit | ||
| if (addressChip && addressInput && addressText) { | ||
| addressChip.addEventListener("click", (e) => { | ||
| if (e.target === addressInput) return; | ||
| addressText.style.display = "none"; | ||
| addressInput.style.display = "block"; | ||
| addressInput.focus(); | ||
| addressInput.select(); | ||
| }); | ||
| statusBar.addEventListener("click", () => { | ||
| addressText.style.display = "none" | ||
| addressInput.style.display = "block" | ||
| addressInput.value = addressText.textContent | ||
| addressInput.focus() | ||
| }) | ||
| const saveAddressInput = async () => { | ||
| const raw = addressInput.value.trim(); | ||
| if (raw.includes(":")) { | ||
| const parts = raw.split(":"); | ||
| const host = parts[0].trim() || "127.0.0.1"; | ||
| const port = parts[1].trim() || "8080"; | ||
| await config.setMultiple({ burpProxyHost: host, burpProxyPort: port }); | ||
| refreshAddressDisplay(host, port); | ||
| showToast(`Target set to ${host}:${port}`); | ||
| } | ||
| addressInput.style.display = "none"; | ||
| addressText.style.display = "block"; | ||
| }; | ||
| addressInput.addEventListener("click", (e) => { | ||
| e.stopPropagation() | ||
| }) | ||
| addressInput.addEventListener("blur", saveAddressInput); | ||
| addressInput.addEventListener("keydown", (e) => { | ||
| if (e.key === "Enter") { | ||
| saveAddressInput(); | ||
| } else if (e.key === "Escape") { | ||
| addressInput.style.display = "none"; | ||
| addressText.style.display = "block"; | ||
| const saveAddress = async () => { | ||
| const value = addressInput.value.trim() | ||
| if (value.includes(":")) { | ||
| const [host, port] = value.split(":") | ||
| if (host && port) { | ||
| await config.set("burpProxyHost", host.trim()) | ||
| await config.set("burpProxyPort", port.trim()) | ||
| } | ||
| }); | ||
| } | ||
| addressInput.style.display = "none" | ||
| addressText.style.display = "block" | ||
| await updateDisplay() | ||
| } | ||
| // Quick Port Presets | ||
| presetButtons.forEach(btn => { | ||
| btn.addEventListener("click", async () => { | ||
| const port = btn.dataset.port; | ||
| const host = (await config.get("burpProxyHost")) || "127.0.0.1"; | ||
| await config.set("burpProxyPort", port); | ||
| refreshAddressDisplay(host, port); | ||
| showToast(`Port switched to ${port}`); | ||
| }); | ||
| }); | ||
| // Copy Address Button | ||
| if (copyBtn) { | ||
| copyBtn.addEventListener("click", async () => { | ||
| const host = (await config.get("burpProxyHost")) || "127.0.0.1"; | ||
| const port = (await config.get("burpProxyPort")) || "8080"; | ||
| const fullAddr = `${host}:${port}`; | ||
| try { | ||
| await navigator.clipboard.writeText(fullAddr); | ||
| showToast(`Copied ${fullAddr}`); | ||
| } catch (err) { | ||
| showToast(`Proxy: ${fullAddr}`); | ||
| } | ||
| }); | ||
| } | ||
| addressInput.addEventListener("blur", saveAddress) | ||
| addressInput.addEventListener("keydown", (e) => { | ||
| if (e.key === "Enter") { | ||
| e.preventDefault() | ||
| saveAddress() | ||
| } | ||
| if (e.key === "Escape") { | ||
| addressInput.style.display = "none" | ||
| addressText.style.display = "block" | ||
| } | ||
| }) | ||
| } | ||
| /* Bind Checkbox with Instant Sync */ | ||
| function bindCheckbox(elementId, configKey, initialValue) { | ||
| const el = document.getElementById(elementId); | ||
| if (!el) return; | ||
| el.checked = Boolean(initialValue); | ||
| el.addEventListener("change", () => { | ||
| config.set(configKey, el.checked); | ||
| }); | ||
| async function main() { | ||
| createContainerTabButtons() | ||
| await setupInlineAddressEdit() | ||
| await bindCheckboxToConfig("#option-enabled", config, "enabled") | ||
| await bindCheckboxToConfig("#option-useBurpProxyAll", config, "useBurpProxyAll") | ||
| await bindCheckboxToConfig("#option-useBurpProxyContainer", config, "useBurpProxyContainer") | ||
| await bindCheckboxToConfig("#option-addContainerHeader", config, "addContainerHeader") | ||
| await bindCheckboxToConfig("#option-removeSecurityHeaders", config, "removeSecurityHeaders") | ||
| /* Hook settings link */ | ||
| document.querySelector("#settings")?.addEventListener("click", ev => { | ||
| ev.preventDefault() | ||
| browser.runtime.openOptionsPage() | ||
| }) | ||
| config.onChange('enabled', (val) => { | ||
| toggleZeroX(Boolean(val)) | ||
| }) | ||
| toggleZeroX(Boolean(await config.get("enabled"))) | ||
| } | ||
| /* Fast Bootstrap */ | ||
| async function initPopup() { | ||
| renderContainerGrid(); | ||
| window.addEventListener("load", main) | ||
| // Batch load all configurations simultaneously | ||
| const currentConfig = await config.getAll(); | ||
| // Master Toggle | ||
| const masterCheckbox = document.getElementById("option-enabled"); | ||
| if (masterCheckbox) { | ||
| masterCheckbox.checked = Boolean(currentConfig.enabled); | ||
| masterCheckbox.addEventListener("change", () => { | ||
| config.set("enabled", masterCheckbox.checked); | ||
| updateMasterStatus(masterCheckbox.checked); | ||
| }); | ||
| } | ||
| updateMasterStatus(Boolean(currentConfig.enabled)); | ||
| // Address & Ports | ||
| setupAddressControls( | ||
| currentConfig.burpProxyHost || "127.0.0.1", | ||
| currentConfig.burpProxyPort || "8080" | ||
| ); | ||
| // Feature Toggles | ||
| bindCheckbox("option-useBurpProxyAll", "useBurpProxyAll", currentConfig.useBurpProxyAll); | ||
| bindCheckbox("option-useBurpProxyContainer", "useBurpProxyContainer", currentConfig.useBurpProxyContainer); | ||
| bindCheckbox("option-addContainerHeader", "addContainerHeader", currentConfig.addContainerHeader); | ||
| bindCheckbox("option-removeSecurityHeaders", "removeSecurityHeaders", currentConfig.removeSecurityHeaders); | ||
| // Settings Link | ||
| const settingsBtn = document.getElementById("btn-settings"); | ||
| if (settingsBtn) { | ||
| settingsBtn.addEventListener("click", () => { | ||
| browser.runtime.openOptionsPage(); | ||
| }); | ||
| } | ||
| // Listen for storage changes from background/options page | ||
| config.onChange("enabled", (newVal) => { | ||
| updateMasterStatus(Boolean(newVal)); | ||
| }); | ||
| } | ||
| // Start as soon as DOM is ready without waiting for remote assets | ||
| if (document.readyState === "loading") { | ||
| document.addEventListener("DOMContentLoaded", initPopup); | ||
| } else { | ||
| initPopup(); | ||
| } |
+123
-65
@@ -6,109 +6,164 @@ <!DOCTYPE html> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <title>ZeroX Settings - Security & Proxy Suite</title> | ||
| <title>ZeroX Settings - Security Proxy</title> | ||
| <link rel="stylesheet" href="settings.css"> | ||
| <link rel="preconnect" href="https://fonts.googleapis.com"> | ||
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | ||
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@500;600;700&display=swap" rel="stylesheet"> | ||
| </head> | ||
| <body> | ||
| <div class="ambient-glow glow-1"></div> | ||
| <div class="ambient-glow glow-2"></div> | ||
| <div class="settings-container"> | ||
| <!-- Header --> | ||
| <header class="settings-header"> | ||
| <header> | ||
| <div class="logo-box"> | ||
| <svg viewBox="0 0 32 32" width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||
| <defs> | ||
| <linearGradient id="settings-grad" x1="0%" y1="0%" x2="100%" y2="100%"> | ||
| <stop offset="0%" stop-color="#a855f7"/> | ||
| <stop offset="50%" stop-color="#7c3aed"/> | ||
| <stop offset="100%" stop-color="#3b82f6"/> | ||
| </linearGradient> | ||
| </defs> | ||
| <path d="M16 3L4 8V15C4 22.38 9.12 29.24 16 31C22.88 29.24 28 22.38 28 15V8L16 3Z" fill="url(#settings-grad)"/> | ||
| <path d="M16 6.5L7 10.3V15.5C7 21.2 10.8 26.6 16 28C21.2 26.6 25 21.2 25 15.5V10.3L16 6.5Z" fill="#0c0e17" fill-opacity="0.8"/> | ||
| <path d="M12 11L20 21M20 11L12 21" stroke="#ffffff" stroke-width="2.5" stroke-linecap="round"/> | ||
| <svg viewBox="0 0 24 24" width="28" height="28" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | ||
| <path d="M12 2L3 7v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V7l-9-5z"/> | ||
| <path d="M12 8v8"/> | ||
| <path d="M8 12h8"/> | ||
| </svg> | ||
| </div> | ||
| <div class="header-text"> | ||
| <div class="header-badge">ZEROX SECURITY TOOLKIT</div> | ||
| <h1>Settings & Configuration</h1> | ||
| <p>Manage Burp Suite proxy endpoints and security interception parameters</p> | ||
| <div class="header-title-row"> | ||
| <h1>ZeroX Configuration</h1> | ||
| <span class="badge-pro">PRO EDITION</span> | ||
| </div> | ||
| <p>Configure your upstream Burp Suite proxy listener and inspection settings</p> | ||
| </div> | ||
| </header> | ||
| <!-- Main Form --> | ||
| <main> | ||
| <form id="settings-form"> | ||
| <!-- Burp Proxy Settings Card --> | ||
| <!-- Burp Suite Proxy Card --> | ||
| <section class="settings-card"> | ||
| <div class="card-header"> | ||
| <div class="card-header-icon"> | ||
| <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2"> | ||
| <rect x="2" y="2" width="20" height="8" rx="2" ry="2"></rect> | ||
| <rect x="2" y="14" width="20" height="8" rx="2" ry="2"></rect> | ||
| <line x1="6" y1="6" x2="6.01" y2="6"></line> | ||
| <line x1="6" y1="18" x2="6.01" y2="18"></line> | ||
| <div class="card-icon-wrap"> | ||
| <svg class="card-icon" viewBox="0 0 24 24" width="20" height="20" fill="currentColor"> | ||
| <path d="M20 7h-4V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2H4a2 2 0 0 0-2 2v11a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2zM10 5h4v2h-4V5zM4 9h16v3h-16V9zm0 11v-6h16v6H4z"/> | ||
| </svg> | ||
| </div> | ||
| <div class="card-header-title"> | ||
| <div class="card-header-text"> | ||
| <h2>Burp Suite Proxy Target</h2> | ||
| <p>Default target address used for intercepting web traffic</p> | ||
| <p>Define upstream HTTP/HTTPS interceptor endpoint</p> | ||
| </div> | ||
| <div class="card-status-pill"> | ||
| <span class="pulse-dot"></span> | ||
| <span>PROXY ENGINE</span> | ||
| </div> | ||
| </div> | ||
| <div class="input-grid"> | ||
| <div class="input-group"> | ||
| <!-- Host Field --> | ||
| <div class="input-field"> | ||
| <label for="burphost">Proxy Host / IP</label> | ||
| <div class="label-row"> | ||
| <label for="burphost">Proxy Host IP / Hostname</label> | ||
| <span class="field-tag">REQUIRED</span> | ||
| </div> | ||
| <div class="input-wrapper"> | ||
| <svg class="input-icon" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"> | ||
| <circle cx="12" cy="12" r="10"></circle> | ||
| <line x1="2" y1="12" x2="22" y2="12"></line> | ||
| <path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path> | ||
| </svg> | ||
| <input type="text" id="burphost" placeholder="127.0.0.1" spellcheck="false" required> | ||
| <span class="input-prefix"> | ||
| <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"> | ||
| <circle cx="12" cy="12" r="10"/> | ||
| <line x1="2" y1="12" x2="22" y2="12"/> | ||
| <path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/> | ||
| </svg> | ||
| </span> | ||
| <input type="text" id="burphost" placeholder="127.0.0.1" spellcheck="false" autocomplete="off"> | ||
| </div> | ||
| <span class="input-hint">The hostname or IP address of your Burp Suite instance (usually <code>127.0.0.1</code>)</span> | ||
| <!-- Preset Host Chips --> | ||
| <div class="preset-chips"> | ||
| <span class="preset-label">Quick Presets:</span> | ||
| <button type="button" class="chip" data-target="burphost" data-val="127.0.0.1">127.0.0.1 (Loopback)</button> | ||
| <button type="button" class="chip" data-target="burphost" data-val="localhost">localhost</button> | ||
| <button type="button" class="chip" data-target="burphost" data-val="0.0.0.0">0.0.0.0</button> | ||
| </div> | ||
| <span class="input-hint">The IP address of your running Burp Suite or OWASP ZAP instance.</span> | ||
| </div> | ||
| <!-- Port Field --> | ||
| <div class="input-field"> | ||
| <label for="burpport">Proxy Port</label> | ||
| <div class="label-row"> | ||
| <label for="burpport">Proxy Port</label> | ||
| <span class="field-tag">TCP</span> | ||
| </div> | ||
| <div class="input-wrapper"> | ||
| <svg class="input-icon" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"> | ||
| <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline> | ||
| </svg> | ||
| <input type="number" id="burpport" placeholder="8080" min="1" max="65535" spellcheck="false" required> | ||
| <span class="input-prefix"> | ||
| <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"> | ||
| <rect x="4" y="4" width="16" height="16" rx="2" ry="2"/> | ||
| <rect x="9" y="9" width="6" height="6"/> | ||
| <line x1="9" y1="1" x2="9" y2="4"/> | ||
| <line x1="15" y1="1" x2="15" y2="4"/> | ||
| <line x1="9" y1="20" x2="9" y2="23"/> | ||
| <line x1="15" y1="20" x2="15" y2="23"/> | ||
| <line x1="20" y1="9" x2="23" y2="9"/> | ||
| <line x1="20" y1="14" x2="23" y2="14"/> | ||
| <line x1="1" y1="9" x2="4" y2="9"/> | ||
| <line x1="1" y1="14" x2="4" y2="14"/> | ||
| </svg> | ||
| </span> | ||
| <input type="text" id="burpport" placeholder="8080" spellcheck="false" autocomplete="off"> | ||
| </div> | ||
| <div class="quick-port-chips"> | ||
| <span class="chips-label">Presets:</span> | ||
| <button type="button" class="chip-btn" data-port="8080">8080 (Default)</button> | ||
| <button type="button" class="chip-btn" data-port="8081">8081</button> | ||
| <button type="button" class="chip-btn" data-port="8888">8888</button> | ||
| <button type="button" class="chip-btn" data-port="1080">1080 (SOCKS)</button> | ||
| <!-- Preset Port Chips --> | ||
| <div class="preset-chips"> | ||
| <span class="preset-label">Standard Ports:</span> | ||
| <button type="button" class="chip" data-target="burpport" data-val="8080">8080 (Default)</button> | ||
| <button type="button" class="chip" data-target="burpport" data-val="8081">8081</button> | ||
| <button type="button" class="chip" data-target="burpport" data-val="8082">8082</button> | ||
| <button type="button" class="chip" data-target="burpport" data-val="8888">8888 (Burp Alt)</button> | ||
| </div> | ||
| <span class="input-hint">Port number matching your Burp Proxy Listener (Proxy > Options > Proxy Listeners).</span> | ||
| </div> | ||
| <!-- Live Endpoint Preview --> | ||
| <div class="endpoint-preview-box"> | ||
| <span class="preview-label">ACTIVE PROXY ENDPOINT:</span> | ||
| <code class="preview-address" id="endpoint-preview">http://127.0.0.1:8080</code> | ||
| </div> | ||
| </div> | ||
| </section> | ||
| <!-- Information Card --> | ||
| <section class="settings-card info-card"> | ||
| <div class="info-row"> | ||
| <div class="info-icon"> | ||
| <!-- Architecture & Feature Highlights --> | ||
| <section class="info-grid"> | ||
| <div class="info-card"> | ||
| <div class="info-icon purple"> | ||
| <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2"> | ||
| <circle cx="12" cy="12" r="10"></circle> | ||
| <line x1="12" y1="16" x2="12" y2="12"></line> | ||
| <line x1="12" y1="8" x2="12.01" y2="8"></line> | ||
| <path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/> | ||
| </svg> | ||
| </div> | ||
| <div class="info-content"> | ||
| <h3>Extension Features & Status</h3> | ||
| <p>ZeroX isolates tabs into Firefox Container tabs and dynamically adds <code>X-ZeroX-Color</code> headers, enabling pinpoint tracking inside Burp Suite Logger & Repeater.</p> | ||
| <div class="info-text"> | ||
| <h3>Contextual Identities</h3> | ||
| <p>Route segregated Firefox containers with custom header tags directly to Burp.</p> | ||
| </div> | ||
| </div> | ||
| <div class="info-card"> | ||
| <div class="info-icon emerald"> | ||
| <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2"> | ||
| <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/> | ||
| </svg> | ||
| </div> | ||
| <div class="info-text"> | ||
| <h3>Header Stripper</h3> | ||
| <p>Automatically remove restrictive security headers (CSP, Framing, XSS) for testing.</p> | ||
| </div> | ||
| </div> | ||
| </section> | ||
| <!-- Actions --> | ||
| <div class="form-actions"> | ||
| <div id="save-status" class="save-status"></div> | ||
| <button type="button" id="reset-defaults" class="btn-secondary"> | ||
| <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2"> | ||
| <path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/> | ||
| <path d="M3 3v5h5"/> | ||
| </svg> | ||
| <span>Restore Defaults</span> | ||
| </button> | ||
| <button type="submit" id="save" class="btn-primary"> | ||
| <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"> | ||
| <path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path> | ||
| <polyline points="17 21 17 13 7 13 7 21"></polyline> | ||
| <polyline points="7 3 7 8 15 8"></polyline> | ||
| <svg class="save-icon" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"> | ||
| <path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/> | ||
| <polyline points="17 21 17 13 7 13 7 21"/> | ||
| <polyline points="7 3 7 8 15 8"/> | ||
| </svg> | ||
| <span id="btn-text">Save Configuration</span> | ||
| <span>Save Configuration</span> | ||
| </button> | ||
@@ -119,5 +174,7 @@ </div> | ||
| <footer class="settings-footer"> | ||
| <p>ZeroX Security Suite • Firefox Add-on</p> | ||
| <p class="version">v2.0.1</p> | ||
| <footer> | ||
| <div class="footer-info"> | ||
| <p class="copyright">ZeroX Toolkit • Crafted for Security Researchers & Penetration Testers</p> | ||
| </div> | ||
| <p class="version">ZeroX Core v2.0.2</p> | ||
| </footer> | ||
@@ -130,1 +187,2 @@ </div> | ||
| </html> | ||
+368
-183
@@ -1,29 +0,55 @@ | ||
| /* ZeroX Settings - Obsidian Cyber Theme */ | ||
| /* ========================================================================== | ||
| ZeroX Pro - Clean Minimalist Light Dashboard System (Settings) | ||
| ========================================================================== */ | ||
| :root { | ||
| --bg-base: #090b12; | ||
| --bg-surface: rgba(18, 22, 38, 0.7); | ||
| --bg-surface-elevated: rgba(25, 30, 52, 0.8); | ||
| --bg-input: rgba(11, 14, 24, 0.85); | ||
| /* Palette: Clean Light Surface */ | ||
| --bg-base: #f8fafc; | ||
| --bg-surface: #ffffff; | ||
| --bg-card: #ffffff; | ||
| --bg-card-hover: #fcfdfe; | ||
| --bg-input: #ffffff; | ||
| --border-subtle: rgba(255, 255, 255, 0.08); | ||
| --border-card: rgba(255, 255, 255, 0.12); | ||
| --border-focus: rgba(168, 85, 247, 0.6); | ||
| --text-primary: #f8fafc; | ||
| --text-secondary: #94a3b8; | ||
| /* Text Hierarchy */ | ||
| --text-main: #0f172a; | ||
| --text-dim: #475569; | ||
| --text-muted: #64748b; | ||
| --text-faint: #94a3b8; | ||
| --accent-purple: #a855f7; | ||
| --accent-purple-dark: #7c3aed; | ||
| --accent-purple-glow: rgba(168, 85, 247, 0.35); | ||
| --accent-emerald: #10b981; | ||
| --accent-cyan: #06b6d4; | ||
| /* Brand Accents */ | ||
| --accent-purple: #7c3aed; | ||
| --accent-purple-light: #8b5cf6; | ||
| --accent-purple-bg: #f5f3ff; | ||
| --accent-purple-border: #ddd6fe; | ||
| --accent-purple-glow: rgba(124, 58, 237, 0.2); | ||
| --font-stack: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Inter", "Helvetica Neue", Arial, sans-serif; | ||
| --transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1); | ||
| --accent-cyan: #0284c7; | ||
| --accent-cyan-bg: #f0f9ff; | ||
| --accent-cyan-border: #bae6fd; | ||
| --radius-sm: 8px; | ||
| --radius-md: 14px; | ||
| --radius-lg: 20px; | ||
| --radius-full: 9999px; | ||
| --accent-emerald: #059669; | ||
| --accent-emerald-bg: #ecfdf5; | ||
| --accent-emerald-glow: rgba(5, 150, 105, 0.2); | ||
| --accent-rose: #e11d48; | ||
| --accent-rose-bg: #fff1f2; | ||
| /* Borders */ | ||
| --border-subtle: #f1f5f9; | ||
| --border-card: #e2e8f0; | ||
| --border-input: #cbd5e1; | ||
| --border-focus: #7c3aed; | ||
| /* Typography */ | ||
| --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | ||
| --font-mono: 'JetBrains Mono', 'Fira Code', ui-monospace, monospace; | ||
| /* Shadows */ | ||
| --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); | ||
| --shadow-card: 0 1px 3px 0 rgba(0, 0, 0, 0.06), 0 1px 2px -1px rgba(0, 0, 0, 0.04); | ||
| --shadow-card-hover: 0 4px 16px 0 rgba(0, 0, 0, 0.06); | ||
| /* Transitions */ | ||
| --ease-out: cubic-bezier(0.16, 1, 0.3, 1); | ||
| --transition: all 0.2s var(--ease-out); | ||
| } | ||
@@ -36,2 +62,3 @@ | ||
| -webkit-font-smoothing: antialiased; | ||
| -moz-osx-font-smoothing: grayscale; | ||
| } | ||
@@ -41,14 +68,18 @@ | ||
| background-color: var(--bg-base); | ||
| background-image: | ||
| radial-gradient(circle at 20% 0%, rgba(168, 85, 247, 0.15) 0%, transparent 50%), | ||
| radial-gradient(circle at 80% 100%, rgba(59, 130, 246, 0.1) 0%, transparent 50%); | ||
| color: var(--text-primary); | ||
| font-family: var(--font-stack); | ||
| line-height: 1.6; | ||
| color: var(--text-main); | ||
| font-family: var(--font-sans); | ||
| line-height: 1.5; | ||
| min-height: 100vh; | ||
| display: flex; | ||
| justify-content: center; | ||
| padding: 48px 24px; | ||
| padding: 40px 20px; | ||
| position: relative; | ||
| overflow-x: hidden; | ||
| } | ||
| /* Subtle background */ | ||
| .ambient-glow { | ||
| display: none; | ||
| } | ||
| .settings-container { | ||
@@ -59,3 +90,5 @@ width: 100%; | ||
| flex-direction: column; | ||
| gap: 32px; | ||
| gap: 20px; | ||
| position: relative; | ||
| z-index: 1; | ||
| } | ||
@@ -66,18 +99,21 @@ | ||
| ========================================================================== */ | ||
| .settings-header { | ||
| header { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 20px; | ||
| gap: 16px; | ||
| padding: 6px 0; | ||
| } | ||
| .logo-box { | ||
| width: 58px; | ||
| height: 58px; | ||
| background: linear-gradient(135deg, #181c30 0%, #101322 100%); | ||
| border: 1px solid var(--border-card); | ||
| border-radius: var(--radius-md); | ||
| width: 48px; | ||
| height: 48px; | ||
| border-radius: 12px; | ||
| background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%); | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4), 0 0 16px var(--accent-purple-glow); | ||
| color: #ffffff; | ||
| box-shadow: 0 4px 12px var(--accent-purple-glow); | ||
| border: 1px solid rgba(255, 255, 255, 0.4); | ||
| flex-shrink: 0; | ||
@@ -92,52 +128,74 @@ } | ||
| .header-badge { | ||
| font-size: 10px; | ||
| .header-title-row { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 8px; | ||
| } | ||
| .header-title-row h1 { | ||
| font-size: 20px; | ||
| font-weight: 800; | ||
| letter-spacing: 1px; | ||
| color: #c084fc; | ||
| text-transform: uppercase; | ||
| letter-spacing: -0.03em; | ||
| color: #0f172a; | ||
| } | ||
| .header-text h1 { | ||
| font-size: 24px; | ||
| .badge-pro { | ||
| font-size: 9.5px; | ||
| font-weight: 800; | ||
| letter-spacing: -0.5px; | ||
| color: #ffffff; | ||
| letter-spacing: 0.08em; | ||
| padding: 2px 6px; | ||
| border-radius: 4px; | ||
| background: var(--accent-purple-bg); | ||
| color: var(--accent-purple); | ||
| border: 1px solid var(--accent-purple-border); | ||
| } | ||
| .header-text p { | ||
| color: var(--text-secondary); | ||
| font-size: 13px; | ||
| color: var(--text-muted); | ||
| } | ||
| /* ========================================================================== | ||
| Cards & Layout | ||
| Settings Main Form & Cards | ||
| ========================================================================== */ | ||
| main form { | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 16px; | ||
| } | ||
| .settings-card { | ||
| background: var(--bg-surface); | ||
| background: var(--bg-card); | ||
| border: 1px solid var(--border-card); | ||
| border-radius: var(--radius-lg); | ||
| padding: 28px; | ||
| backdrop-filter: blur(16px); | ||
| box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35); | ||
| border-radius: 12px; | ||
| padding: 20px; | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 24px; | ||
| gap: 16px; | ||
| box-shadow: var(--shadow-card); | ||
| transition: var(--transition); | ||
| } | ||
| .settings-card:hover { | ||
| border-color: #cbd5e1; | ||
| box-shadow: var(--shadow-card-hover); | ||
| } | ||
| .card-header { | ||
| display: flex; | ||
| align-items: flex-start; | ||
| gap: 14px; | ||
| padding-bottom: 18px; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| gap: 12px; | ||
| padding-bottom: 12px; | ||
| border-bottom: 1px solid var(--border-subtle); | ||
| } | ||
| .card-header-icon { | ||
| width: 36px; | ||
| height: 36px; | ||
| border-radius: 10px; | ||
| background: rgba(168, 85, 247, 0.15); | ||
| border: 1px solid rgba(168, 85, 247, 0.3); | ||
| color: #c084fc; | ||
| .card-icon-wrap { | ||
| width: 34px; | ||
| height: 34px; | ||
| border-radius: 9px; | ||
| background: #ffedd5; | ||
| border: 1px solid #fed7aa; | ||
| color: #ea580c; | ||
| display: flex; | ||
@@ -149,18 +207,56 @@ align-items: center; | ||
| .card-header-title h2 { | ||
| font-size: 17px; | ||
| .card-icon-wrap.emerald { | ||
| background: #ecfdf5; | ||
| border-color: #a7f3d0; | ||
| color: #059669; | ||
| } | ||
| .card-header-text { | ||
| flex: 1; | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 2px; | ||
| } | ||
| .card-header-text h2 { | ||
| font-size: 14.5px; | ||
| font-weight: 700; | ||
| color: #ffffff; | ||
| color: var(--text-main); | ||
| letter-spacing: -0.01em; | ||
| } | ||
| .card-header-title p { | ||
| font-size: 12.5px; | ||
| color: var(--text-secondary); | ||
| .card-header-text p { | ||
| font-size: 12px; | ||
| color: var(--text-muted); | ||
| } | ||
| /* Input Fields */ | ||
| .input-grid { | ||
| .card-status-pill { | ||
| display: inline-flex; | ||
| align-items: center; | ||
| gap: 6px; | ||
| font-size: 10px; | ||
| font-weight: 700; | ||
| letter-spacing: 0.06em; | ||
| padding: 3px 8px; | ||
| border-radius: 99px; | ||
| background: var(--accent-cyan-bg); | ||
| color: var(--accent-cyan); | ||
| border: 1px solid var(--accent-cyan-border); | ||
| } | ||
| .pulse-dot { | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 50%; | ||
| background-color: var(--accent-cyan); | ||
| } | ||
| /* ========================================================================== | ||
| Input Fields | ||
| ========================================================================== */ | ||
| .input-group { | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 20px; | ||
| gap: 16px; | ||
| } | ||
@@ -171,12 +267,29 @@ | ||
| flex-direction: column; | ||
| gap: 8px; | ||
| gap: 6px; | ||
| } | ||
| .label-row { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| } | ||
| .label-row label, | ||
| .input-field label { | ||
| font-size: 13px; | ||
| font-size: 12.5px; | ||
| font-weight: 600; | ||
| color: #e2e8f0; | ||
| letter-spacing: 0.2px; | ||
| color: var(--text-main); | ||
| } | ||
| .field-tag { | ||
| font-size: 9.5px; | ||
| font-weight: 700; | ||
| letter-spacing: 0.05em; | ||
| padding: 1px 5px; | ||
| border-radius: 4px; | ||
| background: #f1f5f9; | ||
| color: var(--text-muted); | ||
| border: 1px solid var(--border-card); | ||
| } | ||
| .input-wrapper { | ||
@@ -188,6 +301,8 @@ position: relative; | ||
| .input-icon { | ||
| .input-prefix { | ||
| position: absolute; | ||
| left: 14px; | ||
| color: var(--text-muted); | ||
| left: 12px; | ||
| color: var(--text-faint); | ||
| display: flex; | ||
| align-items: center; | ||
| pointer-events: none; | ||
@@ -200,61 +315,47 @@ transition: var(--transition); | ||
| background: var(--bg-input); | ||
| border: 1px solid var(--border-card); | ||
| border-radius: var(--radius-md); | ||
| padding: 12px 16px 12px 42px; | ||
| color: #ffffff; | ||
| font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; | ||
| font-size: 13.5px; | ||
| border: 1px solid var(--border-input); | ||
| border-radius: 8px; | ||
| padding: 9px 12px 9px 36px; | ||
| color: var(--text-main); | ||
| font-family: var(--font-mono); | ||
| font-size: 13px; | ||
| font-weight: 500; | ||
| outline: none; | ||
| transition: var(--transition); | ||
| box-shadow: var(--shadow-sm); | ||
| } | ||
| .input-wrapper input:focus { | ||
| outline: none; | ||
| border-color: var(--accent-purple); | ||
| background: rgba(15, 20, 36, 0.95); | ||
| box-shadow: 0 0 0 3px var(--accent-purple-glow); | ||
| box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.12); | ||
| } | ||
| .input-wrapper input:focus + .input-icon, | ||
| .input-wrapper:focus-within .input-icon { | ||
| .input-wrapper input:focus ~ .input-prefix, | ||
| .input-wrapper input:focus + .input-prefix { | ||
| color: var(--accent-purple); | ||
| } | ||
| .input-hint { | ||
| font-size: 11.5px; | ||
| color: var(--text-muted); | ||
| } | ||
| .input-hint code { | ||
| background: rgba(255, 255, 255, 0.08); | ||
| padding: 2px 5px; | ||
| border-radius: 4px; | ||
| font-family: monospace; | ||
| color: #cbd5e1; | ||
| } | ||
| /* Quick Port Chips */ | ||
| .quick-port-chips { | ||
| .preset-chips { | ||
| display: flex; | ||
| align-items: center; | ||
| flex-wrap: wrap; | ||
| gap: 8px; | ||
| margin-top: 4px; | ||
| gap: 6px; | ||
| margin-top: 2px; | ||
| } | ||
| .chips-label { | ||
| font-size: 11.5px; | ||
| .preset-label { | ||
| font-size: 11px; | ||
| font-weight: 600; | ||
| color: var(--text-muted); | ||
| font-weight: 600; | ||
| } | ||
| .chip-btn { | ||
| background: rgba(255, 255, 255, 0.05); | ||
| border: 1px solid var(--border-subtle); | ||
| border-radius: var(--radius-sm); | ||
| color: var(--text-secondary); | ||
| font-family: ui-monospace, SFMono-Regular, monospace; | ||
| .chip { | ||
| background: #f1f5f9; | ||
| border: 1px solid var(--border-card); | ||
| color: var(--text-dim); | ||
| font-size: 11px; | ||
| font-weight: 600; | ||
| padding: 4px 10px; | ||
| font-family: var(--font-mono); | ||
| font-weight: 500; | ||
| padding: 3px 8px; | ||
| border-radius: 6px; | ||
| cursor: pointer; | ||
@@ -264,56 +365,108 @@ transition: var(--transition); | ||
| .chip-btn:hover { | ||
| background: rgba(168, 85, 247, 0.2); | ||
| border-color: rgba(168, 85, 247, 0.4); | ||
| color: #ffffff; | ||
| .chip:hover { | ||
| background: var(--accent-purple-bg); | ||
| border-color: var(--accent-purple-border); | ||
| color: var(--accent-purple); | ||
| transform: translateY(-1px); | ||
| } | ||
| .chip-btn.active { | ||
| background: rgba(168, 85, 247, 0.35); | ||
| border-color: var(--accent-purple); | ||
| color: #ffffff; | ||
| .chip:active { | ||
| transform: scale(0.97); | ||
| } | ||
| /* Info Card */ | ||
| .info-card { | ||
| padding: 18px 24px; | ||
| background: rgba(15, 23, 42, 0.4); | ||
| border-color: rgba(59, 130, 246, 0.2); | ||
| .input-hint { | ||
| font-size: 11.5px; | ||
| color: var(--text-muted); | ||
| line-height: 1.3; | ||
| } | ||
| .info-row { | ||
| /* Endpoint Live Preview Box */ | ||
| .endpoint-preview-box { | ||
| background: #f8fafc; | ||
| border: 1px solid var(--border-card); | ||
| border-radius: 9px; | ||
| padding: 10px 14px; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| } | ||
| .preview-label { | ||
| font-size: 10.5px; | ||
| font-weight: 700; | ||
| color: var(--text-muted); | ||
| letter-spacing: 0.05em; | ||
| } | ||
| .preview-address { | ||
| font-family: var(--font-mono); | ||
| font-size: 12px; | ||
| font-weight: 700; | ||
| color: var(--accent-cyan); | ||
| background: var(--accent-cyan-bg); | ||
| padding: 3px 8px; | ||
| border-radius: 5px; | ||
| border: 1px solid var(--accent-cyan-border); | ||
| } | ||
| /* ========================================================================== | ||
| Info Grid Features | ||
| ========================================================================== */ | ||
| .info-grid { | ||
| display: grid; | ||
| grid-template-columns: 1fr 1fr; | ||
| gap: 12px; | ||
| } | ||
| .info-card { | ||
| background: var(--bg-card); | ||
| border: 1px solid var(--border-card); | ||
| border-radius: 12px; | ||
| padding: 14px; | ||
| display: flex; | ||
| align-items: flex-start; | ||
| gap: 14px; | ||
| gap: 10px; | ||
| box-shadow: var(--shadow-card); | ||
| } | ||
| .info-icon { | ||
| color: #60a5fa; | ||
| margin-top: 2px; | ||
| width: 32px; | ||
| height: 32px; | ||
| border-radius: 8px; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| flex-shrink: 0; | ||
| } | ||
| .info-content h3 { | ||
| font-size: 13.5px; | ||
| .info-icon.purple { | ||
| background: var(--accent-purple-bg); | ||
| border: 1px solid var(--accent-purple-border); | ||
| color: var(--accent-purple); | ||
| } | ||
| .info-icon.emerald { | ||
| background: var(--accent-emerald-bg); | ||
| border: 1px solid #a7f3d0; | ||
| color: var(--accent-emerald); | ||
| } | ||
| .info-text h3 { | ||
| font-size: 12.5px; | ||
| font-weight: 700; | ||
| color: #e2e8f0; | ||
| color: var(--text-main); | ||
| margin-bottom: 2px; | ||
| } | ||
| .info-content p { | ||
| font-size: 12px; | ||
| color: var(--text-secondary); | ||
| line-height: 1.5; | ||
| .info-text p { | ||
| font-size: 11.5px; | ||
| color: var(--text-muted); | ||
| line-height: 1.35; | ||
| } | ||
| .info-content code { | ||
| background: rgba(59, 130, 246, 0.15); | ||
| color: #93c5fd; | ||
| padding: 2px 6px; | ||
| border-radius: 4px; | ||
| font-family: monospace; | ||
| font-size: 11px; | ||
| } | ||
| /* ========================================================================== | ||
| Form Actions & Buttons | ||
| ========================================================================== */ | ||
| /* Form Actions */ | ||
| .form-actions { | ||
@@ -323,62 +476,94 @@ display: flex; | ||
| justify-content: flex-end; | ||
| gap: 16px; | ||
| margin-top: 12px; | ||
| gap: 10px; | ||
| margin-top: 4px; | ||
| } | ||
| .save-status { | ||
| .btn-secondary { | ||
| display: inline-flex; | ||
| align-items: center; | ||
| gap: 6px; | ||
| background: #ffffff; | ||
| border: 1px solid var(--border-card); | ||
| border-radius: 8px; | ||
| padding: 9px 16px; | ||
| font-size: 12.5px; | ||
| font-weight: 600; | ||
| color: var(--accent-emerald); | ||
| opacity: 0; | ||
| transition: opacity 0.2s ease; | ||
| color: var(--text-dim); | ||
| cursor: pointer; | ||
| transition: var(--transition); | ||
| box-shadow: var(--shadow-sm); | ||
| } | ||
| .save-status.show { | ||
| opacity: 1; | ||
| .btn-secondary:hover { | ||
| background: #f8fafc; | ||
| border-color: #cbd5e1; | ||
| color: var(--text-main); | ||
| } | ||
| .btn-primary { | ||
| background: linear-gradient(135deg, #a855f7 0%, #7c3aed 100%); | ||
| display: inline-flex; | ||
| align-items: center; | ||
| gap: 7px; | ||
| background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%); | ||
| color: #ffffff; | ||
| border: none; | ||
| border-radius: var(--radius-md); | ||
| padding: 12px 24px; | ||
| font-size: 13.5px; | ||
| border: 1px solid #6d28d9; | ||
| border-radius: 8px; | ||
| padding: 9px 20px; | ||
| font-size: 13px; | ||
| font-weight: 700; | ||
| cursor: pointer; | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 8px; | ||
| transition: var(--transition); | ||
| box-shadow: 0 4px 16px var(--accent-purple-glow); | ||
| box-shadow: 0 2px 8px var(--accent-purple-glow); | ||
| } | ||
| .btn-primary:hover { | ||
| transform: translateY(-2px); | ||
| box-shadow: 0 6px 22px rgba(168, 85, 247, 0.5); | ||
| filter: brightness(1.1); | ||
| background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%); | ||
| box-shadow: 0 4px 14px rgba(124, 58, 237, 0.35); | ||
| transform: translateY(-1px); | ||
| } | ||
| .btn-primary:active { | ||
| transform: translateY(0); | ||
| transform: scale(0.98); | ||
| } | ||
| .btn-primary.success { | ||
| background: linear-gradient(135deg, #10b981 0%, #059669 100%); | ||
| box-shadow: 0 4px 16px rgba(16, 185, 129, 0.4); | ||
| .save-icon { | ||
| display: block; | ||
| } | ||
| /* Footer */ | ||
| .settings-footer { | ||
| /* ========================================================================== | ||
| Footer | ||
| ========================================================================== */ | ||
| footer { | ||
| display: flex; | ||
| justify-content: space-between; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| padding-top: 20px; | ||
| padding-top: 16px; | ||
| border-top: 1px solid var(--border-subtle); | ||
| } | ||
| .copyright { | ||
| font-size: 11.5px; | ||
| color: var(--text-muted); | ||
| font-size: 12px; | ||
| } | ||
| .version { | ||
| font-weight: 700; | ||
| font-size: 11px; | ||
| font-family: var(--font-mono); | ||
| font-weight: 600; | ||
| color: var(--text-faint); | ||
| } | ||
| @media (max-width: 600px) { | ||
| .info-grid { | ||
| grid-template-columns: 1fr; | ||
| } | ||
| .form-actions { | ||
| flex-direction: column-reverse; | ||
| width: 100%; | ||
| } | ||
| .btn-primary, .btn-secondary { | ||
| width: 100%; | ||
| justify-content: center; | ||
| } | ||
| } |
+69
-69
@@ -1,82 +0,82 @@ | ||
| /** | ||
| * ZeroX Settings Page Controller | ||
| */ | ||
| async function main() { | ||
| const hostInput = document.getElementById("burphost"); | ||
| const portInput = document.getElementById("burpport"); | ||
| const form = document.getElementById("settings-form"); | ||
| const saveBtn = document.getElementById("save"); | ||
| const btnText = document.getElementById("btn-text"); | ||
| const saveStatus = document.getElementById("save-status"); | ||
| const presetChips = document.querySelectorAll(".chip-btn"); | ||
| const $ = i => document.getElementById(i) | ||
| // Load initial values | ||
| const currentConfig = await config.getAll(); | ||
| if (hostInput) hostInput.value = currentConfig.burpProxyHost || "127.0.0.1"; | ||
| if (portInput) portInput.value = currentConfig.burpProxyPort || "8080"; | ||
| const hostInput = $("burphost") | ||
| const portInput = $("burpport") | ||
| const previewEl = $("endpoint-preview") | ||
| const resetBtn = $("reset-defaults") | ||
| const updatePresetHighlight = (port) => { | ||
| presetChips.forEach(chip => { | ||
| if (chip.dataset.port === String(port)) { | ||
| chip.classList.add("active"); | ||
| } else { | ||
| chip.classList.remove("active"); | ||
| } | ||
| }); | ||
| }; | ||
| const updatePreview = () => { | ||
| if (!previewEl) return | ||
| const h = (hostInput.value || "127.0.0.1").trim() | ||
| const p = (portInput.value || "8080").trim() | ||
| previewEl.textContent = `http://${h}:${p}` | ||
| } | ||
| updatePresetHighlight(currentConfig.burpProxyPort || "8080"); | ||
| const configEl = [ | ||
| [hostInput, "burpProxyHost"], | ||
| [portInput, "burpProxyPort"], | ||
| ] | ||
| // Preset Chip click listener | ||
| presetChips.forEach(chip => { | ||
| chip.addEventListener("click", () => { | ||
| if (portInput) { | ||
| portInput.value = chip.dataset.port; | ||
| updatePresetHighlight(chip.dataset.port); | ||
| portInput.focus(); | ||
| } | ||
| }); | ||
| }); | ||
| if (portInput) { | ||
| portInput.addEventListener("input", () => { | ||
| updatePresetHighlight(portInput.value.trim()); | ||
| }); | ||
| /* Config to form */ | ||
| for (const [el, configName] of configEl) { | ||
| if (el) { | ||
| const val = await config.get(configName) | ||
| el.value = val ?? "" | ||
| } | ||
| } | ||
| updatePreview() | ||
| // Save configuration | ||
| if (form) { | ||
| form.addEventListener("submit", async (e) => { | ||
| e.preventDefault(); | ||
| /* Live preview updates on input */ | ||
| hostInput?.addEventListener("input", updatePreview) | ||
| portInput?.addEventListener("input", updatePreview) | ||
| const host = hostInput.value.trim() || "127.0.0.1"; | ||
| const port = portInput.value.trim() || "8080"; | ||
| /* Preset chip click handlers */ | ||
| document.querySelectorAll(".chip").forEach(chip => { | ||
| chip.addEventListener("click", () => { | ||
| const targetId = chip.getAttribute("data-target") | ||
| const val = chip.getAttribute("data-val") | ||
| const targetInput = $(targetId) | ||
| if (targetInput && val) { | ||
| targetInput.value = val | ||
| targetInput.dispatchEvent(new Event("input")) | ||
| targetInput.focus() | ||
| } | ||
| }) | ||
| }) | ||
| await config.setMultiple({ | ||
| burpProxyHost: host, | ||
| burpProxyPort: port | ||
| }); | ||
| /* Reset to defaults */ | ||
| resetBtn?.addEventListener("click", () => { | ||
| if (hostInput) hostInput.value = "127.0.0.1" | ||
| if (portInput) portInput.value = "8080" | ||
| updatePreview() | ||
| }) | ||
| // UI Success State | ||
| if (saveBtn) saveBtn.classList.add("success"); | ||
| if (btnText) btnText.textContent = "Saved Successfully!"; | ||
| if (saveStatus) { | ||
| saveStatus.textContent = `Active target: ${host}:${port}`; | ||
| saveStatus.classList.add("show"); | ||
| /* Form to config */ | ||
| document.querySelector("form")?.addEventListener("submit", async ev => { | ||
| ev.preventDefault() | ||
| for (const [el, configName] of configEl) { | ||
| if (el) { | ||
| await config.set(configName, el.value.trim()) | ||
| } | ||
| setTimeout(() => { | ||
| if (saveBtn) saveBtn.classList.remove("success"); | ||
| if (btnText) btnText.textContent = "Save Configuration"; | ||
| if (saveStatus) saveStatus.classList.remove("show"); | ||
| }, 2500); | ||
| }); | ||
| } | ||
| } | ||
| const saveBtn = $("save") | ||
| if (!saveBtn) return | ||
| const btnSpan = saveBtn.querySelector("span") | ||
| const originalText = btnSpan ? btnSpan.textContent : "Save Configuration" | ||
| if (btnSpan) btnSpan.textContent = "Saved Successfully!" | ||
| saveBtn.style.background = "linear-gradient(135deg, #10b981 0%, #059669 100%)" | ||
| saveBtn.style.boxShadow = "0 0 20px rgba(16, 185, 129, 0.4)" | ||
| setTimeout(() => { | ||
| if (btnSpan) btnSpan.textContent = originalText | ||
| saveBtn.style.background = "" | ||
| saveBtn.style.boxShadow = "" | ||
| }, 2000) | ||
| }) | ||
| } | ||
| if (document.readyState === "loading") { | ||
| document.addEventListener("DOMContentLoaded", main); | ||
| } else { | ||
| main(); | ||
| } | ||
| document.addEventListener("DOMContentLoaded", main) | ||
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display