From d18f2803ec535958b27fefbb85c8a5618e5fe7e8 Mon Sep 17 00:00:00 2001 From: DasMoorhuhn Date: Thu, 18 Dec 2025 23:43:53 +0100 Subject: [PATCH] init --- .gitignore | 10 ++ .python-version | 1 + compile_ui.sh | 2 + pyproject.toml | 10 ++ src/main.py | 140 ++++++++++++++++++++++++++++ src/ui/window.py | 130 ++++++++++++++++++++++++++ src/ui/window.ui | 238 +++++++++++++++++++++++++++++++++++++++++++++++ uv.lock | 87 +++++++++++++++++ 8 files changed, 618 insertions(+) create mode 100644 .gitignore create mode 100644 .python-version create mode 100644 compile_ui.sh create mode 100644 pyproject.toml create mode 100644 src/main.py create mode 100644 src/ui/window.py create mode 100644 src/ui/window.ui create mode 100644 uv.lock diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..505a3b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# Python-generated files +__pycache__/ +*.py[oc] +build/ +dist/ +wheels/ +*.egg-info + +# Virtual environments +.venv diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..2c07333 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.11 diff --git a/compile_ui.sh b/compile_ui.sh new file mode 100644 index 0000000..608234e --- /dev/null +++ b/compile_ui.sh @@ -0,0 +1,2 @@ +pyside6-uic ./src/ui/window.ui -o ./src/ui/window.py +echo Done \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..48c6ce5 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,10 @@ +[project] +name = "clipboard-link-cleaner" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.11" +dependencies = [ + "pyperclip>=1.11.0", + "pyside6>=6.10.0", +] diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..f97030c --- /dev/null +++ b/src/main.py @@ -0,0 +1,140 @@ +import sys +import html +import signal +from time import sleep +from urllib.parse import unquote + +import pyperclip +from PySide6.QtWidgets import QApplication +from PySide6.QtWidgets import QMainWindow +from PySide6.QtWidgets import QSystemTrayIcon +from PySide6.QtWidgets import QTextBrowser +from PySide6.QtCore import QThread +from PySide6.QtCore import Signal +from PySide6.QtGui import QIcon +from PySide6.QtGui import QTextCharFormat, QColor + +from ui.window import Ui_MainWindow + + +def highlight_special_chars(text): + text = html.escape(text) + + text = text.replace( + "?", '?' + ) + text = text.replace( + "&", '&' + ) + + return text + +def set_text_with_highlight(browser: QTextBrowser, text: str): + browser.clear() + text = unquote(text) + cursor = browser.textCursor() + + highlight_fmt = QTextCharFormat() + highlight_fmt.setBackground(QColor("yellow")) + highlight_fmt.setForeground(QColor("black")) + + normal_fmt = QTextCharFormat() + + for ch in text: + if ch in ("?", "&"): + cursor.insertText(ch, highlight_fmt) + else: + cursor.insertText(ch, normal_fmt) + +class ClipboardWatcher(QThread): + clipboard_content = Signal(str) + + def __init__(self): + super().__init__() + self.clipboard = "" + + def run(self): + while True: + content = pyperclip.paste() + if content != self.clipboard: + self.clipboard = content + self.clipboard_content.emit(content) + sleep(.5) + + def stop(self): + self.terminate() + self.wait() + + +class MainWindow(QMainWindow): + def __init__(self): + super(MainWindow, self).__init__() + self.ui = Ui_MainWindow() + self.ui.setupUi(self) + self.tray = QSystemTrayIcon() + self.tray.setIcon(QIcon.fromTheme("edit-copy")) + self.tray.setVisible(True) + self.clipboard_watcher = ClipboardWatcher() + self.clipboard_watcher.clipboard_content.connect(self.on_clipboard) + self.clipboard_watcher.start() + + self.ui.button_cleanup.clicked.connect(self.on_cleanup) + + self.notification_duration_seconds = 7 + self.content = [] + self.sanitized_link = False + + def on_clipboard(self, content:str): + if not content.startswith("http"): return + self.cleanup(content) + self.ui.textbrowser_list.clear() + # self.ui.textbrowser_list.setHtml(highlight_special_chars(content)) + set_text_with_highlight(browser=self.ui.textbrowser_list, text=self.content[0]) + + if len(self.content) == 2: + set_text_with_highlight(browser=self.ui.textbrowser_list_removed, text=self.content[1]) + if self.ui.checkBox_auto_sanitize.isChecked(): + self.overwrite_clipboard() + self.ui.listWidget_history.addItem(unquote(content)) + + def overwrite_clipboard(self): + pyperclip.copy(self.content[0]) + self.sanitized_link = True + self.tray.showMessage( + "CliPy Utility", + "Sanitized link", + icon=QSystemTrayIcon.Information, + msecs=self.notification_duration_seconds * 1000 + ) + + def on_cleanup(self): + self.overwrite_clipboard() + + def cleanup(self, text): + self.content = text.split("?", 1) + + def closeEvent(self, event) -> None: + """On close event take care to stop all QThreads + + Args: + event (_type_): _description_ + """ + self.clipboard_watcher.stop() + super().closeEvent(event) + + def handle_sigterm(self, *args) -> None: + """Handle SigInit and SigTerm + """ + QApplication.quit() + + +if __name__ == "__main__": + app = QApplication(sys.argv) + window = MainWindow() + signal.signal(signal.SIGINT, window.handle_sigterm) + signal.signal(signal.SIGTERM, window.handle_sigterm) + window.setFixedSize(691,477) + window.setWindowIcon(QIcon.fromTheme("edit-copy")) + window.setWindowTitle("CliPy Utility") + window.show() + sys.exit(app.exec()) \ No newline at end of file diff --git a/src/ui/window.py b/src/ui/window.py new file mode 100644 index 0000000..ead73cf --- /dev/null +++ b/src/ui/window.py @@ -0,0 +1,130 @@ +# -*- coding: utf-8 -*- + +################################################################################ +## Form generated from reading UI file 'window.ui' +## +## Created by: Qt User Interface Compiler version 6.9.0 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, + QMetaObject, QObject, QPoint, QRect, + QSize, QTime, QUrl, Qt) +from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, + QFont, QFontDatabase, QGradient, QIcon, + QImage, QKeySequence, QLinearGradient, QPainter, + QPalette, QPixmap, QRadialGradient, QTransform) +from PySide6.QtWidgets import (QApplication, QCheckBox, QFrame, QLabel, + QListWidget, QListWidgetItem, QMainWindow, QMenuBar, + QPushButton, QSizePolicy, QSpinBox, QStatusBar, + QTabWidget, QTextBrowser, QWidget) + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + if not MainWindow.objectName(): + MainWindow.setObjectName(u"MainWindow") + MainWindow.resize(691, 506) + MainWindow.setAnimated(True) + MainWindow.setDocumentMode(False) + MainWindow.setTabShape(QTabWidget.TabShape.Rounded) + self.centralwidget = QWidget(MainWindow) + self.centralwidget.setObjectName(u"centralwidget") + self.tabWidget = QTabWidget(self.centralwidget) + self.tabWidget.setObjectName(u"tabWidget") + self.tabWidget.setGeometry(QRect(0, 0, 691, 461)) + self.tab_2 = QWidget() + self.tab_2.setObjectName(u"tab_2") + self.spinBox_max_history = QSpinBox(self.tab_2) + self.spinBox_max_history.setObjectName(u"spinBox_max_history") + self.spinBox_max_history.setGeometry(QRect(170, 10, 71, 21)) + self.spinBox_max_history.setMaximum(10000) + self.label_max_history = QLabel(self.tab_2) + self.label_max_history.setObjectName(u"label_max_history") + self.label_max_history.setGeometry(QRect(10, 10, 81, 21)) + self.checkBox_auto_sanitize = QCheckBox(self.tab_2) + self.checkBox_auto_sanitize.setObjectName(u"checkBox_auto_sanitize") + self.checkBox_auto_sanitize.setGeometry(QRect(170, 70, 71, 23)) + self.checkBox_auto_sanitize.setChecked(True) + self.label = QLabel(self.tab_2) + self.label.setObjectName(u"label") + self.label.setGeometry(QRect(10, 40, 121, 18)) + self.spinBox_notification_duration = QSpinBox(self.tab_2) + self.spinBox_notification_duration.setObjectName(u"spinBox_notification_duration") + self.spinBox_notification_duration.setGeometry(QRect(170, 40, 71, 21)) + self.spinBox_notification_duration.setMaximum(10000) + self.spinBox_notification_duration.setValue(7) + self.label_2 = QLabel(self.tab_2) + self.label_2.setObjectName(u"label_2") + self.label_2.setGeometry(QRect(10, 70, 131, 18)) + self.tabWidget.addTab(self.tab_2, "") + self.tab = QWidget() + self.tab.setObjectName(u"tab") + self.textbrowser_list = QTextBrowser(self.tab) + self.textbrowser_list.setObjectName(u"textbrowser_list") + self.textbrowser_list.setGeometry(QRect(0, 0, 681, 171)) + self.textbrowser_list.setFrameShape(QFrame.Shape.Box) + self.button_cleanup = QPushButton(self.tab) + self.button_cleanup.setObjectName(u"button_cleanup") + self.button_cleanup.setGeometry(QRect(10, 390, 71, 26)) + self.textbrowser_list_removed = QTextBrowser(self.tab) + self.textbrowser_list_removed.setObjectName(u"textbrowser_list_removed") + self.textbrowser_list_removed.setGeometry(QRect(0, 180, 681, 201)) + self.textbrowser_list_removed.setFrameShape(QFrame.Shape.Box) + self.tabWidget.addTab(self.tab, "") + self.tab_3 = QWidget() + self.tab_3.setObjectName(u"tab_3") + self.listWidget_history = QListWidget(self.tab_3) + self.listWidget_history.setObjectName(u"listWidget_history") + self.listWidget_history.setGeometry(QRect(0, 0, 681, 381)) + self.listWidget_history.setFrameShape(QFrame.Shape.Box) + self.pushButton_clear_history = QPushButton(self.tab_3) + self.pushButton_clear_history.setObjectName(u"pushButton_clear_history") + self.pushButton_clear_history.setGeometry(QRect(10, 390, 61, 26)) + self.tabWidget.addTab(self.tab_3, "") + MainWindow.setCentralWidget(self.centralwidget) + self.menubar = QMenuBar(MainWindow) + self.menubar.setObjectName(u"menubar") + self.menubar.setGeometry(QRect(0, 0, 691, 23)) + MainWindow.setMenuBar(self.menubar) + self.statusbar = QStatusBar(MainWindow) + self.statusbar.setObjectName(u"statusbar") + MainWindow.setStatusBar(self.statusbar) + + self.retranslateUi(MainWindow) + + self.tabWidget.setCurrentIndex(0) + + + QMetaObject.connectSlotsByName(MainWindow) + # setupUi + + def retranslateUi(self, MainWindow): + MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"Py Clipy Utility", None)) + self.label_max_history.setText(QCoreApplication.translate("MainWindow", u"Max history", None)) + self.checkBox_auto_sanitize.setText("") + self.label.setText(QCoreApplication.translate("MainWindow", u"Notify timeout (s)", None)) + self.label_2.setText(QCoreApplication.translate("MainWindow", u"Auto-Sanitize links", None)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), QCoreApplication.translate("MainWindow", u"General", None)) + self.textbrowser_list.setHtml(QCoreApplication.translate("MainWindow", u"\n" +"\n" +"


", None)) + self.button_cleanup.setText(QCoreApplication.translate("MainWindow", u"Sanitize", None)) + self.textbrowser_list_removed.setHtml(QCoreApplication.translate("MainWindow", u"\n" +"\n" +"


", None)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), QCoreApplication.translate("MainWindow", u"URLs", None)) + self.pushButton_clear_history.setText(QCoreApplication.translate("MainWindow", u"Clear", None)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), QCoreApplication.translate("MainWindow", u"History", None)) + # retranslateUi + diff --git a/src/ui/window.ui b/src/ui/window.ui new file mode 100644 index 0000000..d318130 --- /dev/null +++ b/src/ui/window.ui @@ -0,0 +1,238 @@ + + + MainWindow + + + + 0 + 0 + 691 + 506 + + + + Py Clipy Utility + + + true + + + false + + + QTabWidget::TabShape::Rounded + + + + + + 0 + 0 + 691 + 461 + + + + 0 + + + + General + + + + + 170 + 10 + 71 + 21 + + + + 10000 + + + + + + 10 + 10 + 81 + 21 + + + + Max history + + + + + + 170 + 70 + 71 + 23 + + + + + + + true + + + + + + 10 + 40 + 121 + 18 + + + + Notify timeout (s) + + + + + + 170 + 40 + 71 + 21 + + + + 10000 + + + 7 + + + + + + 10 + 70 + 131 + 18 + + + + Auto-Sanitize links + + + + + + URLs + + + + + 0 + 0 + 681 + 171 + + + + QFrame::Shape::Box + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> +p, li { white-space: pre-wrap; } +hr { height: 1px; border-width: 0; } +li.unchecked::marker { content: "\2610"; } +li.checked::marker { content: "\2612"; } +</style></head><body style=" font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + + + + + + 10 + 390 + 71 + 26 + + + + Sanitize + + + + + + 0 + 180 + 681 + 201 + + + + QFrame::Shape::Box + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> +p, li { white-space: pre-wrap; } +hr { height: 1px; border-width: 0; } +li.unchecked::marker { content: "\2610"; } +li.checked::marker { content: "\2612"; } +</style></head><body style=" font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + + + + + + History + + + + + 0 + 0 + 681 + 381 + + + + QFrame::Shape::Box + + + + + + 10 + 390 + 61 + 26 + + + + Clear + + + + + + + + + 0 + 0 + 691 + 23 + + + + + + + + diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..db16327 --- /dev/null +++ b/uv.lock @@ -0,0 +1,87 @@ +version = 1 +revision = 3 +requires-python = ">=3.11" + +[[package]] +name = "clipboard-link-cleaner" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "pyperclip" }, + { name = "pyside6" }, +] + +[package.metadata] +requires-dist = [ + { name = "pyperclip", specifier = ">=1.11.0" }, + { name = "pyside6", specifier = ">=6.10.0" }, +] + +[[package]] +name = "pyperclip" +version = "1.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, +] + +[[package]] +name = "pyside6" +version = "6.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyside6-addons" }, + { name = "pyside6-essentials" }, + { name = "shiboken6" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/22/f82cfcd1158be502c5741fe67c3fa853f3c1edbd3ac2c2250769dd9722d1/pyside6-6.10.1-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:d0e70dd0e126d01986f357c2a555722f9462cf8a942bf2ce180baf69f468e516", size = 558169, upload-time = "2025-11-20T10:09:08.79Z" }, + { url = "https://files.pythonhosted.org/packages/66/eb/54afe242a25d1c33b04ecd8321a549d9efb7b89eef7690eed92e98ba1dc9/pyside6-6.10.1-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4053bf51ba2c2cb20e1005edd469997976a02cec009f7c46356a0b65c137f1fa", size = 557818, upload-time = "2025-11-20T10:09:10.132Z" }, + { url = "https://files.pythonhosted.org/packages/4d/af/5706b1b33587dc2f3dfa3a5000424befba35e4f2d5889284eebbde37138b/pyside6-6.10.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:7d3ca20a40139ca5324a7864f1d91cdf2ff237e11bd16354a42670f2a4eeb13c", size = 558358, upload-time = "2025-11-20T10:09:11.288Z" }, + { url = "https://files.pythonhosted.org/packages/26/41/3f48d724ecc8e42cea8a8442aa9b5a86d394b85093275990038fd1020039/pyside6-6.10.1-cp39-abi3-win_amd64.whl", hash = "sha256:9f89ff994f774420eaa38cec6422fddd5356611d8481774820befd6f3bb84c9e", size = 564424, upload-time = "2025-11-20T10:09:12.677Z" }, + { url = "https://files.pythonhosted.org/packages/af/30/395411473b433875a82f6b5fdd0cb28f19a0e345bcaac9fbc039400d7072/pyside6-6.10.1-cp39-abi3-win_arm64.whl", hash = "sha256:9c5c1d94387d1a32a6fae25348097918ef413b87dfa3767c46f737c6d48ae437", size = 548866, upload-time = "2025-11-20T10:09:14.174Z" }, +] + +[[package]] +name = "pyside6-addons" +version = "6.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyside6-essentials" }, + { name = "shiboken6" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/f9/b72a2578d7dbef7741bb90b5756b4ef9c99a5b40148ea53ce7f048573fe9/pyside6_addons-6.10.1-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:4d2b82bbf9b861134845803837011e5f9ac7d33661b216805273cf0c6d0f8e82", size = 322639446, upload-time = "2025-11-20T09:54:50.75Z" }, + { url = "https://files.pythonhosted.org/packages/94/3b/3ed951c570a15570706a89d39bfd4eaaffdf16d5c2dca17e82fc3ec8aaa6/pyside6_addons-6.10.1-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:330c229b58d30083a7b99ed22e118eb4f4126408429816a4044ccd0438ae81b4", size = 170678293, upload-time = "2025-11-20T09:56:40.991Z" }, + { url = "https://files.pythonhosted.org/packages/22/77/4c780b204d0bf3323a75c184e349d063e208db44c993f1214aa4745d6f47/pyside6_addons-6.10.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:56864b5fecd6924187a2d0f7e98d968ed72b6cc267caa5b294cd7e88fff4e54c", size = 166365011, upload-time = "2025-11-20T09:57:20.261Z" }, + { url = "https://files.pythonhosted.org/packages/04/14/58239776499e6b279fa6ca2e0d47209531454b99f6bd2ad7c96f11109416/pyside6_addons-6.10.1-cp39-abi3-win_amd64.whl", hash = "sha256:b6e249d15407dd33d6a2ffabd9dc6d7a8ab8c95d05f16a71dad4d07781c76341", size = 164864664, upload-time = "2025-11-20T09:57:54.815Z" }, + { url = "https://files.pythonhosted.org/packages/e2/cd/1b74108671ba4b1ebb2661330665c4898b089e9c87f7ba69fe2438f3d1b6/pyside6_addons-6.10.1-cp39-abi3-win_arm64.whl", hash = "sha256:0de303c0447326cdc6c8be5ab066ef581e2d0baf22560c9362d41b8304fdf2db", size = 34191225, upload-time = "2025-11-20T09:58:04.184Z" }, +] + +[[package]] +name = "pyside6-essentials" +version = "6.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "shiboken6" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/b0/c43209fecef79912e9b1c70a1b5172b1edf76caebcc885c58c60a09613b0/pyside6_essentials-6.10.1-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:cd224aff3bb26ff1fca32c050e1c4d0bd9f951a96219d40d5f3d0128485b0bbe", size = 105461499, upload-time = "2025-11-20T09:59:23.733Z" }, + { url = "https://files.pythonhosted.org/packages/5f/8e/b69ba7fa0c701f3f4136b50460441697ec49ee6ea35c229eb2a5ee4b5952/pyside6_essentials-6.10.1-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:e9ccbfb58c03911a0bce1f2198605b02d4b5ca6276bfc0cbcf7c6f6393ffb856", size = 76764617, upload-time = "2025-11-20T09:59:38.831Z" }, + { url = "https://files.pythonhosted.org/packages/bd/83/569d27f4b6c6b9377150fe1a3745d64d02614021bea233636bc936a23423/pyside6_essentials-6.10.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:ec8617c9b143b0c19ba1cc5a7e98c538e4143795480cb152aee47802c18dc5d2", size = 75850373, upload-time = "2025-11-20T09:59:56.082Z" }, + { url = "https://files.pythonhosted.org/packages/1e/64/a8df6333de8ccbf3a320e1346ca30d0f314840aff5e3db9b4b66bf38e26c/pyside6_essentials-6.10.1-cp39-abi3-win_amd64.whl", hash = "sha256:9555a48e8f0acf63fc6a23c250808db841b28a66ed6ad89ee0e4df7628752674", size = 74491180, upload-time = "2025-11-20T10:00:11.215Z" }, + { url = "https://files.pythonhosted.org/packages/67/da/65cc6c6a870d4ea908c59b2f0f9e2cf3bfc6c0710ebf278ed72f69865e4e/pyside6_essentials-6.10.1-cp39-abi3-win_arm64.whl", hash = "sha256:4d1d248644f1778f8ddae5da714ca0f5a150a5e6f602af2765a7d21b876da05c", size = 55190458, upload-time = "2025-11-20T10:00:26.226Z" }, +] + +[[package]] +name = "shiboken6" +version = "6.10.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/8b/e5db743d505ceea3efc4cd9634a3bee22a3e2bf6e07cefd28c9b9edabcc6/shiboken6-6.10.1-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:9f2990f5b61b0b68ecadcd896ab4441f2cb097eef7797ecc40584107d9850d71", size = 478483, upload-time = "2025-11-20T10:08:52.411Z" }, + { url = "https://files.pythonhosted.org/packages/56/ba/b50c1a44b3c4643f482afbf1a0ea58f393827307100389ce29404f9ad3b0/shiboken6-6.10.1-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f4221a52dfb81f24a0d20cc4f8981cb6edd810d5a9fb28287ce10d342573a0e4", size = 271993, upload-time = "2025-11-20T10:08:54.093Z" }, + { url = "https://files.pythonhosted.org/packages/16/b8/939c24ebd662b0aa5c945443d0973145b3fb7079f0196274ef7bb4b98f73/shiboken6-6.10.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:c095b00f4d6bf578c0b2464bb4e264b351a99345374478570f69e2e679a2a1d0", size = 268691, upload-time = "2025-11-20T10:08:55.639Z" }, + { url = "https://files.pythonhosted.org/packages/cf/a6/8c65ee0fa5e172ebcca03246b1bc3bd96cdaf1d60537316648536b7072a5/shiboken6-6.10.1-cp39-abi3-win_amd64.whl", hash = "sha256:c1601d3cda1fa32779b141663873741b54e797cb0328458d7466281f117b0a4e", size = 1234704, upload-time = "2025-11-20T10:08:57.417Z" }, + { url = "https://files.pythonhosted.org/packages/7b/6a/c0fea2f2ac7d9d96618c98156500683a4d1f93fea0e8c5a2bc39913d7ef1/shiboken6-6.10.1-cp39-abi3-win_arm64.whl", hash = "sha256:5cf800917008587b551005a45add2d485cca66f5f7ecd5b320e9954e40448cc9", size = 1795567, upload-time = "2025-11-20T10:08:59.184Z" }, +]