feat: Implement main application shell with navigation and color scheme toggle
- Added Appshell component with responsive navbar and main content area - Integrated ColorSchemeToggle for light/dark mode switching - Created Welcome component with styled title and introductory text - Developed ChatPage for LLM interaction with WebSocket support - Implemented Biblioteca for managing notes with rich text editor - Added LoginPage for user authentication with error handling - Introduced MessageList and MessageBubble components for chat messages - Styled components with CSS modules for consistent design
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
import asyncio
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
import json
|
||||
from typing import Optional
|
||||
import aiohttp
|
||||
|
||||
|
||||
class Navegador:
|
||||
def __init__(self,
|
||||
chrome_path: str,
|
||||
user_data_dir: str,
|
||||
id: Optional[int] = None,
|
||||
download_dir: Optional[str] = None,
|
||||
debugging_port: int = 9222,
|
||||
headless: bool = False,
|
||||
user_agent: Optional[str] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"):
|
||||
self.chrome_path = chrome_path
|
||||
self.user_data_dir = user_data_dir
|
||||
self.id = id
|
||||
self.download_dir = download_dir or os.path.join(self.user_data_dir, "downloads")
|
||||
self.debugging_port = debugging_port
|
||||
self.headless = headless
|
||||
self.user_agent = user_agent
|
||||
self.chrome_process: Optional[subprocess.Popen] = None
|
||||
|
||||
async def _esperar_debugger(self, timeout=10):
|
||||
url = f"http://127.0.0.1:{self.debugging_port}/json"
|
||||
for _ in range(timeout * 10): # 10 intentos por segundo
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(url) as resp:
|
||||
if resp.status == 200:
|
||||
print("✅ Chrome listo para debugging.")
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
await asyncio.sleep(0.1)
|
||||
raise RuntimeError("❌ Chrome no respondió en el puerto de debugging.")
|
||||
|
||||
def _preconfigurar_preferencias(self):
|
||||
prefs_path = os.path.join(self.user_data_dir, "Default", "Preferences")
|
||||
os.makedirs(os.path.dirname(prefs_path), exist_ok=True)
|
||||
os.makedirs(self.download_dir, exist_ok=True)
|
||||
|
||||
prefs = {
|
||||
"profile": {
|
||||
"exit_type": "Normal",
|
||||
"exited_cleanly": True
|
||||
},
|
||||
"browser": {
|
||||
"has_seen_welcome_page": True
|
||||
},
|
||||
"distribution": {
|
||||
"skip_first_run_ui": True
|
||||
},
|
||||
"download": {
|
||||
"default_directory": self.download_dir,
|
||||
"prompt_for_download": False,
|
||||
"directory_upgrade": True,
|
||||
"extensions_to_open": ""
|
||||
},
|
||||
"savefile": {
|
||||
"default_directory": self.download_dir
|
||||
}
|
||||
}
|
||||
|
||||
if os.path.exists(prefs_path):
|
||||
try:
|
||||
with open(prefs_path, "r", encoding="utf-8") as f:
|
||||
existing = json.load(f)
|
||||
existing.update(prefs)
|
||||
prefs = existing
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
with open(prefs_path, "w", encoding="utf-8") as f:
|
||||
json.dump(prefs, f, indent=2)
|
||||
|
||||
def _build_args(self):
|
||||
os.makedirs(self.user_data_dir, exist_ok=True)
|
||||
self._preconfigurar_preferencias()
|
||||
|
||||
args = [
|
||||
f"--remote-debugging-port={self.debugging_port}",
|
||||
f"--user-data-dir={self.user_data_dir}",
|
||||
"--disable-blink-features=AutomationControlled",
|
||||
"--no-sandbox",
|
||||
# "--disable-web-security",
|
||||
# "--disable-extensions",
|
||||
# "--disable-dev-shm-usage",
|
||||
"--disable-infobars",
|
||||
"--disable-popup-blocking",
|
||||
"--disable-default-apps",
|
||||
"--mute-audio",
|
||||
"--window-size=1024,1024",
|
||||
"--no-first-run",
|
||||
"--no-default-browser-check",
|
||||
"--disable-features=DefaultBrowserPrompt",
|
||||
"--disable-component-update",
|
||||
"--disable-background-networking",
|
||||
"--disable-sync",
|
||||
"--disable-translate",
|
||||
"--disable-background-timer-throttling",
|
||||
"--disable-client-side-phishing-detection",
|
||||
"--disable-component-extensions-with-background-pages",
|
||||
"--metrics-recording-only",
|
||||
"--safebrowsing-disable-auto-update",
|
||||
|
||||
|
||||
]
|
||||
|
||||
if self.headless:
|
||||
args.append("--headless=new")
|
||||
|
||||
if self.user_agent:
|
||||
args.append(f"--user-agent={self.user_agent}")
|
||||
|
||||
return args
|
||||
|
||||
|
||||
|
||||
async def inyectar_spoof_chrome(self):
|
||||
script = """
|
||||
window.chrome = {
|
||||
app: {
|
||||
isInstalled: false,
|
||||
InstallState: {
|
||||
DISABLED: 'disabled',
|
||||
INSTALLED: 'installed',
|
||||
NOT_INSTALLED: 'not_installed'
|
||||
},
|
||||
RunningState: {
|
||||
CANNOT_RUN: 'cannot_run',
|
||||
READY_TO_RUN: 'ready_to_run',
|
||||
RUNNING: 'running'
|
||||
}
|
||||
},
|
||||
runtime: {
|
||||
PlatformOs: { MAC: 'mac', WIN: 'win', ANDROID: 'android', CROS: 'cros', LINUX: 'linux', OPENBSD: 'openbsd' },
|
||||
PlatformArch: { ARM: 'arm', X86_32: 'x86-32', X86_64: 'x86-64' },
|
||||
PlatformNaclArch: { ARM: 'arm', X86_32: 'x86-32', X86_64: 'x86-64' },
|
||||
RequestUpdateCheckStatus: { THROTTLED: 'throttled', NO_UPDATE: 'no_update', UPDATE_AVAILABLE: 'update_available' },
|
||||
OnInstalledReason: { INSTALL: 'install', UPDATE: 'update', CHROME_UPDATE: 'chrome_update', SHARED_MODULE_UPDATE: 'shared_module_update' },
|
||||
OnRestartRequiredReason: { APP_UPDATE: 'app_update', OS_UPDATE: 'os_update', PERIODIC: 'periodic' }
|
||||
}
|
||||
};
|
||||
"""
|
||||
|
||||
url = f"http://127.0.0.1:{self.debugging_port}/json"
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(url) as resp:
|
||||
targets = await resp.json()
|
||||
|
||||
for target in targets:
|
||||
if "webSocketDebuggerUrl" not in target:
|
||||
continue
|
||||
|
||||
target_id = target["id"]
|
||||
async with session.post(
|
||||
f"http://127.0.0.1:{self.debugging_port}/json/protocol",
|
||||
json={"targetId": target_id}
|
||||
):
|
||||
pass # CDP protocol fetch optional
|
||||
|
||||
async with session.post(
|
||||
f"http://127.0.0.1:{self.debugging_port}/json/send",
|
||||
json={
|
||||
"id": 1,
|
||||
"method": "Page.addScriptToEvaluateOnNewDocument",
|
||||
"params": {"source": script}
|
||||
}
|
||||
) as inject_resp:
|
||||
if inject_resp.status == 200:
|
||||
print("✅ chrome.* spoof inyectado.")
|
||||
|
||||
|
||||
async def iniciar(self):
|
||||
args = self._build_args()
|
||||
self.chrome_process = subprocess.Popen([self.chrome_path] + args)
|
||||
print(f"Chrome iniciado (headless={self.headless}). Esperando disponibilidad del debugger...")
|
||||
await self._esperar_debugger()
|
||||
await self.inyectar_spoof_chrome()
|
||||
|
||||
async def cerrar(self):
|
||||
if self.chrome_process and self.chrome_process.poll() is None:
|
||||
self.chrome_process.terminate()
|
||||
try:
|
||||
await asyncio.wait_for(asyncio.to_thread(self.chrome_process.wait), timeout=5)
|
||||
except asyncio.TimeoutError:
|
||||
self.chrome_process.kill()
|
||||
print("🛑 Chrome cerrado correctamente.")
|
||||
Reference in New Issue
Block a user