ATlast — you'll never need to find your favorites on another platform again. Find your favs in the ATmosphere.
atproto
16
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor: consolidate popup HTML to single file with JS-injected dev UI

- Merge popup-dev.html into popup.html - dev UI now injected via JS
- popup.ts checks __BUILD_MODE__ and conditionally injects dev banner/toolbar
- Eliminates copy-paste between dev and prod HTML
- Dev mode: injects orange banner + state-switching toolbar
- Prod mode: runs normal extension logic

byarielm.fyi 61cf0db8 da3c93f0

verified
+264 -569
-225
packages/extension/src/popup/popup-dev.html
··· 1 - <!doctype html> 2 - <html lang="en"> 3 - <head> 4 - <meta charset="UTF-8" /> 5 - <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 - <title>ATlast Importer - Dev Mode</title> 7 - <link rel="stylesheet" href="popup.css" /> 8 - </head> 9 - <body 10 - class="w-[350px] min-h-[400px] font-sans text-slate-800 dark:text-cyan-50 bg-gradient-to-br from-purple-50 via-white to-cyan-50 dark:from-slate-900 dark:via-purple-950 dark:to-sky-900" 11 - > 12 - <!-- Dev Mode Banner --> 13 - <div 14 - style=" 15 - background: #f97316; 16 - color: white; 17 - text-align: center; 18 - padding: 4px; 19 - font-size: 11px; 20 - font-weight: bold; 21 - " 22 - > 23 - 🔧 DEVELOPMENT MODE 24 - </div> 25 - 26 - <div class="flex flex-col min-h-[400px]"> 27 - <header class="bg-firefly-banner text-white p-5 text-center"> 28 - <h1 class="text-xl font-bold mb-1">ATlast Importer</h1> 29 - <p class="text-[13px] opacity-90"> 30 - Find your follows in the ATmosphere 31 - </p> 32 - </header> 33 - 34 - <main 35 - id="app" 36 - class="flex-1 px-5 py-6 flex items-center justify-center" 37 - > 38 - <!-- Idle state --> 39 - <div id="state-idle" class="w-full text-center hidden"> 40 - <div class="text-5xl mb-4">🔍</div> 41 - <p 42 - class="text-base font-semibold mb-3 text-slate-700 dark:text-cyan-50" 43 - > 44 - Go to your Twitter/X Following page to start 45 - </p> 46 - <p 47 - class="text-[13px] text-slate-500 dark:text-slate-400 mt-2" 48 - > 49 - Visit x.com/yourusername/following 50 - </p> 51 - </div> 52 - 53 - <!-- Ready state --> 54 - <div id="state-ready" class="w-full text-center hidden"> 55 - <div class="text-5xl mb-4">✅</div> 56 - <p 57 - class="text-base font-semibold mb-3 text-slate-700 dark:text-cyan-50" 58 - > 59 - Ready to scan <span id="platform-name"></span> 60 - </p> 61 - <button 62 - id="btn-start" 63 - class="w-full bg-orange-600 hover:bg-orange-700 text-white font-semibold py-3 px-6 rounded-lg mt-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg hover:shadow-orange-600/30 active:translate-y-0" 64 - > 65 - Start Scan 66 - </button> 67 - </div> 68 - 69 - <!-- Scraping state --> 70 - <div id="state-scraping" class="w-full text-center hidden"> 71 - <div class="text-5xl mb-4 spinner">⏳</div> 72 - <p 73 - class="text-base font-semibold mb-3 text-slate-700 dark:text-cyan-50" 74 - > 75 - Scanning... 76 - </p> 77 - <div class="mt-5"> 78 - <div 79 - class="w-full h-2 bg-sky-50 dark:bg-slate-800 rounded overflow-hidden mb-3" 80 - > 81 - <div 82 - id="progress-fill" 83 - class="h-full bg-gradient-to-r from-orange-600 to-pink-600 w-0 transition-all duration-300 progress-fill" 84 - ></div> 85 - </div> 86 - <p 87 - class="text-base font-semibold text-slate-700 dark:text-cyan-50" 88 - > 89 - Found <span id="count">0</span> users 90 - </p> 91 - <p 92 - id="status-message" 93 - class="text-[13px] text-slate-500 dark:text-slate-400 mt-2" 94 - ></p> 95 - </div> 96 - </div> 97 - 98 - <!-- Complete state --> 99 - <div id="state-complete" class="w-full text-center hidden"> 100 - <div class="text-5xl mb-4">🎉</div> 101 - <p 102 - class="text-base font-semibold mb-3 text-slate-700 dark:text-cyan-50" 103 - > 104 - Scan complete! 105 - </p> 106 - <p class="text-sm text-slate-500 dark:text-slate-400 mt-2"> 107 - Found 108 - <strong 109 - id="final-count" 110 - class="text-orange-600 dark:text-orange-400 text-lg" 111 - >0</strong 112 - > 113 - users 114 - </p> 115 - <button 116 - id="btn-upload" 117 - class="w-full bg-orange-600 hover:bg-orange-700 text-white font-semibold py-3 px-6 rounded-lg mt-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg hover:shadow-orange-600/30 active:translate-y-0" 118 - > 119 - Open in ATlast 120 - </button> 121 - </div> 122 - 123 - <!-- Uploading state --> 124 - <div id="state-uploading" class="w-full text-center hidden"> 125 - <div class="text-5xl mb-4 spinner">📤</div> 126 - <p 127 - class="text-base font-semibold mb-3 text-slate-700 dark:text-cyan-50" 128 - > 129 - Uploading to ATlast... 130 - </p> 131 - </div> 132 - 133 - <!-- Error state --> 134 - <div id="state-error" class="w-full text-center hidden"> 135 - <div class="text-5xl mb-4">⚠️</div> 136 - <p 137 - class="text-base font-semibold mb-3 text-slate-700 dark:text-cyan-50" 138 - > 139 - Error 140 - </p> 141 - <p 142 - id="error-message" 143 - class="text-[13px] text-red-600 dark:text-red-400 mt-2 p-3 bg-red-50 dark:bg-red-950/50 rounded border-l-[3px] border-red-600" 144 - ></p> 145 - <button 146 - id="btn-retry" 147 - class="w-full bg-white dark:bg-purple-950 text-purple-700 dark:text-cyan-400 border-2 border-purple-700 dark:border-cyan-400 font-semibold py-2.5 px-6 rounded-lg mt-4 transition-all duration-200 hover:bg-purple-50 dark:hover:bg-purple-900" 148 - > 149 - Try Again 150 - </button> 151 - </div> 152 - 153 - <!-- Server offline state --> 154 - <div id="state-offline" class="w-full text-center hidden"> 155 - <div class="text-5xl mb-4">🔌</div> 156 - <p 157 - class="text-base font-semibold mb-3 text-slate-700 dark:text-cyan-50" 158 - > 159 - Server not available 160 - </p> 161 - <p 162 - id="dev-instructions" 163 - class="text-[13px] text-red-600 dark:text-red-400 mt-2 p-3 bg-red-50 dark:bg-red-950/50 rounded border-l-[3px] border-red-600" 164 - > 165 - Start the dev server:<br /> 166 - <code 167 - class="bg-black/10 dark:bg-white/10 px-2 py-1 rounded font-mono text-[11px] inline-block my-2" 168 - >npx netlify-cli dev --filter @atlast/web</code 169 - > 170 - </p> 171 - <p 172 - class="text-[13px] text-slate-500 dark:text-slate-400 mt-2" 173 - id="server-url" 174 - ></p> 175 - <button 176 - id="btn-check-server" 177 - class="w-full bg-orange-600 hover:bg-orange-700 text-white font-semibold py-3 px-6 rounded-lg mt-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg hover:shadow-orange-600/30 active:translate-y-0" 178 - > 179 - Check Again 180 - </button> 181 - </div> 182 - 183 - <!-- Not logged in state --> 184 - <div id="state-not-logged-in" class="w-full text-center hidden"> 185 - <div class="text-5xl mb-4">🔐</div> 186 - <p 187 - class="text-base font-semibold mb-3 text-slate-700 dark:text-cyan-50" 188 - > 189 - Not logged in to ATlast 190 - </p> 191 - <p 192 - class="text-[13px] text-red-600 dark:text-red-400 mt-2 p-3 bg-red-50 dark:bg-red-950/50 rounded border-l-[3px] border-red-600" 193 - > 194 - Please log in to ATlast first, then return here to scan. 195 - </p> 196 - <button 197 - id="btn-open-atlast" 198 - class="w-full bg-orange-600 hover:bg-orange-700 text-white font-semibold py-3 px-6 rounded-lg mt-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg hover:shadow-orange-600/30 active:translate-y-0" 199 - > 200 - Open ATlast 201 - </button> 202 - <button 203 - id="btn-retry-login" 204 - class="w-full bg-white dark:bg-purple-950 text-purple-700 dark:text-cyan-400 border-2 border-purple-700 dark:border-cyan-400 font-semibold py-2.5 px-6 rounded-lg mt-4 transition-all duration-200 hover:bg-purple-50 dark:hover:bg-purple-900" 205 - > 206 - Check Again 207 - </button> 208 - </div> 209 - </main> 210 - 211 - <footer 212 - class="p-4 text-center border-t border-purple-200 dark:border-slate-800 bg-white dark:bg-slate-900" 213 - > 214 - <a 215 - href="https://atlast.byarielm.fyi" 216 - target="_blank" 217 - class="text-orange-600 dark:text-orange-400 no-underline text-[13px] font-medium hover:underline" 218 - >atlast.byarielm.fyi</a 219 - > 220 - </footer> 221 - </div> 222 - 223 - <script type="module" src="./popup-dev.ts"></script> 224 - </body> 225 - </html>
-324
packages/extension/src/popup/popup-dev.ts
··· 1 - // packages/extension/src/popup/popup-dev.ts 2 - /** 3 - * Development entry point for popup UI 4 - * Simulates extension behavior for UI development 5 - */ 6 - 7 - import type { ExtensionState } from "../lib/messaging.js"; 8 - 9 - // Build mode injected at build time (mock for dev) 10 - declare const __BUILD_MODE__: string; 11 - const BUILD_MODE = "development"; 12 - 13 - // Mock browser object (handled by Vite alias) 14 - import browser from "webextension-polyfill"; 15 - 16 - /** 17 - * DOM elements 18 - */ 19 - const states = { 20 - idle: document.getElementById("state-idle")!, 21 - ready: document.getElementById("state-ready")!, 22 - scraping: document.getElementById("state-scraping")!, 23 - complete: document.getElementById("state-complete")!, 24 - uploading: document.getElementById("state-uploading")!, 25 - error: document.getElementById("state-error")!, 26 - offline: document.getElementById("state-offline")!, 27 - notLoggedIn: document.getElementById("state-not-logged-in")!, 28 - }; 29 - 30 - const elements = { 31 - platformName: document.getElementById("platform-name")!, 32 - count: document.getElementById("count")!, 33 - finalCount: document.getElementById("final-count")!, 34 - statusMessage: document.getElementById("status-message")!, 35 - errorMessage: document.getElementById("error-message")!, 36 - serverUrl: document.getElementById("server-url")!, 37 - devInstructions: document.getElementById("dev-instructions")!, 38 - progressFill: document.getElementById("progress-fill")! as HTMLElement, 39 - btnStart: document.getElementById("btn-start")! as HTMLButtonElement, 40 - btnUpload: document.getElementById("btn-upload")! as HTMLButtonElement, 41 - btnRetry: document.getElementById("btn-retry")! as HTMLButtonElement, 42 - btnCheckServer: document.getElementById( 43 - "btn-check-server", 44 - )! as HTMLButtonElement, 45 - btnOpenAtlast: document.getElementById( 46 - "btn-open-atlast", 47 - )! as HTMLButtonElement, 48 - btnRetryLogin: document.getElementById( 49 - "btn-retry-login", 50 - )! as HTMLButtonElement, 51 - }; 52 - 53 - /** 54 - * Show specific state, hide others 55 - */ 56 - function showState(stateName: keyof typeof states): void { 57 - Object.keys(states).forEach((key) => { 58 - states[key as keyof typeof states].classList.add("hidden"); 59 - }); 60 - states[stateName].classList.remove("hidden"); 61 - } 62 - 63 - /** 64 - * Update UI based on extension state 65 - */ 66 - function updateUI(state: ExtensionState): void { 67 - console.log("[Popup Dev] Updating UI with state:", state); 68 - 69 - switch (state.status) { 70 - case "idle": 71 - showState("idle"); 72 - break; 73 - 74 - case "ready": 75 - showState("ready"); 76 - if (state.platform) { 77 - const platformName = 78 - state.platform === "twitter" ? "Twitter/X" : state.platform; 79 - elements.platformName.textContent = platformName; 80 - } 81 - break; 82 - 83 - case "scraping": 84 - showState("scraping"); 85 - if (state.progress) { 86 - elements.count.textContent = state.progress.count.toString(); 87 - elements.statusMessage.textContent = state.progress.message || ""; 88 - 89 - // Animate progress bar 90 - const progress = Math.min(state.progress.count / 100, 1) * 100; 91 - elements.progressFill.style.width = `${progress}%`; 92 - } 93 - break; 94 - 95 - case "complete": 96 - showState("complete"); 97 - if (state.result) { 98 - elements.finalCount.textContent = state.result.totalCount.toString(); 99 - } 100 - break; 101 - 102 - case "uploading": 103 - showState("uploading"); 104 - break; 105 - 106 - case "error": 107 - showState("error"); 108 - elements.errorMessage.textContent = 109 - state.error || "An unknown error occurred"; 110 - break; 111 - 112 - default: 113 - showState("idle"); 114 - } 115 - } 116 - 117 - /** 118 - * Simulate state transitions for development 119 - */ 120 - let currentState: ExtensionState = { status: "idle" }; 121 - let simulationInterval: number | null = null; 122 - 123 - function simulateScraping() { 124 - let count = 0; 125 - currentState = { 126 - status: "scraping", 127 - platform: "twitter", 128 - pageType: "following", 129 - progress: { 130 - count: 0, 131 - status: "scraping", 132 - message: "Starting scan...", 133 - }, 134 - }; 135 - updateUI(currentState); 136 - 137 - simulationInterval = window.setInterval(() => { 138 - count += Math.floor(Math.random() * 25) + 5; 139 - 140 - if (count >= 247) { 141 - count = 247; 142 - if (simulationInterval) clearInterval(simulationInterval); 143 - 144 - currentState = { 145 - status: "complete", 146 - platform: "twitter", 147 - pageType: "following", 148 - result: { 149 - usernames: Array(247).fill("mockuser"), 150 - totalCount: 247, 151 - scrapedAt: new Date().toISOString(), 152 - }, 153 - }; 154 - updateUI(currentState); 155 - return; 156 - } 157 - 158 - currentState = { 159 - ...currentState, 160 - status: "scraping", 161 - progress: { 162 - count, 163 - status: "scraping", 164 - message: `Found ${count} users...`, 165 - }, 166 - }; 167 - updateUI(currentState); 168 - }, 500); 169 - } 170 - 171 - /** 172 - * Initialize popup 173 - */ 174 - async function init(): Promise<void> { 175 - console.log("[Popup Dev] Initializing development popup..."); 176 - 177 - // Show ready state for development 178 - currentState = { 179 - status: "ready", 180 - platform: "twitter", 181 - pageType: "following", 182 - }; 183 - updateUI(currentState); 184 - 185 - // Set up event listeners 186 - elements.btnStart.addEventListener("click", () => { 187 - console.log("[Popup Dev] Start scan clicked"); 188 - elements.btnStart.disabled = true; 189 - simulateScraping(); 190 - }); 191 - 192 - elements.btnUpload.addEventListener("click", () => { 193 - console.log("[Popup Dev] Upload clicked"); 194 - alert("In a real extension, this would open ATlast with your results!"); 195 - }); 196 - 197 - elements.btnRetry.addEventListener("click", () => { 198 - console.log("[Popup Dev] Retry clicked"); 199 - currentState = { 200 - status: "ready", 201 - platform: "twitter", 202 - pageType: "following", 203 - }; 204 - updateUI(currentState); 205 - elements.btnStart.disabled = false; 206 - }); 207 - 208 - elements.btnCheckServer.addEventListener("click", () => { 209 - console.log("[Popup Dev] Check server clicked"); 210 - currentState = { 211 - status: "ready", 212 - platform: "twitter", 213 - pageType: "following", 214 - }; 215 - updateUI(currentState); 216 - }); 217 - 218 - elements.btnOpenAtlast.addEventListener("click", () => { 219 - console.log("[Popup Dev] Open ATlast clicked"); 220 - window.open("http://127.0.0.1:8888", "_blank"); 221 - }); 222 - 223 - elements.btnRetryLogin.addEventListener("click", () => { 224 - console.log("[Popup Dev] Retry login clicked"); 225 - currentState = { 226 - status: "ready", 227 - platform: "twitter", 228 - pageType: "following", 229 - }; 230 - updateUI(currentState); 231 - }); 232 - 233 - // Add dev toolbar 234 - addDevToolbar(); 235 - 236 - console.log("[Popup Dev] Ready"); 237 - } 238 - 239 - /** 240 - * Add development toolbar for testing different states 241 - */ 242 - function addDevToolbar() { 243 - const toolbar = document.createElement("div"); 244 - toolbar.style.cssText = ` 245 - position: fixed; 246 - bottom: 0; 247 - left: 0; 248 - right: 0; 249 - background: #1e293b; 250 - color: white; 251 - padding: 8px; 252 - font-size: 12px; 253 - border-top: 2px solid #f97316; 254 - display: flex; 255 - gap: 8px; 256 - flex-wrap: wrap; 257 - z-index: 10000; 258 - `; 259 - 260 - const createButton = (label: string, state: ExtensionState) => { 261 - const btn = document.createElement("button"); 262 - btn.textContent = label; 263 - btn.style.cssText = ` 264 - background: #f97316; 265 - color: white; 266 - border: none; 267 - padding: 4px 8px; 268 - border-radius: 4px; 269 - cursor: pointer; 270 - font-size: 11px; 271 - `; 272 - btn.onclick = () => { 273 - currentState = state; 274 - updateUI(state); 275 - elements.btnStart.disabled = false; 276 - }; 277 - return btn; 278 - }; 279 - 280 - toolbar.innerHTML = '<strong style="margin-right: 8px;">Dev Tools:</strong>'; 281 - toolbar.appendChild(createButton("Idle", { status: "idle" })); 282 - toolbar.appendChild( 283 - createButton("Ready", { 284 - status: "ready", 285 - platform: "twitter", 286 - pageType: "following", 287 - }), 288 - ); 289 - toolbar.appendChild( 290 - createButton("Scraping", { 291 - status: "scraping", 292 - platform: "twitter", 293 - progress: { count: 42, status: "scraping", message: "Found 42 users..." }, 294 - }), 295 - ); 296 - toolbar.appendChild( 297 - createButton("Complete", { 298 - status: "complete", 299 - platform: "twitter", 300 - result: { 301 - usernames: [], 302 - totalCount: 247, 303 - scrapedAt: new Date().toISOString(), 304 - }, 305 - }), 306 - ); 307 - toolbar.appendChild( 308 - createButton("Error", { 309 - status: "error", 310 - error: "Failed to scrape page", 311 - }), 312 - ); 313 - toolbar.appendChild(createButton("Offline", { status: "idle" })); 314 - toolbar.appendChild(createButton("Not Logged In", { status: "idle" })); 315 - 316 - document.body.appendChild(toolbar); 317 - } 318 - 319 - // Initialize when DOM is ready 320 - if (document.readyState === "loading") { 321 - document.addEventListener("DOMContentLoaded", init); 322 - } else { 323 - init(); 324 - }
+263 -19
packages/extension/src/popup/popup.ts
··· 8 8 9 9 // Build mode injected at build time 10 10 declare const __BUILD_MODE__: string; 11 + const IS_DEV_MODE = __BUILD_MODE__ === "development"; 11 12 12 13 /** 13 14 * DOM elements ··· 60 61 * Update UI based on extension state 61 62 */ 62 63 function updateUI(state: ExtensionState): void { 63 - console.log("[Popup] 🎨 Updating UI with state:", state); 64 - console.log("[Popup] 🎯 Current status:", state.status); 65 - console.log("[Popup] 🌐 Platform:", state.platform); 66 - console.log("[Popup] 📄 Page type:", state.pageType); 64 + console.log("[Popup] Updating UI with state:", state); 67 65 68 66 switch (state.status) { 69 67 case "idle": ··· 113 111 } 114 112 } 115 113 114 + // ============================================================================ 115 + // DEV MODE: Mock state and UI injection 116 + // ============================================================================ 117 + 118 + let devState: ExtensionState = { status: "idle" }; 119 + let devSimulationInterval: number | null = null; 120 + 121 + /** 122 + * Inject dev mode banner at top of page 123 + */ 124 + function injectDevBanner(): void { 125 + const banner = document.createElement("div"); 126 + banner.id = "dev-banner"; 127 + banner.style.cssText = ` 128 + background: #f97316; 129 + color: white; 130 + text-align: center; 131 + padding: 4px; 132 + font-size: 11px; 133 + font-weight: bold; 134 + `; 135 + banner.textContent = "🔧 DEVELOPMENT MODE"; 136 + document.body.insertBefore(banner, document.body.firstChild); 137 + } 138 + 139 + /** 140 + * Inject dev toolbar for state switching 141 + */ 142 + function injectDevToolbar(): void { 143 + const toolbar = document.createElement("div"); 144 + toolbar.id = "dev-toolbar"; 145 + toolbar.style.cssText = ` 146 + position: fixed; 147 + bottom: 0; 148 + left: 0; 149 + right: 0; 150 + background: #1e293b; 151 + color: white; 152 + padding: 8px; 153 + font-size: 12px; 154 + border-top: 2px solid #f97316; 155 + display: flex; 156 + gap: 8px; 157 + flex-wrap: wrap; 158 + z-index: 10000; 159 + `; 160 + 161 + const createButton = (label: string, state: ExtensionState) => { 162 + const btn = document.createElement("button"); 163 + btn.textContent = label; 164 + btn.style.cssText = ` 165 + background: #f97316; 166 + color: white; 167 + border: none; 168 + padding: 4px 8px; 169 + border-radius: 4px; 170 + cursor: pointer; 171 + font-size: 11px; 172 + `; 173 + btn.onclick = () => { 174 + devState = state; 175 + updateUI(state); 176 + elements.btnStart.disabled = false; 177 + }; 178 + return btn; 179 + }; 180 + 181 + const label = document.createElement("strong"); 182 + label.textContent = "Dev Tools:"; 183 + label.style.marginRight = "8px"; 184 + toolbar.appendChild(label); 185 + 186 + toolbar.appendChild(createButton("Idle", { status: "idle" })); 187 + toolbar.appendChild( 188 + createButton("Ready", { 189 + status: "ready", 190 + platform: "twitter", 191 + pageType: "following", 192 + }), 193 + ); 194 + toolbar.appendChild( 195 + createButton("Scraping", { 196 + status: "scraping", 197 + platform: "twitter", 198 + progress: { count: 42, status: "scraping", message: "Found 42 users..." }, 199 + }), 200 + ); 201 + toolbar.appendChild( 202 + createButton("Complete", { 203 + status: "complete", 204 + platform: "twitter", 205 + result: { 206 + usernames: [], 207 + totalCount: 247, 208 + scrapedAt: new Date().toISOString(), 209 + }, 210 + }), 211 + ); 212 + toolbar.appendChild( 213 + createButton("Error", { 214 + status: "error", 215 + error: "Failed to scrape page", 216 + }), 217 + ); 218 + toolbar.appendChild(createButton("Offline", { status: "idle" })); 219 + toolbar.appendChild(createButton("Not Logged In", { status: "idle" })); 220 + 221 + document.body.appendChild(toolbar); 222 + } 223 + 224 + /** 225 + * Simulate scraping progress for dev mode 226 + */ 227 + function devSimulateScraping(): void { 228 + let count = 0; 229 + devState = { 230 + status: "scraping", 231 + platform: "twitter", 232 + pageType: "following", 233 + progress: { 234 + count: 0, 235 + status: "scraping", 236 + message: "Starting scan...", 237 + }, 238 + }; 239 + updateUI(devState); 240 + 241 + devSimulationInterval = window.setInterval(() => { 242 + count += Math.floor(Math.random() * 25) + 5; 243 + 244 + if (count >= 247) { 245 + count = 247; 246 + if (devSimulationInterval) clearInterval(devSimulationInterval); 247 + 248 + devState = { 249 + status: "complete", 250 + platform: "twitter", 251 + pageType: "following", 252 + result: { 253 + usernames: Array(247).fill("mockuser"), 254 + totalCount: 247, 255 + scrapedAt: new Date().toISOString(), 256 + }, 257 + }; 258 + updateUI(devState); 259 + return; 260 + } 261 + 262 + devState = { 263 + ...devState, 264 + status: "scraping", 265 + progress: { 266 + count, 267 + status: "scraping", 268 + message: `Found ${count} users...`, 269 + }, 270 + }; 271 + updateUI(devState); 272 + }, 500); 273 + } 274 + 275 + /** 276 + * Initialize dev mode 277 + */ 278 + function initDevMode(): void { 279 + console.log("[Popup Dev] Initializing development mode..."); 280 + 281 + // Inject dev UI 282 + injectDevBanner(); 283 + injectDevToolbar(); 284 + 285 + // Start with ready state 286 + devState = { 287 + status: "ready", 288 + platform: "twitter", 289 + pageType: "following", 290 + }; 291 + updateUI(devState); 292 + 293 + // Set up event listeners for dev mode 294 + elements.btnStart.addEventListener("click", () => { 295 + console.log("[Popup Dev] Start scan clicked"); 296 + elements.btnStart.disabled = true; 297 + devSimulateScraping(); 298 + }); 299 + 300 + elements.btnUpload.addEventListener("click", () => { 301 + console.log("[Popup Dev] Upload clicked"); 302 + alert("In dev mode - would open ATlast with results!"); 303 + }); 304 + 305 + elements.btnRetry.addEventListener("click", () => { 306 + console.log("[Popup Dev] Retry clicked"); 307 + devState = { 308 + status: "ready", 309 + platform: "twitter", 310 + pageType: "following", 311 + }; 312 + updateUI(devState); 313 + elements.btnStart.disabled = false; 314 + }); 315 + 316 + elements.btnCheckServer.addEventListener("click", () => { 317 + console.log("[Popup Dev] Check server clicked"); 318 + devState = { 319 + status: "ready", 320 + platform: "twitter", 321 + pageType: "following", 322 + }; 323 + updateUI(devState); 324 + }); 325 + 326 + elements.btnOpenAtlast.addEventListener("click", () => { 327 + console.log("[Popup Dev] Open ATlast clicked"); 328 + window.open("http://127.0.0.1:8888", "_blank"); 329 + }); 330 + 331 + elements.btnRetryLogin.addEventListener("click", () => { 332 + console.log("[Popup Dev] Retry login clicked"); 333 + devState = { 334 + status: "ready", 335 + platform: "twitter", 336 + pageType: "following", 337 + }; 338 + updateUI(devState); 339 + }); 340 + 341 + console.log("[Popup Dev] Ready"); 342 + } 343 + 344 + // ============================================================================ 345 + // PRODUCTION MODE: Real extension logic 346 + // ============================================================================ 347 + 116 348 /** 117 349 * Start scraping 118 350 */ ··· 217 449 * Check server health and show offline state if needed 218 450 */ 219 451 async function checkServer(): Promise<boolean> { 220 - console.log("[Popup] 🏥 Checking server health..."); 452 + console.log("[Popup] Checking server health..."); 221 453 222 454 // Import health check function 223 455 const { checkServerHealth, getApiUrl } = await import("../lib/api-client.js"); ··· 225 457 const isOnline = await checkServerHealth(); 226 458 227 459 if (!isOnline) { 228 - console.log("[Popup] ❌ Server is offline"); 460 + console.log("[Popup] Server is offline"); 229 461 showState("offline"); 230 462 231 463 // Show appropriate message based on build mode ··· 244 476 return false; 245 477 } 246 478 247 - console.log("[Popup] ✅ Server is online"); 479 + console.log("[Popup] Server is online"); 248 480 return true; 249 481 } 250 482 251 483 /** 252 - * Initialize popup 484 + * Initialize production mode 253 485 */ 254 - async function init(): Promise<void> { 255 - console.log("[Popup] 🚀 Initializing popup..."); 486 + async function initProdMode(): Promise<void> { 487 + console.log("[Popup] Initializing popup..."); 256 488 257 489 // Check server health first (only in dev mode) 258 490 const { getApiUrl } = await import("../lib/api-client.js"); ··· 270 502 const online = await checkServer(); 271 503 if (online) { 272 504 // Server is back online, re-initialize 273 - init(); 505 + initProdMode(); 274 506 } else { 275 507 elements.btnCheckServer.disabled = false; 276 508 elements.btnCheckServer.textContent = "Check Again"; ··· 281 513 } 282 514 283 515 // Check if user is logged in to ATlast 284 - console.log("[Popup] 🔐 Checking login status..."); 516 + console.log("[Popup] Checking login status..."); 285 517 const { checkSession } = await import("../lib/api-client.js"); 286 518 const session = await checkSession(); 287 519 288 520 if (!session) { 289 - console.log("[Popup] ❌ Not logged in"); 521 + console.log("[Popup] Not logged in"); 290 522 showState("notLoggedIn"); 291 523 292 524 // Set up login buttons ··· 301 533 const newSession = await checkSession(); 302 534 if (newSession) { 303 535 // User is now logged in, re-initialize 304 - init(); 536 + initProdMode(); 305 537 } else { 306 538 elements.btnRetryLogin.disabled = false; 307 539 elements.btnRetryLogin.textContent = "Check Again"; ··· 310 542 return; 311 543 } 312 544 313 - console.log("[Popup] ✅ Logged in as", session.handle); 545 + console.log("[Popup] Logged in as", session.handle); 314 546 315 547 // Get current state 316 - console.log("[Popup] 📡 Requesting state from background..."); 548 + console.log("[Popup] Requesting state from background..."); 317 549 const state = await sendToBackground<ExtensionState>({ 318 550 type: MessageType.GET_STATE, 319 551 }); 320 552 321 - console.log("[Popup] 📥 Received state from background:", state); 553 + console.log("[Popup] Received state from background:", state); 322 554 updateUI(state); 323 555 324 556 // Set up event listeners ··· 335 567 browser.storage.onChanged.addListener((changes, areaName) => { 336 568 if (areaName === "local" && changes.extensionState) { 337 569 const newState = changes.extensionState.newValue; 338 - console.log("[Popup] 🔄 Storage changed, new state:", newState); 570 + console.log("[Popup] Storage changed, new state:", newState); 339 571 updateUI(newState); 340 572 } 341 573 }); ··· 345 577 pollForUpdates(); 346 578 } 347 579 348 - console.log("[Popup] ✅ Popup ready"); 580 + console.log("[Popup] Popup ready"); 581 + } 582 + 583 + // ============================================================================ 584 + // INITIALIZATION 585 + // ============================================================================ 586 + 587 + function init(): void { 588 + if (IS_DEV_MODE) { 589 + initDevMode(); 590 + } else { 591 + initProdMode(); 592 + } 349 593 } 350 594 351 595 // Initialize when DOM is ready
+1 -1
packages/extension/vite.config.ts
··· 10 10 }, 11 11 server: { 12 12 port: 5174, 13 - open: "/popup-dev.html", 13 + open: "/popup.html", 14 14 }, 15 15 define: { 16 16 __ATLAST_API_URL__: JSON.stringify("http://localhost:8888"),