diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/PIRC.iml b/.idea/PIRC.iml new file mode 100644 index 0000000..bec9418 --- /dev/null +++ b/.idea/PIRC.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..462ce8b --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..013ad0b --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/__pycache__/pirc_client.cpython-313.pyc b/__pycache__/pirc_client.cpython-313.pyc new file mode 100644 index 0000000..af26360 Binary files /dev/null and b/__pycache__/pirc_client.cpython-313.pyc differ diff --git a/__pycache__/pirc_setup_wizard.cpython-313.pyc b/__pycache__/pirc_setup_wizard.cpython-313.pyc new file mode 100644 index 0000000..fc83786 Binary files /dev/null and b/__pycache__/pirc_setup_wizard.cpython-313.pyc differ diff --git a/main.py b/main.py new file mode 100644 index 0000000..3072cd4 --- /dev/null +++ b/main.py @@ -0,0 +1,28 @@ +# main.py +import sys +import json +import os +from PySide6.QtWidgets import QApplication +from pirc_setup_wizard import PIRCSetupWizard +from pirc_client import PIRCClient + +CONFIG_FILE = "pirc_config.json" + +def main(): + app = QApplication(sys.argv) + + # Check if config exists + if not os.path.exists(CONFIG_FILE): + wizard = PIRCSetupWizard() + if wizard.exec() != 0: + pass # Wizard handled saving config + else: + print("Setup cancelled.") + sys.exit(0) + + client = PIRCClient() + client.show() + sys.exit(app.exec()) + +if __name__ == "__main__": + main() diff --git a/pirc_client.py b/pirc_client.py new file mode 100644 index 0000000..746ffd1 --- /dev/null +++ b/pirc_client.py @@ -0,0 +1,350 @@ +import sys +import socket +import threading +import json +import os +from datetime import datetime +from PySide6.QtWidgets import ( + QApplication, QWidget, QVBoxLayout, QHBoxLayout, QTextEdit, + QLineEdit, QPushButton, QLabel, QListWidget, QListWidgetItem, + QMessageBox, QDialog, QDialogButtonBox, QFormLayout, QLineEdit as QLE +) +from PySide6.QtCore import Qt, Signal, QObject +from PySide6.QtGui import QIcon + +CONFIG_FILE = "pirc_config.json" + + +class IRCWorker(QObject): + message_received = Signal(str) + system_message = Signal(str) + user_list_update = Signal(list) + disconnected = Signal() + + def __init__(self, server, port, nick, channels): + super().__init__() + self.server = server + self.port = port + self.nick = nick + self.channels = channels + self.running = True + self.sock = None + self.current_users = [] + + def start(self): + try: + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock.connect((self.server, self.port)) + self.sock.send(f"NICK {self.nick}\r\n".encode("utf-8")) + self.sock.send(f"USER {self.nick} 0 * :PIRC User\r\n".encode("utf-8")) + + for channel in self.channels: + self.sock.send(f"JOIN {channel}\r\n".encode("utf-8")) + self.system_message.emit(f"Joined {channel}") + + threading.Thread(target=self.listen, daemon=True).start() + except Exception as e: + self.system_message.emit(f"Connection failed: {e}") + self.disconnected.emit() + + def listen(self): + try: + while self.running: + data = self.sock.recv(4096).decode("utf-8", errors="ignore") + if not data: + break + + for line in data.split("\r\n"): + if not line: + continue + + # PING/PONG + if line.startswith("PING"): + self.sock.send(f"PONG {line.split()[1]}\r\n".encode("utf-8")) + continue + + # Ignore raw handshake notices + if line.startswith(":") and "NOTICE * :***" in line: + continue + + parts = line.split() + if len(parts) > 1 and parts[1].isdigit(): + code = int(parts[1]) + if code == 353: # NAMES list + name_parts = line.split(":", 2) + if len(name_parts) < 3: + continue + users_chunk = name_parts[2].split() + self.current_users.extend(users_chunk) + elif code == 366: # End of NAMES list + self.user_list_update.emit(self.current_users) + self.current_users = [] + elif code in [0o01, 332, 333]: + self.system_message.emit(line) + continue + # PRIVMSG + if "PRIVMSG" in line: + msg_parts = line.split(":", 2) + if len(msg_parts) >= 3: + prefix = msg_parts[1].split("!")[0] + message = msg_parts[2] + self.message_received.emit(f"<{prefix}> {message}") + continue + + # JOIN + if "JOIN" in line: + user = line.split("!")[0].replace(":", "") + chan = line.split("JOIN")[1].strip() + if user not in self.current_users: + self.current_users.append(user) + self.user_list_update.emit(self.current_users) + self.system_message.emit(f"{user} joined {chan}") + continue + + # PART/QUIT + if "PART" in line or "QUIT" in line: + user = line.split("!")[0].replace(":", "") + if user in self.current_users: + self.current_users.remove(user) + self.user_list_update.emit(self.current_users) + self.system_message.emit(f"{user} left") + continue + + # Other system messages + self.system_message.emit(line) + except Exception as e: + self.system_message.emit(f"Error: {e}") + finally: + self.disconnected.emit() + + def send_message(self, channel, message): + try: + self.sock.send(f"PRIVMSG {channel} :{message}\r\n".encode("utf-8")) + except Exception as e: + self.system_message.emit(f"Failed to send message: {e}") + + def stop(self): + self.running = False + try: + self.sock.send(b"QUIT :Goodbye!\r\n") + self.sock.close() + except: + pass + + +class AddServerDialog(QDialog): + def __init__(self): + super().__init__() + self.setWindowTitle("Add Server") + self.setFixedSize(300, 200) + layout = QFormLayout() + self.server_input = QLE() + self.port_input = QLE() + self.port_input.setText("6667") + self.channels_input = QLE() + layout.addRow("Server:", self.server_input) + layout.addRow("Port:", self.port_input) + layout.addRow("Channels (comma-separated):", self.channels_input) + buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) + buttons.accepted.connect(self.accept) + buttons.rejected.connect(self.reject) + layout.addWidget(buttons) + self.setLayout(layout) + + def get_data(self): + return ( + self.server_input.text().strip(), + int(self.port_input.text().strip()), + [c.strip() for c in self.channels_input.text().split(",") if c.strip()] + ) + + +class PIRCClient(QWidget): + def __init__(self): + super().__init__() + self.setWindowTitle("PIRC — Python IRC Client") + self.setGeometry(100, 100, 1000, 600) + self.worker = None + + self.load_config() + self.init_ui() + + if self.favorites: + first = self.favorites[0] + self.connect_to_server(first["server"], first.get("port", 6667), first["channels"]) + else: + self.system_message("No favorite servers found — click the + button to connect.") + + def load_config(self): + if os.path.exists(CONFIG_FILE): + with open(CONFIG_FILE, "r") as f: + cfg = json.load(f) + self.nick = cfg.get("username", "PIRCUser") + self.favorites = cfg.get("favorites", []) + else: + self.nick = "PIRCUser" + self.favorites = [] + + def save_config(self): + cfg = { + "username": self.nick, + "favorites": self.favorites + } + with open(CONFIG_FILE, "w") as f: + json.dump(cfg, f, indent=4) + + def init_ui(self): + layout = QHBoxLayout() + + # Server sidebar + sidebar_layout = QVBoxLayout() + self.server_list = QListWidget() + for fav in self.favorites: + item = QListWidgetItem(fav["server"]) + self.server_list.addItem(item) + self.server_list.itemClicked.connect(self.server_selected) + sidebar_layout.addWidget(self.server_list) + + # Add server button with + icon + self.add_server_button = QPushButton() + self.add_server_button.setIcon(QIcon("add.png")) + self.add_server_button.setText("") # icon only + self.add_server_button.clicked.connect(self.add_server) + self.disconnect_button = QPushButton("Disconnect") + self.disconnect_button.clicked.connect(self.disconnect_server) + sidebar_layout.addWidget(self.add_server_button) + sidebar_layout.addWidget(self.disconnect_button) + layout.addLayout(sidebar_layout, 2) + + # Chat + user list + chat_layout = QVBoxLayout() + self.info_label = QLabel("Disconnected") + self.info_label.setStyleSheet("font-weight: bold; padding: 4px;") + chat_layout.addWidget(self.info_label) + + chat_area_layout = QHBoxLayout() + self.chat_display = QTextEdit() + self.chat_display.setReadOnly(True) + chat_area_layout.addWidget(self.chat_display, 7) + + # User list sidebar + self.user_list = QListWidget() + chat_area_layout.addWidget(self.user_list, 3) + + chat_layout.addLayout(chat_area_layout, 8) + + # Message input + input_layout = QHBoxLayout() + self.message_input = QLineEdit() + self.message_input.returnPressed.connect(self.send_message) + self.send_button = QPushButton("Send") + self.send_button.clicked.connect(self.send_message) + input_layout.addWidget(self.message_input) + input_layout.addWidget(self.send_button) + chat_layout.addLayout(input_layout) + + layout.addLayout(chat_layout, 8) + self.setLayout(layout) + + def connect_to_server(self, server, port, channels): + if self.worker: + self.disconnect_server() + self.worker = IRCWorker(server, port, self.nick, channels) + self.worker.message_received.connect(self.display_message) + self.worker.system_message.connect(self.system_message) + self.worker.user_list_update.connect(self.update_user_list) + self.worker.disconnected.connect(self.handle_disconnect) + self.worker.start() + self.info_label.setText(f"Connected to {server} as {self.nick}") + self.system_message(f"Connected to {server}") + + def disconnect_server(self): + if self.worker: + self.worker.stop() + self.worker = None + self.system_message("Disconnected from server.") + self.info_label.setText("Disconnected") + self.user_list.clear() + + def handle_disconnect(self): + self.worker = None + self.system_message("Connection closed by server.") + self.info_label.setText("Disconnected") + self.user_list.clear() + + def server_selected(self, item): + selected_server = item.text() + fav = next((f for f in self.favorites if f["server"] == selected_server), None) + if fav: + self.connect_to_server(fav["server"], fav.get("port", 6667), fav["channels"]) + + def add_server(self): + dialog = AddServerDialog() + if dialog.exec() == QDialog.Accepted: + server, port, channels = dialog.get_data() + if not server: + QMessageBox.warning(self, "Error", "Server cannot be empty.") + return + new_server = {"server": server, "port": port, "channels": channels} + self.favorites.append(new_server) + self.save_config() + self.server_list.addItem(server) + self.connect_to_server(server, port, channels) + + def display_message(self, msg): + timestamp = datetime.now().strftime("[%H:%M:%S]") + self.chat_display.append(f"{timestamp} {msg}") + + def system_message(self, msg): + timestamp = datetime.now().strftime("[%H:%M:%S]") + self.chat_display.append( + f'
{timestamp} [System] {msg}
' + ) + + def update_user_list(self, users): + self.user_list.clear() + admins = [] + normal_users = [] + + for user in users: + if user.startswith("@"): + admins.append(user[1:]) # remove @ for display + elif user.startswith("+"): + normal_users.append(user[1:]) # optional remove + + else: + normal_users.append(user) + + admins.sort() + normal_users.sort() + ordered_users = admins + normal_users + + for user in ordered_users: + item = QListWidgetItem(user) + if user in admins: + item.setIcon(QIcon("admin.png")) + else: + item.setIcon(QIcon("user.png")) + self.user_list.addItem(item) + + def send_message(self): + text = self.message_input.text().strip() + if not text: + return + if self.worker and self.worker.channels: + self.worker.send_message(self.worker.channels[0], text) + self.display_message(f"<{self.nick}> {text}") + else: + self.system_message("Not connected to a server.") + self.message_input.clear() + + def closeEvent(self, event): + if self.worker: + self.worker.stop() + super().closeEvent(event) + + +if __name__ == "__main__": + app = QApplication(sys.argv) + client = PIRCClient() + client.show() + sys.exit(app.exec()) diff --git a/pirc_config.json b/pirc_config.json new file mode 100644 index 0000000..9b9c7f6 --- /dev/null +++ b/pirc_config.json @@ -0,0 +1,10 @@ +{ + "username": "OptimiDev", + "favorites": [ + { + "server": "irc.libera.chat", + "port": 6667, + "channels": ["#python"] + } + ] +} diff --git a/pirc_setup_wizard.py b/pirc_setup_wizard.py new file mode 100644 index 0000000..db7433a --- /dev/null +++ b/pirc_setup_wizard.py @@ -0,0 +1,138 @@ +# pirC_setup_wizard.py +import sys +import json +from PySide6.QtWidgets import ( + QApplication, QDialog, QVBoxLayout, QLabel, QLineEdit, QPushButton, + QListWidget, QHBoxLayout, QStackedLayout, QWidget, QDialogButtonBox +) + +CONFIG_FILE = "pirc_config.json" + +class PIRCSetupWizard(QDialog): + def __init__(self): + super().__init__() + self.setWindowTitle("PIRC Setup Wizard") + self.setGeometry(200, 200, 500, 400) + + self.nicknames = [] + self.favorites = [] + + # Multi-step layout + self.layout = QVBoxLayout() + self.stack = QStackedLayout() + self.layout.addLayout(self.stack) + self.setLayout(self.layout) + + # Step 0: Welcome + self.step_welcome = QWidget() + w_layout = QVBoxLayout() + w_layout.addWidget(QLabel( + "Welcome to PIRC!\n\nThis wizard will guide you through the setup:\n" + "1. Add your usernames (multiple allowed)\n" + "2. Add favorite servers/channels (optional)\n" + "3. Open the chat window\n" + )) + self.next0 = QPushButton("Start") + self.next0.clicked.connect(lambda: self.stack.setCurrentIndex(1)) + w_layout.addWidget(self.next0) + self.step_welcome.setLayout(w_layout) + self.stack.addWidget(self.step_welcome) + + # Step 1: Usernames + self.step_users = QWidget() + u_layout = QVBoxLayout() + u_layout.addWidget(QLabel("Step 1: Add your usernames")) + self.nick_input = QLineEdit() + self.nick_input.setPlaceholderText("Enter a username") + u_layout.addWidget(self.nick_input) + self.nick_list = QListWidget() + u_layout.addWidget(self.nick_list) + h_btn_layout = QHBoxLayout() + self.add_nick_btn = QPushButton("Add") + self.add_nick_btn.clicked.connect(self.add_nickname) + self.remove_nick_btn = QPushButton("Remove Selected") + self.remove_nick_btn.clicked.connect(self.remove_nickname) + h_btn_layout.addWidget(self.add_nick_btn) + h_btn_layout.addWidget(self.remove_nick_btn) + u_layout.addLayout(h_btn_layout) + self.next1 = QPushButton("Next") + self.next1.clicked.connect(lambda: self.stack.setCurrentIndex(2)) + u_layout.addWidget(self.next1) + self.step_users.setLayout(u_layout) + self.stack.addWidget(self.step_users) + + # Step 2: Favorites + self.step_fav = QWidget() + f_layout = QVBoxLayout() + f_layout.addWidget(QLabel("Step 2: Add favorite servers/channels (optional)")) + self.server_input = QLineEdit() + self.server_input.setPlaceholderText("Server address (e.g., irc.libera.chat)") + self.channel_input = QLineEdit() + self.channel_input.setPlaceholderText("Channel (e.g., #python)") + f_layout.addWidget(self.server_input) + f_layout.addWidget(self.channel_input) + self.fav_list = QListWidget() + f_layout.addWidget(self.fav_list) + h_fav_btn_layout = QHBoxLayout() + self.add_fav_btn = QPushButton("Add") + self.add_fav_btn.clicked.connect(self.add_favorite) + self.remove_fav_btn = QPushButton("Remove Selected") + self.remove_fav_btn.clicked.connect(self.remove_favorite) + h_fav_btn_layout.addWidget(self.add_fav_btn) + h_fav_btn_layout.addWidget(self.remove_fav_btn) + f_layout.addLayout(h_fav_btn_layout) + self.next2 = QPushButton("Finish") + self.next2.clicked.connect(self.finish_setup) + f_layout.addWidget(self.next2) + self.step_fav.setLayout(f_layout) + self.stack.addWidget(self.step_fav) + + # --- Nicknames --- + def add_nickname(self): + nick = self.nick_input.text().strip() + if nick: + self.nicknames.append(nick) + self.nick_list.addItem(nick) + self.nick_input.clear() + + def remove_nickname(self): + selected = self.nick_list.currentRow() + if selected >= 0: + self.nick_list.takeItem(selected) + self.nicknames.pop(selected) + + # --- Favorites --- + def add_favorite(self): + server = self.server_input.text().strip() + channel = self.channel_input.text().strip() + if server and channel: + self.favorites.append({"server": server, "channel": channel}) + self.fav_list.addItem(f"{server} {channel}") + self.server_input.clear() + self.channel_input.clear() + + def remove_favorite(self): + selected = self.fav_list.currentRow() + if selected >= 0: + self.fav_list.takeItem(selected) + self.favorites.pop(selected) + + # --- Finish --- + def finish_setup(self): + config = { + "nicknames": self.nicknames, + "favorites": self.favorites + } + with open(CONFIG_FILE, "w") as f: + json.dump(config, f, indent=4) + self.accept() # Close wizard + +# --- Test --- +if __name__ == "__main__": + app = QApplication(sys.argv) + wizard = PIRCSetupWizard() + if wizard.exec() == QDialog.Accepted: + print("Setup completed!") + with open(CONFIG_FILE, "r") as f: + print(json.load(f)) + sys.exit(0) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..2fc14bb --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,9 @@ +[project] +name = "pirc" +version = "0.1.0" +description = "Add your description here" +requires-python = ">=3.13" +dependencies = [ + "pyqt6>=6.9.1", + "pyside6>=6.10.0", +] diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..76db6b4 --- /dev/null +++ b/uv.lock @@ -0,0 +1,126 @@ +version = 1 +revision = 3 +requires-python = ">=3.13" + +[[package]] +name = "pirc" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "pyqt6" }, + { name = "pyside6" }, +] + +[package.metadata] +requires-dist = [ + { name = "pyqt6", specifier = ">=6.9.1" }, + { name = "pyside6", specifier = ">=6.10.0" }, +] + +[[package]] +name = "pyqt6" +version = "6.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyqt6-qt6" }, + { name = "pyqt6-sip" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/32/1b/567f46eb43ca961efd38d7a0b73efb70d7342854f075fd919179fdb2a571/pyqt6-6.9.1.tar.gz", hash = "sha256:50642be03fb40f1c2111a09a1f5a0f79813e039c15e78267e6faaf8a96c1c3a6", size = 1067230, upload-time = "2025-06-06T08:49:30.307Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/c4/fc2a69cf3df09b213185ef5a677c3940cd20e7855d29e40061a685b9c6ee/pyqt6-6.9.1-cp39-abi3-macosx_10_14_universal2.whl", hash = "sha256:33c23d28f6608747ecc8bfd04c8795f61631af9db4fb1e6c2a7523ec4cc916d9", size = 59770566, upload-time = "2025-06-06T08:48:20.331Z" }, + { url = "https://files.pythonhosted.org/packages/d5/78/92f3c46440a83ebe22ae614bd6792e7b052bcb58ff128f677f5662015184/pyqt6-6.9.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:37884df27f774e2e1c0c96fa41e817a222329b80ffc6241725b0dc8c110acb35", size = 37804959, upload-time = "2025-06-06T08:48:39.587Z" }, + { url = "https://files.pythonhosted.org/packages/5a/5e/e77fa2761d809cd08d724f44af01a4b6ceb0ff9648e43173187b0e4fac4e/pyqt6-6.9.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:055870b703c1a49ca621f8a89e2ec4d848e6c739d39367eb9687af3b056d9aa3", size = 40414608, upload-time = "2025-06-06T08:49:00.26Z" }, + { url = "https://files.pythonhosted.org/packages/c4/09/69cf80456b6a985e06dd24ed0c2d3451e43567bf2807a5f3a86ef7a74a2e/pyqt6-6.9.1-cp39-abi3-win_amd64.whl", hash = "sha256:15b95bd273bb6288b070ed7a9503d5ff377aa4882dd6d175f07cad28cdb21da0", size = 25717996, upload-time = "2025-06-06T08:49:13.208Z" }, + { url = "https://files.pythonhosted.org/packages/52/b3/0839d8fd18b86362a4de384740f2f6b6885b5d06fda7720f8a335425e316/pyqt6-6.9.1-cp39-abi3-win_arm64.whl", hash = "sha256:08792c72d130a02e3248a120f0b9bbb4bf4319095f92865bc5b365b00518f53d", size = 25212132, upload-time = "2025-06-06T08:49:27.41Z" }, +] + +[[package]] +name = "pyqt6-qt6" +version = "6.9.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/6f/fe2cd9cb2201c685be2f50c8c915df97848cac3dca4bad44bc3aed56fc63/pyqt6_qt6-6.9.2-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:183b62be49216da80c7df1931d74885610a88f74812489d29610d13b7c215a1c", size = 66568266, upload-time = "2025-09-01T11:43:31.339Z" }, + { url = "https://files.pythonhosted.org/packages/db/1d/47dc51b4383b350f4ff6b1db461b01eba580030683ffa65475b4fdd9b80d/pyqt6_qt6-6.9.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7897fb74ee21bdc87b5ccf84e94f4a551377e792fd180a9211c17eb41c3338a3", size = 60859706, upload-time = "2025-09-01T11:43:36.624Z" }, + { url = "https://files.pythonhosted.org/packages/a8/07/21f7dc188e35b46631707f3b40ace5643a0e03a8e1e446854826d08a04ae/pyqt6_qt6-6.9.2-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:9abfc0ee4a8293a6442128ae3f87f68e82e2a949d7b9caabd98c86ba5679ab48", size = 82322871, upload-time = "2025-09-01T11:43:41.685Z" }, + { url = "https://files.pythonhosted.org/packages/0c/c0/da658e735817feaa35ddfddb4c5d699291e8b8e3138e69ad7ae1a38a7db8/pyqt6_qt6-6.9.2-py3-none-manylinux_2_39_aarch64.whl", hash = "sha256:940aac6462532578e8ddefe0494cd17e33a85e0f3cfb21c612f56ab9ad7bc871", size = 80826693, upload-time = "2025-09-01T11:43:46.823Z" }, + { url = "https://files.pythonhosted.org/packages/63/3a/d811ed1aa579b93ab56188d1371b05eacb4188599d83e72b761263a10f92/pyqt6_qt6-6.9.2-py3-none-win_amd64.whl", hash = "sha256:f9289768039bef4a63e5949b7f8cfbbddc3b6d24bd58c21ba0f2921bed8d1c08", size = 74147171, upload-time = "2025-09-01T11:43:53.468Z" }, + { url = "https://files.pythonhosted.org/packages/57/59/7db6c5ddcb60ef3ecca2040274a30e8bc35b569c49e25e1cf2ef9f159426/pyqt6_qt6-6.9.2-py3-none-win_arm64.whl", hash = "sha256:8f82944ef68c8f8c78aa8eca4832c7bc05116c6de00a3bad8af5a0d63d1caafb", size = 54534019, upload-time = "2025-09-01T11:43:58.763Z" }, +] + +[[package]] +name = "pyqt6-sip" +version = "13.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/4a/96daf6c2e4f689faae9bd8cebb52754e76522c58a6af9b5ec86a2e8ec8b4/pyqt6_sip-13.10.2.tar.gz", hash = "sha256:464ad156bf526500ce6bd05cac7a82280af6309974d816739b4a9a627156fafe", size = 92548, upload-time = "2025-05-23T12:26:49.901Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/1e/979ea64c98ca26979d8ce11e9a36579e17d22a71f51d7366d6eec3c82c13/pyqt6_sip-13.10.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8b5d06a0eac36038fa8734657d99b5fe92263ae7a0cd0a67be6acfe220a063e1", size = 112227, upload-time = "2025-05-23T12:26:38.758Z" }, + { url = "https://files.pythonhosted.org/packages/d9/21/84c230048e3bfef4a9209d16e56dcd2ae10590d03a31556ae8b5f1dcc724/pyqt6_sip-13.10.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad376a6078da37b049fdf9d6637d71b52727e65c4496a80b753ddc8d27526aca", size = 322920, upload-time = "2025-05-23T12:26:39.856Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/c6a28a142f14e735088534cc92951c3f48cccd77cdd4f3b10d7996be420f/pyqt6_sip-13.10.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3dde8024d055f496eba7d44061c5a1ba4eb72fc95e5a9d7a0dbc908317e0888b", size = 303833, upload-time = "2025-05-23T12:26:41.075Z" }, + { url = "https://files.pythonhosted.org/packages/89/63/e5adf350c1c3123d4865c013f164c5265512fa79f09ad464fb2fdf9f9e61/pyqt6_sip-13.10.2-cp313-cp313-win_amd64.whl", hash = "sha256:0b097eb58b4df936c4a2a88a2f367c8bb5c20ff049a45a7917ad75d698e3b277", size = 53527, upload-time = "2025-05-23T12:26:42.625Z" }, + { url = "https://files.pythonhosted.org/packages/58/74/2df4195306d050fbf4963fb5636108a66e5afa6dc05fd9e81e51ec96c384/pyqt6_sip-13.10.2-cp313-cp313-win_arm64.whl", hash = "sha256:cc6a1dfdf324efaac6e7b890a608385205e652845c62130de919fd73a6326244", size = 45373, upload-time = "2025-05-23T12:26:43.536Z" }, + { url = "https://files.pythonhosted.org/packages/23/57/74b4eb7a51b9133958daa8409b55de95e44feb694d4e2e3eba81a070ca20/pyqt6_sip-13.10.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8a76a06a8e5c5b1f17a3f6f3c834ca324877e07b960b18b8b9bbfd9c536ec658", size = 112354, upload-time = "2025-10-08T08:44:00.22Z" }, + { url = "https://files.pythonhosted.org/packages/f2/cb/fdef02e0d6ee8443a9683a43650d61c6474b634b6ae6e1c6f097da6310bf/pyqt6_sip-13.10.2-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9128d770a611200529468397d710bc972f1dcfe12bfcbb09a3ccddcd4d54fa5b", size = 323488, upload-time = "2025-10-08T08:44:01.965Z" }, + { url = "https://files.pythonhosted.org/packages/8c/5b/8ede8d6234c3ea884cbd097d7d47ff9910fb114efe041af62b4453acd23b/pyqt6_sip-13.10.2-cp314-cp314-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d820a0fae7315932c08f27dc0a7e33e0f50fe351001601a8eb9cf6f22b04562e", size = 303881, upload-time = "2025-10-08T08:44:04.086Z" }, + { url = "https://files.pythonhosted.org/packages/be/44/b5e78b072d1594643b0f1ff348f2bf54d4adb5a3f9b9f0989c54e33238d6/pyqt6_sip-13.10.2-cp314-cp314-win_amd64.whl", hash = "sha256:3213bb6e102d3842a3bb7e59d5f6e55f176c80880ff0b39d0dac0cfe58313fb3", size = 55098, upload-time = "2025-10-08T08:44:08.943Z" }, + { url = "https://files.pythonhosted.org/packages/e2/91/357e9fcef5d830c3d50503d35e0357818aca3540f78748cc214dfa015d00/pyqt6_sip-13.10.2-cp314-cp314-win_arm64.whl", hash = "sha256:ce33ff1f94960ad4b08035e39fa0c3c9a67070bec39ffe3e435c792721504726", size = 46088, upload-time = "2025-10-08T08:44:10.014Z" }, +] + +[[package]] +name = "pyside6" +version = "6.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyside6-addons" }, + { name = "pyside6-essentials" }, + { name = "shiboken6" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/98/84b16f78b5d92dd234fb1eb9890a350a5b0c83d985bb8c44a92f813a2d02/pyside6-6.10.0-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:c2cbc5dc2a164e3c7c51b3435e24203e90e5edd518c865466afccbd2e5872bb0", size = 558115, upload-time = "2025-10-08T09:47:09.246Z" }, + { url = "https://files.pythonhosted.org/packages/4e/76/0961c8c5653ecb60a6881b649dcb6b71a6be5bd1c8d441ecc48ac7f50b1a/pyside6-6.10.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:ae8c3c8339cd7c3c9faa7cc5c52670dcc8662ccf4b63a6fed61c6345b90c4c01", size = 557762, upload-time = "2025-10-08T09:47:11.819Z" }, + { url = "https://files.pythonhosted.org/packages/c8/73/6187502fff8b6599443d15c46dd900b2ded24be5aacb2becce33f6faf566/pyside6-6.10.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:9f402f883e640048fab246d36e298a5e16df9b18ba2e8c519877e472d3602820", size = 558299, upload-time = "2025-10-08T09:47:14.255Z" }, + { url = "https://files.pythonhosted.org/packages/43/67/94794ebaf198bbdb35cb77f19f38370f9b323b036ab149874bc33c38faab/pyside6-6.10.0-cp39-abi3-win_amd64.whl", hash = "sha256:70a8bcc73ea8d6baab70bba311eac77b9a1d31f658d0b418e15eb6ea36c97e6f", size = 564367, upload-time = "2025-10-08T09:47:16.287Z" }, + { url = "https://files.pythonhosted.org/packages/1d/cc/552331d413c1b933d54ed45e33cc7ff29d0b239677975fe2528e7ac8bfbc/pyside6-6.10.0-cp39-abi3-win_arm64.whl", hash = "sha256:4b709bdeeb89d386059343a5a706fc185cee37b517bda44c7d6b64d5fdaf3339", size = 548826, upload-time = "2025-10-08T09:47:18.399Z" }, +] + +[[package]] +name = "pyside6-addons" +version = "6.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyside6-essentials" }, + { name = "shiboken6" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/23/9fbdec2ce16244ac3fe28e6d44c39c70465c93a03325939a792fd00fde7f/pyside6_addons-6.10.0-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:88e61e21ee4643cdd9efb39ec52f4dc1ac74c0b45c5b7fa453d03c094f0a8a5c", size = 322248256, upload-time = "2025-10-08T09:47:37.844Z" }, + { url = "https://files.pythonhosted.org/packages/b7/b8/d129210f2c7366b4e1bf5bb6230be42052b29e8ba1b1d7db6ef333cf5a39/pyside6_addons-6.10.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:08d4ed46c4c9a353a9eb84134678f8fdd4ce17fb8cce2b3686172a7575025464", size = 170238987, upload-time = "2025-10-08T09:47:51.446Z" }, + { url = "https://files.pythonhosted.org/packages/cf/ae/ede1edd009395092219f3437d2ee59f9ba93739c28c040542ed47c6cc831/pyside6_addons-6.10.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:15d32229d681be0bba1b936c4a300da43d01e1917ada5b57f9e03a387c245ab0", size = 165939425, upload-time = "2025-10-08T09:48:02.073Z" }, + { url = "https://files.pythonhosted.org/packages/7d/5d/a3c32f85ac7f905c95679967c0ddda0ba043c273b75623cc90d8185064e4/pyside6_addons-6.10.0-cp39-abi3-win_amd64.whl", hash = "sha256:99d93a32c17c5f6d797c3b90dd58f2a8bae13abde81e85802c34ceafaee11859", size = 164814172, upload-time = "2025-10-08T09:48:12.891Z" }, + { url = "https://files.pythonhosted.org/packages/a3/2a/4ff71b09571202c8e1320c45276fc1d0cd81ee53107dfc17bb22d4243f88/pyside6_addons-6.10.0-cp39-abi3-win_arm64.whl", hash = "sha256:92536427413f3b6557cf53f1a515cd766725ee46a170aff57ad2ff1dfce0ffb1", size = 34104251, upload-time = "2025-10-08T09:48:18.287Z" }, +] + +[[package]] +name = "pyside6-essentials" +version = "6.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "shiboken6" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/55/bad02ab890c8b8101abef0db4a2e5304be78a69e23a438e4d8555b664467/pyside6_essentials-6.10.0-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:003e871effe1f3e5b876bde715c15a780d876682005a6e989d89f48b8b93e93a", size = 105034090, upload-time = "2025-10-08T09:48:24.944Z" }, + { url = "https://files.pythonhosted.org/packages/5c/75/e17efc7eb900993e0e3925885635c6cf373c817196f09bcbcc102b00ac94/pyside6_essentials-6.10.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:1d5e013a8698e37ab8ef360e6960794eb5ef20832a8d562e649b8c5a0574b2d8", size = 76362150, upload-time = "2025-10-08T09:48:31.849Z" }, + { url = "https://files.pythonhosted.org/packages/06/62/fbd1e81caafcda97b147c03f5b06cfaadd8da5fa8298f527d2ec648fa5b7/pyside6_essentials-6.10.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:b1dd0864f0577a448fb44426b91cafff7ee7cccd1782ba66491e1c668033f998", size = 75454169, upload-time = "2025-10-08T09:48:38.21Z" }, + { url = "https://files.pythonhosted.org/packages/bb/3a/d8211d17e6ca70f641c6ebd309f08ef18930acda60e74082c75875a274da/pyside6_essentials-6.10.0-cp39-abi3-win_amd64.whl", hash = "sha256:fc167eb211dd1580e20ba90d299e74898e7a5a1306d832421e879641fc03b6fe", size = 74361794, upload-time = "2025-10-08T09:48:44.335Z" }, + { url = "https://files.pythonhosted.org/packages/61/e9/0e22e3c10325c4ff09447fadb43f7962afb82cef0b65358f5704251c6b32/pyside6_essentials-6.10.0-cp39-abi3-win_arm64.whl", hash = "sha256:6dd0936394cb14da2fd8e869899f5e0925a738b1c8d74c2f22503720ea363fb1", size = 55099467, upload-time = "2025-10-08T09:48:50.902Z" }, +] + +[[package]] +name = "shiboken6" +version = "6.10.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/78/3e730aea82089dd82b1e092bc265778bda329459e6ad9b7134eec5fff3f2/shiboken6-6.10.0-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:7a5f5f400ebfb3a13616030815708289c2154e701a60b9db7833b843e0bee543", size = 476535, upload-time = "2025-10-08T09:49:08Z" }, + { url = "https://files.pythonhosted.org/packages/ea/09/4ffa3284a17b6b765d45b41c9a7f1b2cde6c617c853ac6f170fb62bbbece/shiboken6-6.10.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:e612734da515d683696980107cdc0396a3ae0f07b059f0f422ec8a2333810234", size = 271098, upload-time = "2025-10-08T09:49:09.47Z" }, + { url = "https://files.pythonhosted.org/packages/31/29/00e26f33a0fb259c2edce9c761a7a438d7531ca514bdb1a4c072673bd437/shiboken6-6.10.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:b01377e68d14132360efb0f4b7233006d26aa8ae9bb50edf00960c2a5f52d148", size = 267698, upload-time = "2025-10-08T09:49:10.694Z" }, + { url = "https://files.pythonhosted.org/packages/11/30/e4624a7e3f0dc9796b701079b77defcce0d32d1afc86bb1d0df04bc3d9e2/shiboken6-6.10.0-cp39-abi3-win_amd64.whl", hash = "sha256:0bc5631c1bf150cbef768a17f5f289aae1cb4db6c6b0c19b2421394e27783717", size = 1234227, upload-time = "2025-10-08T09:49:12.774Z" }, + { url = "https://files.pythonhosted.org/packages/dd/e5/0ab862005ea87dc8647ba958a3099b3b0115fd6491c65da5c5a0f6364db1/shiboken6-6.10.0-cp39-abi3-win_arm64.whl", hash = "sha256:dfc4beab5fec7dbbebbb418f3bf99af865d6953aa0795435563d4cbb82093b61", size = 1794775, upload-time = "2025-10-08T09:49:14.641Z" }, +]