https://drive.google.com/file/d/1ttlOTacHBs6VKoemC02fhcUjz8jqxiqr/view?usp=sharing
SIDX1 ZEN BROWSER — fast, clean, no BS
Created because I was sick of fat browsers tracking you and making everything slower.
Windows version (.exe):
Just download and double-click it. There’s no installation. It can only be run on Windows.
Python version (any OS):
Make sure Python 3 is installed:
python --version
# or
python3 --version
If it isn’t installed, then download it from python.org or install it using your Linux package manager.
Debian/Ubuntu: sudo apt update && sudo apt install python3 python3-pip
Fedora: sudo dnf install python3 python3-pip
Arch: sudo pacman -S python python-pip
Change to the directory where you want the browser to be stored:
bash
Copy
Edit
cd path/to/your/directory
Create a virtual environment for the browser:
python -m venv venv
Activate it:
Windows CMD: .\venv\Scripts\activate
PowerShell: .\venv\Scripts\Activate.ps1
Linux/macOS: source venv/bin/activate
Install the necessary dependencies:
pip install PyQt5 PyQtWebEngine
If you want the AI assistant:
pip install openai
Launch the browser:
python main.py
Or if you don’t want to create new files, you can just open python and paste the code.
Done. You now have a lightweight browser with offline support and no tracking, built by me.
Subscribe to my YouTube channel for more projects and updates:
https://www.youtube.com/@sidx1-scratch-and-apps
— sidx1
this is the code you need btw:
import sys
import os
import json
import hashlib
import traceback
import time
import re
import base64
from typing import Optional, Tuple
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QTabWidget,
QInputDialog, QLineEdit, QMessageBox, QTextEdit, QPushButton, QDialog,
QLabel, QFrame, QToolButton, QSizePolicy, QSpacerItem, QGroupBox, QFormLayout,
QProgressBar, QStatusBar, QAction, QMenuBar, QMenu, QCheckBox
)
from PyQt5.QtCore import QUrl, Qt, pyqtSignal, QTimer, QThread, pyqtSlot
from PyQt5.QtGui import QFont, QIcon, QPalette, QColor, QKeySequence
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage, QWebEngineProfile
# Optional OpenAI import with fallback
try:
import openai
OPENAI_AVAILABLE = True
except ImportError:
OPENAI_AVAILABLE = False
# === Utility Functions ===
def get_app_data_dir():
"""Get the application data directory in the user's home folder."""
home = os.path.expanduser("~")
app_dir = os.path.join(home, ".sidx1_browser")
os.makedirs(app_dir, exist_ok=True)
return app_dir
def xor_cipher(data: str, key: str) -> str:
"""A simple XOR cipher for basic data obfuscation."""
if not data or not key:
return ""
key_stream = (key * (len(data) // len(key) + 1))[:len(data)]
return ''.join(chr(ord(c1) ^ ord(c2)) for c1, c2 in zip(data, key_stream))
def encrypt_data(data: str, password: str) -> str:
"""Encrypts data using a password-derived key."""
if not data:
return ""
key = hashlib.sha256(password.encode()).hexdigest()
encrypted = xor_cipher(data, key)
return base64.b64encode(encrypted.encode()).decode()
def decrypt_data(encrypted_data: str, password: str) -> str:
"""Decrypts data using a password-derived key."""
if not encrypted_data:
return ""
key = hashlib.sha256(password.encode()).hexdigest()
try:
decoded_b64 = base64.b64decode(encrypted_data.encode()).decode()
return xor_cipher(decoded_b64, key)
except (base64.binascii.Error, UnicodeDecodeError):
return "" # Failed to decrypt
def secure_hash(password: str, salt: str = None) -> Tuple[str, str]:
"""Create secure hash with salt"""
if salt is None:
salt = os.urandom(32).hex()
key = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'),
salt.encode('utf-8'), 100000)
return key.hex(), salt
def verify_password(stored_hash: str, stored_salt: str, password: str) -> bool:
"""Verify password against stored hash"""
computed_hash, _ = secure_hash(password, stored_salt)
return computed_hash == stored_hash
def is_valid_url(url: str) -> bool:
"""Check if URL is valid"""
from urllib.parse import urlparse
try:
result = urlparse(url)
return all([result.scheme, result.netloc])
except:
return False
# === OpenAI API Thread ===
class OpenAIWorker(QThread):
response_ready = pyqtSignal(str)
error_occurred = pyqtSignal(str)
def __init__(self, api_key: str, prompt: str):
super().__init__()
self.api_key = api_key
self.prompt = prompt
def run(self):
try:
if not OPENAI_AVAILABLE:
self.error_occurred.emit("OpenAI library not installed. Please install with: pip install openai")
return
from openai import OpenAI
client = OpenAI(api_key=self.api_key)
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": self.prompt}],
temperature=0.7,
max_tokens=400
)
answer = response.choices[0].message.content.strip()
self.response_ready.emit(answer)
except Exception as e:
self.error_occurred.emit(f"OpenAI API Error: {str(e)}")
# === Account Management ===
class AccountManager:
def __init__(self):
self.accounts_file = os.path.join(get_app_data_dir(), "accounts.json")
self.accounts = self.load_accounts()
def load_accounts(self) -> dict:
if not os.path.exists(self.accounts_file):
return {}
try:
with open(self.accounts_file, 'r', encoding='utf-8') as f:
return json.load(f)
except (json.JSONDecodeError, IOError) as e:
QMessageBox.warning(None, "Account Load Error", f"Could not load accounts file: {e}")
return {}
def save_accounts(self) -> bool:
try:
with open(self.accounts_file, 'w', encoding='utf-8') as f:
json.dump(self.accounts, f, indent=2, ensure_ascii=False)
return True
except (IOError, OSError) as e:
QMessageBox.critical(None, "Account Save Error", f"Failed to save accounts: {e}")
return False
def create_account(self, username: str, password: str, api_key: str = "") -> Tuple[bool, str]:
if not username or not password:
return False, "Username and password are required"
if username in self.accounts:
return False, "Username already exists"
password_hash, salt = secure_hash(password)
encrypted_api_key = encrypt_data(api_key, password)
self.accounts[username] = {
"password": password_hash,
"salt": salt,
"api_key": encrypted_api_key,
"created_at": str(time.time())
}
if self.save_accounts():
return True, "Account created successfully"
else:
# Remove the account if saving failed
self.accounts.pop(username, None)
return False, "Failed to save account"
def authenticate(self, username: str, password: str) -> Tuple[bool, Optional[str]]:
if username not in self.accounts:
return False, None
account = self.accounts[username]
# Handle legacy accounts without salt
if 'salt' not in account:
old_hash = hashlib.sha256(password.encode()).hexdigest()
if account['password'] == old_hash:
# Migrate account to new format
new_hash, salt = secure_hash(password)
account['password'] = new_hash
account['salt'] = salt
# Encrypt existing API key if present
if "api_key" in account and account["api_key"]:
account["api_key"] = encrypt_data(account["api_key"], password)
self.save_accounts()
decrypted_key = decrypt_data(account.get("api_key", ""), password)
return True, decrypted_key
return False, None
if verify_password(account['password'], account['salt'], password):
decrypted_key = decrypt_data(account.get("api_key", ""), password)
return True, decrypted_key
return False, None
def update_api_key(self, username: str, password: str, api_key: str) -> bool:
if username in self.accounts:
encrypted_key = encrypt_data(api_key, password)
self.accounts[username]["api_key"] = encrypted_key
return self.save_accounts()
return False
# === Login Dialog ===
class LoginDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Sidx1 Zen Browser - Login")
self.setFixedSize(450, 350)
self.setModal(True)
self.account_manager = AccountManager()
self.username = None
self.api_key = None
self.password = None
self.setup_ui()
self.setup_style()
def setup_style(self):
self.setStyleSheet("""
QDialog {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 #2c3e50, stop:1 #34495e);
}
QGroupBox {
font-weight: bold;
color: #ecf0f1;
border: 2px solid #3498db;
border-radius: 10px;
margin-top: 10px;
padding-top: 10px;
}
QLineEdit {
background-color: #ecf0f1;
border: 2px solid #bdc3c7;
border-radius: 8px;
padding: 10px;
font-size: 13px;
}
QLineEdit:focus {
border: 2px solid #3498db;
}
QPushButton {
background-color: #3498db;
color: white;
border: none;
border-radius: 8px;
padding: 12px;
font-weight: bold;
font-size: 13px;
min-width: 100px;
}
QPushButton:hover {
background-color: #2980b9;
}
QPushButton:pressed {
background-color: #21618c;
}
QLabel {
color: #ecf0f1;
}
""")
def setup_ui(self):
layout = QVBoxLayout()
layout.setSpacing(20)
title = QLabel("Welcome to Sidx1 Zen Browser")
title.setAlignment(Qt.AlignCenter)
title.setStyleSheet("color: #ecf0f1; font-size: 20px; font-weight: bold; margin: 20px;")
layout.addWidget(title)
login_group = QGroupBox("Login")
login_layout = QFormLayout()
login_layout.setSpacing(15)
self.username_edit = QLineEdit()
self.username_edit.setPlaceholderText("Enter username")
self.password_edit = QLineEdit()
self.password_edit.setPlaceholderText("Enter password")
self.password_edit.setEchoMode(QLineEdit.Password)
self.remember_checkbox = QCheckBox("Remember me")
self.remember_checkbox.setStyleSheet("color: #ecf0f1;")
login_layout.addRow("Username:", self.username_edit)
login_layout.addRow("Password:", self.password_edit)
login_layout.addRow("", self.remember_checkbox)
login_group.setLayout(login_layout)
layout.addWidget(login_group)
button_layout = QHBoxLayout()
button_layout.setSpacing(10)
self.login_btn = QPushButton("Login")
self.create_account_btn = QPushButton("Create Account")
self.exit_btn = QPushButton("Exit")
button_layout.addWidget(self.login_btn)
button_layout.addWidget(self.create_account_btn)
button_layout.addWidget(self.exit_btn)
layout.addLayout(button_layout)
self.status_label = QLabel("")
self.status_label.setAlignment(Qt.AlignCenter)
self.status_label.setStyleSheet("color: #e74c3c; font-weight: bold;")
layout.addWidget(self.status_label)
self.setLayout(layout)
self.login_btn.clicked.connect(self.login)
self.create_account_btn.clicked.connect(self.create_account)
self.exit_btn.clicked.connect(self.reject)
self.password_edit.returnPressed.connect(self.login)
self.username_edit.returnPressed.connect(self.password_edit.setFocus)
def login(self):
username = self.username_edit.text().strip()
password = self.password_edit.text().strip()
if not username or not password:
self.show_status("Please enter both username and password", error=True)
return
self.login_btn.setEnabled(False)
self.login_btn.setText("Logging in...")
success, api_key = self.account_manager.authenticate(username, password)
if success:
self.username = username
self.api_key = api_key or ""
self.password = password
self.show_status("Login successful!", error=False)
QTimer.singleShot(500, self.accept)
else:
self.show_status("Invalid username or password", error=True)
self.login_btn.setEnabled(True)
self.login_btn.setText("Login")
def create_account(self):
dialog = CreateAccountDialog(self)
if dialog.exec_() == QDialog.Accepted:
self.show_status("Account created successfully! Please login.", error=False)
def show_status(self, message: str, error: bool = False):
self.status_label.setText(message)
color = "#e74c3c" if error else "#27ae60"
self.status_label.setStyleSheet(f"color: {color}; font-weight: bold;")
if not error:
QTimer.singleShot(2000, lambda: self.status_label.setText(""))
# === Create Account Dialog ===
class CreateAccountDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Create Account")
self.setFixedSize(500, 400)
self.setModal(True)
if parent:
self.setStyleSheet(parent.styleSheet())
self.account_manager = parent.account_manager if parent else AccountManager()
self.setup_ui()
def setup_ui(self):
layout = QVBoxLayout()
layout.setSpacing(20)
title = QLabel("Create New Account")
title.setAlignment(Qt.AlignCenter)
title.setStyleSheet("color: #ecf0f1; font-size: 18px; font-weight: bold; margin: 20px;")
layout.addWidget(title)
form_group = QGroupBox("Account Details")
form_layout = QFormLayout()
form_layout.setSpacing(15)
self.username_edit = QLineEdit()
self.username_edit.setPlaceholderText("Choose a username (3-20 characters)")
self.password_edit = QLineEdit()
self.password_edit.setPlaceholderText("At least 8 chars, 1 number, 1 symbol")
self.password_edit.setEchoMode(QLineEdit.Password)
self.confirm_password_edit = QLineEdit()
self.confirm_password_edit.setPlaceholderText("Confirm password")
self.confirm_password_edit.setEchoMode(QLineEdit.Password)
self.api_key_edit = QLineEdit()
self.api_key_edit.setPlaceholderText("OpenAI API Key (optional)")
form_layout.addRow("Username:", self.username_edit)
form_layout.addRow("Password:", self.password_edit)
form_layout.addRow("Confirm Password:", self.confirm_password_edit)
form_layout.addRow("OpenAI API Key:", self.api_key_edit)
form_group.setLayout(form_layout)
layout.addWidget(form_group)
button_layout = QHBoxLayout()
button_layout.setSpacing(10)
self.create_btn = QPushButton("Create Account")
self.cancel_btn = QPushButton("Cancel")
button_layout.addWidget(self.create_btn)
button_layout.addWidget(self.cancel_btn)
layout.addLayout(button_layout)
self.status_label = QLabel("")
self.status_label.setAlignment(Qt.AlignCenter)
self.status_label.setStyleSheet("color: #e74c3c; font-weight: bold;")
layout.addWidget(self.status_label)
self.setLayout(layout)
self.create_btn.clicked.connect(self.create_account)
self.cancel_btn.clicked.connect(self.reject)
self.confirm_password_edit.returnPressed.connect(self.create_account)
def is_strong_password(self, password: str) -> bool:
if len(password) < 8:
return False
if not re.search(r"\d", password):
return False
if not re.search(r"[!@#$%^&*(),.?\":{}|<>]", password):
return False
return True
def create_account(self):
username = self.username_edit.text().strip()
password = self.password_edit.text().strip()
confirm_password = self.confirm_password_edit.text().strip()
api_key = self.api_key_edit.text().strip()
if not username or not password:
self.show_status("Username and password are required", error=True)
return
if len(username) < 3 or len(username) > 20:
self.show_status("Username must be 3-20 characters", error=True)
return
if password != confirm_password:
self.show_status("Passwords do not match", error=True)
return
if not self.is_strong_password(password):
self.show_status("Password must be at least 8 characters long and contain at least one number and one special symbol.", error=True)
return
self.create_btn.setEnabled(False)
self.create_btn.setText("Creating...")
success, message = self.account_manager.create_account(username, password, api_key)
if success:
self.show_status("Account created successfully!", error=False)
QTimer.singleShot(1000, self.accept)
else:
self.show_status(message, error=True)
self.create_btn.setEnabled(True)
self.create_btn.setText("Create Account")
def show_status(self, message: str, error: bool = False):
self.status_label.setText(message)
color = "#e74c3c" if error else "#27ae60"
self.status_label.setStyleSheet(f"color: {color}; font-weight: bold;")
# === AI Assistant Dialog ===
class AIAssistantDialog(QDialog):
def __init__(self, parent, api_key: str):
super().__init__(parent)
self.setWindowTitle("AI Assistant")
self.resize(700, 500)
self.setModal(True)
self.api_key = api_key
self.worker = None
self.setup_ui()
def setup_ui(self):
self.setStyleSheet("""
QDialog {
background-color: #f8f9fa;
}
QTextEdit {
border: 2px solid #e9ecef;
border-radius: 10px;
padding: 15px;
font-family: 'Segoe UI', Arial, sans-serif;
font-size: 12px;
background-color: white;
}
QTextEdit:focus {
border: 2px solid #007bff;
}
QPushButton {
background-color: #007bff;
color: white;
border: none;
border-radius: 8px;
padding: 12px 25px;
font-weight: bold;
font-size: 13px;
min-width: 120px;
}
QPushButton:hover {
background-color: #0056b3;
}
QPushButton:disabled {
background-color: #6c757d;
}
QLabel {
font-weight: bold;
color: #495057;
font-size: 14px;
}
""")
layout = QVBoxLayout()
layout.setSpacing(15)
header = QLabel("AI Assistant - Powered by OpenAI")
header.setAlignment(Qt.AlignCenter)
header.setStyleSheet("font-size: 16px; color: #007bff; margin: 10px;")
layout.addWidget(header)
layout.addWidget(QLabel("Enter your question or prompt:"))
self.prompt_edit = QTextEdit()
self.prompt_edit.setPlaceholderText("Ask me anything... For example:\n- Explain quantum computing\n- Write a Python function\n- Summarize a topic")
self.prompt_edit.setMaximumHeight(120)
layout.addWidget(self.prompt_edit)
button_layout = QHBoxLayout()
self.send_button = QPushButton("Send to AI")
self.clear_button = QPushButton("Clear")
self.close_button = QPushButton("Close")
button_layout.addWidget(self.send_button)
button_layout.addWidget(self.clear_button)
button_layout.addWidget(self.close_button)
layout.addLayout(button_layout)
self.progress_bar = QProgressBar()
self.progress_bar.setRange(0, 0)
self.progress_bar.setVisible(False)
layout.addWidget(self.progress_bar)
layout.addWidget(QLabel("AI Response:"))
self.response_edit = QTextEdit()
self.response_edit.setReadOnly(True)
self.response_edit.setPlaceholderText("AI response will appear here...")
layout.addWidget(self.response_edit)
self.setLayout(layout)
self.send_button.clicked.connect(self.call_openai)
self.clear_button.clicked.connect(self.clear_content)
self.close_button.clicked.connect(self.accept)
self.prompt_edit.setFocus()
def call_openai(self):
prompt_text = self.prompt_edit.toPlainText().strip()
if not prompt_text:
QMessageBox.warning(self, "Empty Prompt", "Please enter some text to send to the AI.")
return
if not self.api_key:
QMessageBox.warning(self, "No API Key", "Please set your OpenAI API key first.")
return
self.send_button.setEnabled(False)
self.send_button.setText("Sending...")
self.progress_bar.setVisible(True)
self.response_edit.setText("🤖 AI is thinking...")
self.worker = OpenAIWorker(self.api_key, prompt_text)
self.worker.response_ready.connect(self.handle_response)
self.worker.error_occurred.connect(self.handle_error)
self.worker.start()
@pyqtSlot(str)
def handle_response(self, response: str):
self.response_edit.setText(response)
self.reset_ui()
@pyqtSlot(str)
def handle_error(self, error: str):
self.response_edit.setText(f"❌ Error: {error}")
self.reset_ui()
def reset_ui(self):
self.send_button.setEnabled(True)
self.send_button.setText("Send to AI")
self.progress_bar.setVisible(False)
if self.worker:
self.worker.quit()
self.worker = None
def clear_content(self):
self.prompt_edit.clear()
self.response_edit.clear()
self.prompt_edit.setFocus()
def closeEvent(self, event):
if self.worker and self.worker.isRunning():
self.worker.quit()
self.worker.wait()
event.accept()
# === Search Bar Widget ===
class SearchBar(QWidget):
navigate_requested = pyqtSignal(str)
def __init__(self, parent=None):
super().__init__(parent)
self.setup_ui()
def setup_ui(self):
layout = QHBoxLayout()
layout.setContentsMargins(15, 8, 15, 8)
layout.setSpacing(10)
self.back_button = QPushButton("◀")
self.back_button.setFixedSize(35, 35)
self.back_button.setStyleSheet("""
QPushButton {
background-color: #3498db;
color: white;
border: none;
border-radius: 17px;
font-weight: bold;
font-size: 14px;
}
QPushButton:hover {
background-color: #2980b9;
}
""")
self.forward_button = QPushButton("▶")
self.forward_button.setFixedSize(35, 35)
self.forward_button.setStyleSheet(self.back_button.styleSheet())
self.refresh_button = QPushButton("↻")
self.refresh_button.setFixedSize(35, 35)
self.refresh_button.setStyleSheet(self.back_button.styleSheet())
self.url_edit = QLineEdit()
self.url_edit.setPlaceholderText("Enter URL or search term...")
self.url_edit.setStyleSheet("""
QLineEdit {
border: 2px solid #bdc3c7;
border-radius: 22px;
padding: 12px 20px;
font-size: 13px;
background-color: white;
}
QLineEdit:focus {
border: 2px solid #3498db;
}
""")
self.go_button = QPushButton("Go")
self.go_button.setStyleSheet("""
QPushButton {
background-color: #27ae60;
color: white;
border: none;
border-radius: 20px;
padding: 12px 25px;
font-weight: bold;
font-size: 13px;
min-width: 70px;
}
QPushButton:hover {
background-color: #219a52;
}
""")
layout.addWidget(self.back_button)
layout.addWidget(self.forward_button)
layout.addWidget(self.refresh_button)
layout.addWidget(self.url_edit)
layout.addWidget(self.go_button)
self.setLayout(layout)
self.url_edit.returnPressed.connect(self.navigate)
self.go_button.clicked.connect(self.navigate)
def navigate(self):
url = self.url_edit.text().strip()
if not url:
return
processed_url = self.process_url(url)
self.navigate_requested.emit(processed_url)
def process_url(self, url: str) -> str:
if not url:
return "https://duckduckgo.com"
if is_valid_url(url):
return url
if '.' in url and ' ' not in url and not url.startswith(('http://', 'https://')):
return f"https://{url}"
return f"https://duckduckgo.com/?q={url.replace(' ', '+')}"
def set_url(self, url: str):
self.url_edit.setText(url)
# === Browser Tab Widget ===
class BrowserTab(QWidget):
def __init__(self, url: str = None, parent=None):
super().__init__(parent)
self.setup_webview(url)
def setup_webview(self, url: str):
self.profile = QWebEngineProfile(self)
self.profile.setHttpUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
self.webview = QWebEngineView(self)
page = QWebEnginePage(self.profile, self.webview)
self.webview.setPage(page)
if not url or not isinstance(url, str):
url = "https://duckduckgo.com"
self.webview.load(QUrl(url))
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.webview)
self.setLayout(layout)
def navigate_to(self, url: str):
if url and isinstance(url, str):
self.webview.load(QUrl(url))
def get_url(self) -> str:
return self.webview.url().toString()
def get_title(self) -> str:
return self.webview.page().title()
# === Custom Tab Widget ===
class CustomTabWidget(QTabWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setTabsClosable(True)
self.setMovable(True)
self.setDocumentMode(True)
self.tabCloseRequested.connect(self.close_tab)
self.setStyleSheet("""
QTabWidget::pane {
border: 1px solid #bdc3c7;
background-color: white;
}
QTabBar::tab {
background-color: #ecf0f1;
border: 1px solid #bdc3c7;
padding: 10px 15px;
margin-right: 2px;
min-width: 120px;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
}
QTabBar::tab:selected {
background-color: white;
border-bottom: 1px solid white;
}
QTabBar::tab:hover {
background-color: #d5dbdb;
}
""")
def close_tab(self, index: int):
if self.count() > 1:
widget = self.widget(index)
if widget:
# Explicitly delete webview and page to ensure proper cleanup
if hasattr(widget, 'webview') and widget.webview:
widget.webview.deleteLater()
if hasattr(widget, 'page') and widget.page:
widget.page.deleteLater()
widget.deleteLater()
self.removeTab(index)
else:
# If only one tab remains, navigate to a default page instead of closing
current_tab = self.currentWidget()
if current_tab:
current_tab.navigate_to("https://duckduckgo.com")
# === Main Browser Window ===
class Sidx1ZenBrowser(QMainWindow):
def __init__(self, username: str, password: str, api_key: str):
super().__init__()
self.username = username
self.password = password # Store password for API key updates
self.api_key = api_key
self.account_manager = AccountManager()
self.setup_ui()
self.setup_style()
self.setup_shortcuts()
self.add_tab("https://sidx1.com")
def setup_ui(self):
self.setWindowTitle(f"Sidx1 Zen Browser - {self.username}")
self.resize(1400, 900)
central_widget = QWidget()
self.setCentralWidget(central_widget)
main_layout = QVBoxLayout(central_widget)
main_layout.setSpacing(0)
main_layout.setContentsMargins(0, 0, 0, 0)
self.search_bar = SearchBar()
self.search_bar.navigate_requested.connect(self.navigate_current_tab)
main_layout.addWidget(self.search_bar)
self.tabs = CustomTabWidget()
self.tabs.currentChanged.connect(self.on_tab_changed)
main_layout.addWidget(self.tabs)
self.search_bar.back_button.clicked.connect(self.go_back)
self.search_bar.forward_button.clicked.connect(self.go_forward)
self.search_bar.refresh_button.clicked.connect(self.refresh_page)
self.create_menu()
self.status_bar_widget = QStatusBar()
self.setStatusBar(self.status_bar_widget)
self.status_bar_widget.showMessage(f"Welcome, {self.username}!")
def setup_style(self):
self.setStyleSheet("""
QMainWindow {
background-color: #ecf0f1;
}
QMenuBar {
background-color: #34495e;
color: white;
border: none;
padding: 4px;
}
QMenuBar::item {
background-color: transparent;
padding: 8px 15px;
border-radius: 4px;
}
QMenuBar::item:selected {
background-color: #3498db;
}
QMenu {
background-color: #34495e;
color: white;
border: 1px solid #2c3e50;
border-radius: 8px;
}
QMenu::item {
padding: 10px 30px;
border-radius: 4px;
margin: 2px;
}
QMenu::item:selected {
background-color: #3498db;
}
QMenu::separator {
height: 1px;
background-color: #2c3e50;
margin: 5px;
}
QStatusBar {
background-color: #34495e;
color: white;
border-top: 1px solid #2c3e50;
}
""")
def setup_shortcuts(self):
new_tab_shortcut = QAction(self)
new_tab_shortcut.setShortcut(QKeySequence.New)
new_tab_shortcut.triggered.connect(lambda: self.add_tab())
self.addAction(new_tab_shortcut)
close_tab_shortcut = QAction(self)
close_tab_shortcut.setShortcut(QKeySequence.Close)
close_tab_shortcut.triggered.connect(self.close_current_tab)
self.addAction(close_tab_shortcut)
refresh_shortcut = QAction(self)
refresh_shortcut.setShortcut(QKeySequence.Refresh)
refresh_shortcut.triggered.connect(self.refresh_page)
self.addAction(refresh_shortcut)
focus_address_shortcut = QAction(self)
focus_address_shortcut.setShortcut(QKeySequence("Ctrl+L"))
focus_address_shortcut.triggered.connect(self.focus_address_bar)
self.addAction(focus_address_shortcut)
def create_menu(self):
menubar = self.menuBar()
file_menu = menubar.addMenu("File")
new_tab_action = QAction("New Tab", self)
new_tab_action.setShortcut(QKeySequence.New)
new_tab_action.triggered.connect(lambda: self.add_tab())
file_menu.addAction(new_tab_action)
close_tab_action = QAction("Close Tab", self)
close_tab_action.setShortcut(QKeySequence.Close)
close_tab_action.triggered.connect(self.close_current_tab)
file_menu.addAction(close_tab_action)
file_menu.addSeparator()
quick_menu = file_menu.addMenu("Quick Access")
sites = [
("DuckDuckGo", "https://duckduckgo.com"),
("GitHub", "https://github.com"),
("Stack Overflow", "https://stackoverflow.com"),
("Wikipedia", "https://wikipedia.org"),
]
for name, url in sites:
action = QAction(name, self)
action.triggered.connect(lambda checked, u=url: self.add_tab(u))
quick_menu.addAction(action)
file_menu.addSeparator()
exit_action = QAction("Exit", self)
exit_action.setShortcut(QKeySequence.Quit)
exit_action.triggered.connect(self.close)
file_menu.addAction(exit_action)
edit_menu = menubar.addMenu("Edit")
find_action = QAction("Find in Page", self)
find_action.setShortcut(QKeySequence.Find)
find_action.triggered.connect(self.find_in_page)
edit_menu.addAction(find_action)
view_menu = menubar.addMenu("View")
refresh_action = QAction("Refresh", self)
refresh_action.setShortcut(QKeySequence.Refresh)
refresh_action.triggered.connect(self.refresh_page)
view_menu.addAction(refresh_action)
zoom_in_action = QAction("Zoom In", self)
zoom_in_action.setShortcut(QKeySequence.ZoomIn)
zoom_in_action.triggered.connect(self.zoom_in)
view_menu.addAction(zoom_in_action)
zoom_out_action = QAction("Zoom Out", self)
zoom_out_action.setShortcut(QKeySequence.ZoomOut)
zoom_out_action.triggered.connect(self.zoom_out)
view_menu.addAction(zoom_out_action)
ai_menu = menubar.addMenu("AI")
if OPENAI_AVAILABLE:
open_ai_action = QAction("Open AI Assistant", self)
open_ai_action.setShortcut(QKeySequence("Ctrl+Shift+A"))
open_ai_action.triggered.connect(self.open_ai_assistant)
ai_menu.addAction(open_ai_action)
else:
disabled_action = QAction("AI Assistant (OpenAI not installed)", self)
disabled_action.setEnabled(False)
ai_menu.addAction(disabled_action)
set_api_key_action = QAction("Set API Key", self)
set_api_key_action.triggered.connect(self.set_api_key)
ai_menu.addAction(set_api_key_action)
help_menu = menubar.addMenu("Help")
about_action = QAction("About", self)
about_action.triggered.connect(self.show_about)
help_menu.addAction(about_action)
shortcuts_action = QAction("Keyboard Shortcuts", self)
shortcuts_action.triggered.connect(self.show_shortcuts)
help_menu.addAction(shortcuts_action)
def add_tab(self, url: str = None):
if url is None:
url = "https://duckduckgo.com"
try:
tab = BrowserTab(url, self)
tab_title = self.get_tab_title(url)
tab_index = self.tabs.addTab(tab, tab_title)
self.tabs.setCurrentIndex(tab_index)
self.search_bar.set_url(url)
tab.webview.titleChanged.connect(lambda title: self.update_tab_title(tab, title))
tab.webview.urlChanged.connect(lambda url: self.on_url_changed(url))
self.status_bar_widget.showMessage(f"New tab opened: {url}")
except Exception as e:
self.status_bar_widget.showMessage(f"Error creating tab: {str(e)}")
def get_tab_title(self, url: str) -> str:
if not url:
return "New Tab"
if len(url) > 25:
return url[:25] + "..."
return url
def update_tab_title(self, tab_widget, title: str):
if not title:
return
tab_index = self.tabs.indexOf(tab_widget)
if tab_index >= 0:
if len(title) > 20:
title = title[:20] + "..."
self.tabs.setTabText(tab_index, title)
def close_current_tab(self):
current_index = self.tabs.currentIndex()
self.tabs.close_tab(current_index)
def navigate_current_tab(self, url: str):
current_tab = self.tabs.currentWidget()
if current_tab:
current_tab.navigate_to(url)
self.status_bar_widget.showMessage(f"Navigating to: {url}")
def on_tab_changed(self, index: int):
current_tab = self.tabs.currentWidget()
if current_tab:
current_url = current_tab.get_url()
self.search_bar.set_url(current_url)
self.status_bar_widget.showMessage(f"Active tab: {current_url}")
def on_url_changed(self, url: QUrl):
current_tab = self.tabs.currentWidget()
if current_tab:
self.search_bar.set_url(url.toString())
def go_back(self):
current_tab = self.tabs.currentWidget()
if current_tab:
current_tab.webview.back()
def go_forward(self):
current_tab = self.tabs.currentWidget()
if current_tab:
current_tab.webview.forward()
def refresh_page(self):
current_tab = self.tabs.currentWidget()
if current_tab:
current_tab.webview.reload()
self.status_bar_widget.showMessage("Page refreshed")
def zoom_in(self):
current_tab = self.tabs.currentWidget()
if current_tab:
current_tab.webview.setZoomFactor(current_tab.webview.zoomFactor() + 0.1)
def zoom_out(self):
current_tab = self.tabs.currentWidget()
if current_tab:
current_tab.webview.setZoomFactor(current_tab.webview.zoomFactor() - 0.1)
def focus_address_bar(self):
self.search_bar.url_edit.setFocus()
self.search_bar.url_edit.selectAll()
def find_in_page(self):
current_tab = self.tabs.currentWidget()
if current_tab:
text, ok = QInputDialog.getText(self, "Find in Page", "Enter text to find:")
if ok and text:
current_tab.webview.findText(text)
def open_ai_assistant(self):
if not OPENAI_AVAILABLE:
QMessageBox.warning(self, "OpenAI Not Available",
"OpenAI library is not installed. Please install it with:\npip install openai")
return
if not self.api_key:
QMessageBox.information(self, "API Key Required",
"Please set your OpenAI API key in the AI menu first.")
return
try:
dialog = AIAssistantDialog(self, self.api_key)
dialog.exec_()
except Exception as e:
QMessageBox.critical(self, "Error", f"Failed to open AI assistant: {str(e)}")
def set_api_key(self):
key, ok = QInputDialog.getText(
self, "Set OpenAI API Key",
"Enter your OpenAI API Key:",
text=self.api_key or "",
mode=QLineEdit.Password
)
if ok:
self.api_key = key.strip()
if self.account_manager.update_api_key(self.username, self.password, self.api_key):
QMessageBox.information(self, "Success", "API key updated successfully!")
self.status_bar_widget.showMessage("API key updated")
else:
QMessageBox.warning(self, "Error", "Failed to save API key")
def show_about(self):
about_text = """
<h2>Sidx1 Zen Browser</h2>
<p><b>Version:</b> 1.1.0</p>
<p><b>Author:</b> Sidx1</p>
<p><b>Description:</b> A modern, secure web browser built with PyQt5.</p>
<p><b>Features:</b></p>
<ul>
<li>Tabbed browsing</li>
<li>Secure user account management with encrypted API keys</li>
<li>AI Assistant integration</li>
<li>Modern UI design</li>
<li>Keyboard shortcuts</li>
</ul>
<p><b>Built with:</b> Python, PyQt5, QtWebEngine</p>
"""
QMessageBox.about(self, "About Sidx1 Zen Browser", about_text)
def show_shortcuts(self):
shortcuts_text = """
<h3>Keyboard Shortcuts</h3>
<table>
<tr><td><b>Ctrl+T</b></td><td>New Tab</td></tr>
<tr><td><b>Ctrl+W</b></td><td>Close Tab</td></tr>
<tr><td><b>Ctrl+R / F5</b></td><td>Refresh Page</td></tr>
<tr><td><b>Ctrl+L</b></td><td>Focus Address Bar</td></tr>
<tr><td><b>Ctrl+F</b></td><td>Find in Page</td></tr>
<tr><td><b>Ctrl+Shift+A</b></td><td>Open AI Assistant</td></tr>
<tr><td><b>Ctrl+Plus</b></td><td>Zoom In</td></tr>
<tr><td><b>Ctrl+Minus</b></td><td>Zoom Out</td></tr>
<tr><td><b>Ctrl+Q</b></td><td>Exit Application</td></tr>
</table>
"""
QMessageBox.information(self, "Keyboard Shortcuts", shortcuts_text)
def closeEvent(self, event):
try:
# Iterate through all tabs and explicitly delete webview and page objects
for i in range(self.tabs.count()):
tab = self.tabs.widget(i)
if tab:
if hasattr(tab, 'webview') and tab.webview:
tab.webview.deleteLater()
if hasattr(tab, 'page') and tab.page:
tab.page.deleteLater()
tab.deleteLater()
self.status_bar_widget.showMessage("Browser closed")
event.accept()
except Exception as e:
print(f"Error during close: {e}")
event.accept()
# === Main Application ===
def main():
try:
app = QApplication(sys.argv)
app.setApplicationName("Sidx1 Zen Browser")
app.setApplicationVersion("1.1.0")
app.setOrganizationName("Sidx1")
app.setStyle("Fusion")
login_dialog = LoginDialog()
if login_dialog.exec_() == QDialog.Accepted:
window = Sidx1ZenBrowser(login_dialog.username, login_dialog.password, login_dialog.api_key)
window.show()
sys.exit(app.exec_())
else:
sys.exit(0)
except Exception as e:
print(f"Fatal error: {e}")
traceback.print_exc()
try:
# Ensure we can show a message box even if the app failed to initialize
error_app = QApplication.instance() or QApplication(sys.argv)
QMessageBox.critical(None, "Fatal Error",
f"Application failed to start:\n{str(e)}")
except Exception as final_e:
print(f"Could not show fatal error message box: {final_e}")
sys.exit(1)
if __name__ == "__main__":
main()
and press enter it will take some time to start up well I mean it is python an interpreted language that reads everything line by line then it converts to c or c++ then the computer runs it but after ran boom it works great.
also here is my very encrypted photo encryptor it is not as encrypted but rquires a 300+ char password and it is very secure to download the .py file use https://drive.google.com/file/d/1aIRJ5_M4_HPHlA4v8NWjnxqwf1o7WdCi/view?usp=sharing also if drive says this file is malicious or something google thinks it violates their terms of server and that it is a virus but its kind of the opposite it encrypts stuff instead of harms your computer and decrypts yoru files to steak yoru data or if you want the python code to run it on other operating systems use the python code provided below also, make sure use install all the dependencies so if you want to do that use also note that Pillow is the only external dependency. The other modules
(tkinter, hashlib, base64, os) are all included in the Python standard library, so they don't need to be
installed separately. so to install pillow ( if you dont have it already ) use pip install pillow
here is the code:
import tkinter as tk
from tkinter import filedialog, messagebox
from PIL import Image
import hashlib
import base64
import os
# ==== ENCRYPTION/DECRYPTION ====
def xor_bytes(data: bytes, key: bytes) -> bytes:
return bytes([b ^ key[i % len(key)] for i, b in enumerate(data)])
def encrypt_image(input_path, password, output_path):
try:
img = Image.open(input_path).convert('RGB')
raw_data = img.tobytes()
size = img.size
key = hashlib.sha512(password.encode()).digest()
encrypted = xor_bytes(raw_data, key)
encoded = base64.b64encode(encrypted).decode()
header = f"{size[0]},{size[1]}\n"
with open(output_path, 'w') as f:
f.write(header + encoded)
messagebox.showinfo("Success", f"Image encrypted and saved to:\n{output_path}")
except Exception as e:
messagebox.showerror("Error", str(e))
def decrypt_image(input_path, password, output_path):
try:
with open(input_path, 'r') as f:
lines = f.readlines()
width, height = map(int, lines[0].strip().split(','))
encrypted_data = base64.b64decode(''.join(lines[1:]))
key = hashlib.sha512(password.encode()).digest()
decrypted = xor_bytes(encrypted_data, key)
img = Image.frombytes('RGB', (width, height), decrypted)
img.save(output_path)
messagebox.showinfo("Success", f"Image decrypted and saved to:\n{output_path}")
except Exception as e:
messagebox.showerror("Error", f"Decryption failed:\n{e}")
# ==== GUI ====
def choose_encrypt_file():
filepath = filedialog.askopenfilename(filetypes=[("PNG files", "*.png")])
if not filepath:
return
password = password_entry.get()
if not password:
messagebox.showerror("Error", "Enter a password to encrypt.")
return
outpath = filedialog.asksaveasfilename(defaultextension=".sidx1binarypic", filetypes=[("Sidx1 Binary Pic", "*.sidx1binarypic")])
if outpath:
encrypt_image(filepath, password, outpath)
def choose_decrypt_file():
filepath = filedialog.askopenfilename(filetypes=[("Sidx1 Binary Pic", "*.sidx1binarypic")])
if not filepath:
return
password = password_entry.get()
if not password:
messagebox.showerror("Error", "Enter a password to decrypt.")
return
outpath = filedialog.asksaveasfilename(defaultextension=".png", filetypes=[("PNG files", "*.png")])
if outpath:
decrypt_image(filepath, password, outpath)
root = tk.Tk()
root.title("Sidx1 Binary Pic Encryptor")
root.geometry("400x220")
root.resizable(False, False)
tk.Label(root, text="Password:", font=("Segoe UI", 10)).pack(pady=5)
password_entry = tk.Entry(root, show="*", width=50)
password_entry.pack(pady=5)
encrypt_btn = tk.Button(root, text="Encrypt PNG → .sidx1binarypic", command=choose_encrypt_file, width=40)
encrypt_btn.pack(pady=10)
decrypt_btn = tk.Button(root, text="Decrypt .sidx1binarypic → PNG", command=choose_decrypt_file, width=40)
decrypt_btn.pack(pady=10)
tk.Label(root, text="By sidx1 | ⚠️ Don't forget your password!", fg="gray").pack(pady=5)
root.mainloop()