pygpt-net 2.7.5__py3-none-any.whl → 2.7.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pygpt_net/CHANGELOG.txt +14 -0
- pygpt_net/__init__.py +4 -4
- pygpt_net/controller/chat/remote_tools.py +3 -9
- pygpt_net/controller/chat/stream.py +2 -2
- pygpt_net/controller/chat/{handler/worker.py → stream_worker.py} +20 -64
- pygpt_net/controller/debug/fixtures.py +3 -2
- pygpt_net/controller/files/files.py +65 -4
- pygpt_net/core/debug/models.py +2 -2
- pygpt_net/core/filesystem/url.py +4 -1
- pygpt_net/core/render/web/body.py +3 -2
- pygpt_net/core/types/chunk.py +27 -0
- pygpt_net/data/config/config.json +14 -4
- pygpt_net/data/config/models.json +192 -4
- pygpt_net/data/config/settings.json +126 -36
- pygpt_net/data/js/app/template.js +1 -1
- pygpt_net/data/js/app.min.js +2 -2
- pygpt_net/data/locale/locale.de.ini +5 -0
- pygpt_net/data/locale/locale.en.ini +35 -8
- pygpt_net/data/locale/locale.es.ini +5 -0
- pygpt_net/data/locale/locale.fr.ini +5 -0
- pygpt_net/data/locale/locale.it.ini +5 -0
- pygpt_net/data/locale/locale.pl.ini +5 -0
- pygpt_net/data/locale/locale.uk.ini +5 -0
- pygpt_net/data/locale/locale.zh.ini +5 -0
- pygpt_net/data/locale/plugin.cmd_mouse_control.en.ini +2 -2
- pygpt_net/item/ctx.py +3 -5
- pygpt_net/js_rc.py +2449 -2447
- pygpt_net/plugin/cmd_mouse_control/config.py +8 -7
- pygpt_net/plugin/cmd_mouse_control/plugin.py +3 -4
- pygpt_net/plugin/cmd_mouse_control/worker.py +2 -1
- pygpt_net/plugin/cmd_mouse_control/worker_sandbox.py +2 -1
- pygpt_net/provider/api/anthropic/__init__.py +16 -9
- pygpt_net/provider/api/anthropic/chat.py +259 -11
- pygpt_net/provider/api/anthropic/computer.py +844 -0
- pygpt_net/provider/api/anthropic/remote_tools.py +172 -0
- pygpt_net/{controller/chat/handler/anthropic_stream.py → provider/api/anthropic/stream.py} +24 -10
- pygpt_net/provider/api/anthropic/tools.py +32 -77
- pygpt_net/provider/api/anthropic/utils.py +30 -0
- pygpt_net/provider/api/google/__init__.py +6 -5
- pygpt_net/provider/api/google/chat.py +3 -8
- pygpt_net/{controller/chat/handler/google_stream.py → provider/api/google/stream.py} +1 -1
- pygpt_net/provider/api/google/utils.py +185 -0
- pygpt_net/{controller/chat/handler → provider/api/langchain}/__init__.py +0 -0
- pygpt_net/{controller/chat/handler/langchain_stream.py → provider/api/langchain/stream.py} +1 -1
- pygpt_net/provider/api/llama_index/__init__.py +0 -0
- pygpt_net/{controller/chat/handler/llamaindex_stream.py → provider/api/llama_index/stream.py} +1 -1
- pygpt_net/provider/api/openai/__init__.py +7 -3
- pygpt_net/provider/api/openai/image.py +2 -2
- pygpt_net/provider/api/openai/responses.py +0 -0
- pygpt_net/{controller/chat/handler/openai_stream.py → provider/api/openai/stream.py} +1 -1
- pygpt_net/provider/api/openai/utils.py +69 -3
- pygpt_net/provider/api/x_ai/__init__.py +117 -17
- pygpt_net/provider/api/x_ai/chat.py +272 -102
- pygpt_net/provider/api/x_ai/image.py +149 -47
- pygpt_net/provider/api/x_ai/{remote.py → remote_tools.py} +165 -70
- pygpt_net/provider/api/x_ai/responses.py +507 -0
- pygpt_net/provider/api/x_ai/stream.py +715 -0
- pygpt_net/provider/api/x_ai/tools.py +59 -8
- pygpt_net/{controller/chat/handler → provider/api/x_ai}/utils.py +1 -2
- pygpt_net/provider/api/x_ai/vision.py +1 -4
- pygpt_net/provider/core/config/patch.py +22 -1
- pygpt_net/provider/core/model/patch.py +26 -1
- pygpt_net/tools/image_viewer/ui/dialogs.py +300 -13
- pygpt_net/tools/text_editor/ui/dialogs.py +3 -2
- pygpt_net/tools/text_editor/ui/widgets.py +5 -1
- pygpt_net/ui/base/context_menu.py +44 -1
- pygpt_net/ui/layout/toolbox/indexes.py +22 -19
- pygpt_net/ui/layout/toolbox/model.py +28 -5
- pygpt_net/ui/widget/dialog/base.py +16 -5
- pygpt_net/ui/widget/image/display.py +25 -8
- pygpt_net/ui/widget/tabs/output.py +9 -1
- pygpt_net/ui/widget/textarea/editor.py +14 -1
- pygpt_net/ui/widget/textarea/input.py +20 -7
- pygpt_net/ui/widget/textarea/notepad.py +24 -1
- pygpt_net/ui/widget/textarea/output.py +23 -1
- pygpt_net/ui/widget/textarea/web.py +16 -1
- {pygpt_net-2.7.5.dist-info → pygpt_net-2.7.7.dist-info}/METADATA +16 -2
- {pygpt_net-2.7.5.dist-info → pygpt_net-2.7.7.dist-info}/RECORD +80 -73
- pygpt_net/controller/chat/handler/xai_stream.py +0 -135
- {pygpt_net-2.7.5.dist-info → pygpt_net-2.7.7.dist-info}/LICENSE +0 -0
- {pygpt_net-2.7.5.dist-info → pygpt_net-2.7.7.dist-info}/WHEEL +0 -0
- {pygpt_net-2.7.5.dist-info → pygpt_net-2.7.7.dist-info}/entry_points.txt +0 -0
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date:
|
|
9
|
+
# Updated Date: 2026.01.03 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
|
+
from typing import Union
|
|
11
12
|
|
|
12
13
|
from PySide6.QtWidgets import QMenu
|
|
13
14
|
from PySide6.QtGui import QAction, QIcon
|
|
@@ -24,6 +25,8 @@ class ContextMenu:
|
|
|
24
25
|
_ICON_CODE = QIcon(":/icons/code.svg")
|
|
25
26
|
_ICON_TEXT = QIcon(":/icons/text.svg")
|
|
26
27
|
_ICON_TRANSLATOR = QIcon(":/icons/translate.svg")
|
|
28
|
+
_ICON_ZOOM_IN = QIcon(":/icons/zoom_in.svg")
|
|
29
|
+
_ICON_ZOOM_OUT = QIcon(":/icons/zoom_out.svg")
|
|
27
30
|
|
|
28
31
|
def __init__(self, window=None):
|
|
29
32
|
"""
|
|
@@ -33,6 +36,46 @@ class ContextMenu:
|
|
|
33
36
|
"""
|
|
34
37
|
self.window = window
|
|
35
38
|
|
|
39
|
+
def get_zoom_menu(self, parent, parent_type: str, current_zoom: Union[int, float], callback = None) -> QMenu:
|
|
40
|
+
"""
|
|
41
|
+
Get zoom menu (Zoom In/Out)
|
|
42
|
+
|
|
43
|
+
:param parent: Parent menu
|
|
44
|
+
:param parent_type: Type of textarea ('chat', 'notepad', etc.)
|
|
45
|
+
:param current_zoom: Current zoom level
|
|
46
|
+
:param callback: Callback function on zoom change
|
|
47
|
+
:return: Menu
|
|
48
|
+
"""
|
|
49
|
+
menu = QMenu(trans('context_menu.zoom'), parent)
|
|
50
|
+
if parent_type in ("font_size.input", "font_size", "editor"):
|
|
51
|
+
action_zoom_in = QAction(self._ICON_ZOOM_IN, trans('context_menu.zoom.in'), menu)
|
|
52
|
+
new_zoom = current_zoom + 2
|
|
53
|
+
action_zoom_in.triggered.connect(
|
|
54
|
+
lambda checked=False, new_zoom=new_zoom: callback(new_zoom)
|
|
55
|
+
)
|
|
56
|
+
menu.addAction(action_zoom_in)
|
|
57
|
+
action_zoom_out = QAction(self._ICON_ZOOM_OUT, trans('context_menu.zoom.out'), menu)
|
|
58
|
+
new_zoom = current_zoom - 2
|
|
59
|
+
action_zoom_out.triggered.connect(
|
|
60
|
+
lambda checked=False, new_zoom=new_zoom: callback(new_zoom)
|
|
61
|
+
)
|
|
62
|
+
menu.addAction(action_zoom_out)
|
|
63
|
+
elif parent_type in ("zoom"):
|
|
64
|
+
action_zoom_in = QAction(self._ICON_ZOOM_IN, trans('context_menu.zoom.in'), menu)
|
|
65
|
+
new_zoom = current_zoom + 0.1
|
|
66
|
+
action_zoom_in.triggered.connect(
|
|
67
|
+
lambda checked=False, new_zoom=new_zoom: callback(new_zoom)
|
|
68
|
+
)
|
|
69
|
+
menu.addAction(action_zoom_in)
|
|
70
|
+
action_zoom_out = QAction(self._ICON_ZOOM_OUT, trans('context_menu.zoom.out'), menu)
|
|
71
|
+
new_zoom = current_zoom - 0.1
|
|
72
|
+
action_zoom_out.triggered.connect(
|
|
73
|
+
lambda checked=False, new_zoom=new_zoom: callback(new_zoom)
|
|
74
|
+
)
|
|
75
|
+
menu.addAction(action_zoom_out)
|
|
76
|
+
|
|
77
|
+
return menu
|
|
78
|
+
|
|
36
79
|
def get_copy_to_menu(
|
|
37
80
|
self,
|
|
38
81
|
parent,
|
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date:
|
|
9
|
+
# Updated Date: 2026.01.03 17:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
|
-
from PySide6.
|
|
12
|
+
from PySide6.QtCore import Qt, QSize
|
|
13
|
+
from PySide6.QtGui import QStandardItemModel, QIcon
|
|
13
14
|
from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout, QPushButton, QWidget, QCheckBox, QSizePolicy
|
|
14
15
|
|
|
15
16
|
from pygpt_net.ui.widget.element.labels import HelpLabel, TitleLabel
|
|
@@ -54,6 +55,14 @@ class Indexes:
|
|
|
54
55
|
"""
|
|
55
56
|
nodes = self.window.ui.nodes
|
|
56
57
|
nodes['indexes.new'] = QPushButton(self._settings_icon, "")
|
|
58
|
+
# Configure compact, borderless settings button aligned to the right
|
|
59
|
+
icon_size = 20
|
|
60
|
+
nodes['indexes.new'].setFlat(True)
|
|
61
|
+
nodes['indexes.new'].setStyleSheet("QPushButton { border: none; background: transparent; padding: 0; }")
|
|
62
|
+
nodes['indexes.new'].setIconSize(QSize(icon_size, icon_size))
|
|
63
|
+
nodes['indexes.new'].setFixedSize(icon_size, icon_size)
|
|
64
|
+
nodes['indexes.new'].setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
|
65
|
+
nodes['indexes.new'].setFocusPolicy(Qt.NoFocus)
|
|
57
66
|
nodes['indexes.new'].clicked.connect(self._open_llama_index_settings)
|
|
58
67
|
|
|
59
68
|
nodes['indexes.label'] = TitleLabel(trans("toolbox.indexes.label"))
|
|
@@ -119,6 +128,14 @@ class Indexes:
|
|
|
119
128
|
nodes['llama_index.mode.select'].setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
|
120
129
|
|
|
121
130
|
nodes['indexes.new'] = QPushButton(self._settings_icon, "")
|
|
131
|
+
# Configure compact, borderless settings button for options row
|
|
132
|
+
icon_size = 20
|
|
133
|
+
nodes['indexes.new'].setFlat(True)
|
|
134
|
+
nodes['indexes.new'].setStyleSheet("QPushButton { border: none; background: transparent; padding: 0; }")
|
|
135
|
+
nodes['indexes.new'].setIconSize(QSize(icon_size, icon_size))
|
|
136
|
+
nodes['indexes.new'].setFixedSize(icon_size, icon_size)
|
|
137
|
+
nodes['indexes.new'].setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
|
138
|
+
nodes['indexes.new'].setFocusPolicy(Qt.NoFocus)
|
|
122
139
|
nodes['indexes.new'].clicked.connect(self._open_llama_index_settings)
|
|
123
140
|
|
|
124
141
|
nodes['indexes.label'] = TitleLabel(trans("toolbox.indexes.label"))
|
|
@@ -129,6 +146,8 @@ class Indexes:
|
|
|
129
146
|
idx_layout.addWidget(nodes['indexes.select'])
|
|
130
147
|
idx_layout.addWidget(nodes['indexes.new'], alignment=Qt.AlignRight)
|
|
131
148
|
idx_layout.setContentsMargins(0, 0, 0, 10)
|
|
149
|
+
# Ensure the combo takes maximum horizontal space
|
|
150
|
+
idx_layout.setStretch(1, 1)
|
|
132
151
|
idx_widget = QWidget()
|
|
133
152
|
idx_widget.setLayout(idx_layout)
|
|
134
153
|
idx_widget.setMinimumHeight(55)
|
|
@@ -138,6 +157,7 @@ class Indexes:
|
|
|
138
157
|
mode_layout.addWidget(nodes['llama_index.mode.label'])
|
|
139
158
|
mode_layout.addWidget(nodes['llama_index.mode.select'])
|
|
140
159
|
mode_layout.setContentsMargins(0, 0, 0, 10)
|
|
160
|
+
mode_layout.setStretch(1, 1)
|
|
141
161
|
mode_widget = QWidget()
|
|
142
162
|
mode_widget.setLayout(mode_layout)
|
|
143
163
|
mode_widget.setMinimumHeight(55)
|
|
@@ -173,23 +193,6 @@ class Indexes:
|
|
|
173
193
|
if self._last_combo_signature != signature:
|
|
174
194
|
self.window.ui.nodes['indexes.select'].set_keys(combo_keys)
|
|
175
195
|
self._last_combo_signature = signature
|
|
176
|
-
"""
|
|
177
|
-
# store previous selection
|
|
178
|
-
self.window.ui.nodes[self.id].backup_selection()
|
|
179
|
-
|
|
180
|
-
self.window.ui.models[self.id].removeRows(0, self.window.ui.models[self.id].rowCount())
|
|
181
|
-
i = 0
|
|
182
|
-
for item in data:
|
|
183
|
-
self.window.ui.models[self.id].insertRow(i)
|
|
184
|
-
name = item['name']
|
|
185
|
-
index = self.window.ui.models[self.id].index(i, 0)
|
|
186
|
-
self.window.ui.models[self.id].setData(index, "ID: " + item['id'], QtCore.Qt.ToolTipRole)
|
|
187
|
-
self.window.ui.models[self.id].setData(self.window.ui.models[self.id].index(i, 0), name)
|
|
188
|
-
i += 1
|
|
189
|
-
|
|
190
|
-
# restore previous selection
|
|
191
|
-
self.window.ui.nodes[self.id].restore_selection()
|
|
192
|
-
"""
|
|
193
196
|
|
|
194
197
|
def _open_llama_index_settings(self):
|
|
195
198
|
self.window.controller.settings.open_section('llama-index')
|
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date:
|
|
9
|
+
# Updated Date: 2026.01.03 17:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
|
-
|
|
12
|
-
from PySide6.
|
|
11
|
+
from PySide6.QtCore import Qt, QSize
|
|
12
|
+
from PySide6.QtGui import QIcon
|
|
13
|
+
from PySide6.QtWidgets import QVBoxLayout, QWidget, QPushButton, QHBoxLayout, QSizePolicy
|
|
13
14
|
|
|
14
15
|
from pygpt_net.ui.widget.element.labels import TitleLabel
|
|
15
16
|
from pygpt_net.ui.widget.lists.model_combo import ModelCombo
|
|
@@ -27,6 +28,7 @@ class Model:
|
|
|
27
28
|
self.window = window
|
|
28
29
|
self.id = 'prompt.model'
|
|
29
30
|
self.label_key = f'{self.id}.label'
|
|
31
|
+
self._settings_icon = QIcon(":/icons/settings.svg")
|
|
30
32
|
|
|
31
33
|
def setup(self) -> QWidget:
|
|
32
34
|
"""
|
|
@@ -51,12 +53,33 @@ class Model:
|
|
|
51
53
|
nodes[self.label_key] = label
|
|
52
54
|
|
|
53
55
|
combo = ModelCombo(self.window, self.id)
|
|
56
|
+
# Ensure combo takes maximum horizontal space
|
|
57
|
+
combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
|
54
58
|
nodes[self.id] = combo
|
|
55
59
|
|
|
60
|
+
nodes['prompt.model.settings'] = QPushButton(self._settings_icon, "")
|
|
61
|
+
# Configure compact, borderless settings button aligned to the right
|
|
62
|
+
icon_size = 20
|
|
63
|
+
nodes['prompt.model.settings'].setFlat(True)
|
|
64
|
+
nodes['prompt.model.settings'].setStyleSheet("QPushButton { border: none; padding: 0; }")
|
|
65
|
+
nodes['prompt.model.settings'].setIconSize(QSize(icon_size, icon_size))
|
|
66
|
+
nodes['prompt.model.settings'].setFixedSize(icon_size, icon_size)
|
|
67
|
+
nodes['prompt.model.settings'].setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
|
68
|
+
nodes['prompt.model.settings'].setFocusPolicy(Qt.NoFocus)
|
|
69
|
+
nodes['prompt.model.settings'].clicked.connect(self._open_settings)
|
|
70
|
+
|
|
71
|
+
model_cols = QHBoxLayout()
|
|
72
|
+
model_cols.addWidget(combo, 1) # stretch to take remaining space
|
|
73
|
+
model_cols.addWidget(nodes['prompt.model.settings'], alignment=Qt.AlignRight)
|
|
74
|
+
model_cols.setContentsMargins(0, 0, 0, 0)
|
|
75
|
+
|
|
56
76
|
layout = QVBoxLayout()
|
|
57
77
|
layout.addWidget(label)
|
|
58
|
-
layout.
|
|
78
|
+
layout.addLayout(model_cols)
|
|
59
79
|
layout.addStretch()
|
|
60
80
|
layout.setContentsMargins(2, 5, 5, 5)
|
|
61
81
|
|
|
62
|
-
return layout
|
|
82
|
+
return layout
|
|
83
|
+
|
|
84
|
+
def _open_settings(self):
|
|
85
|
+
self.window.controller.model.editor.open()
|
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date:
|
|
9
|
+
# Updated Date: 2026.01.05 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtWidgets import QApplication
|
|
13
|
-
from PySide6.QtCore import
|
|
13
|
+
from PySide6.QtCore import QSize, QPoint, QEvent, Qt
|
|
14
14
|
from PySide6.QtWidgets import QDialog
|
|
15
15
|
|
|
16
16
|
|
|
@@ -25,11 +25,20 @@ class BaseDialog(QDialog):
|
|
|
25
25
|
super(BaseDialog, self).__init__(window)
|
|
26
26
|
self.window = window
|
|
27
27
|
self.id = id
|
|
28
|
+
self.shared_id = None
|
|
28
29
|
self.disable_geometry_store = False
|
|
29
30
|
self.setWindowFlags(
|
|
30
31
|
self.windowFlags() | Qt.WindowMaximizeButtonHint
|
|
31
32
|
)
|
|
32
33
|
|
|
34
|
+
def _get_id(self):
|
|
35
|
+
"""
|
|
36
|
+
Get dialog id
|
|
37
|
+
|
|
38
|
+
:return: dialog id
|
|
39
|
+
"""
|
|
40
|
+
return self.shared_id if self.shared_id is not None else self.id
|
|
41
|
+
|
|
33
42
|
def showEvent(self, event):
|
|
34
43
|
"""
|
|
35
44
|
Event called when the dialog is shown
|
|
@@ -58,7 +67,7 @@ class BaseDialog(QDialog):
|
|
|
58
67
|
if self.disable_geometry_store:
|
|
59
68
|
return False
|
|
60
69
|
|
|
61
|
-
if self.window is None or self.
|
|
70
|
+
if self.window is None or self._get_id() is None:
|
|
62
71
|
return False
|
|
63
72
|
|
|
64
73
|
config = self.window.core.config
|
|
@@ -71,6 +80,7 @@ class BaseDialog(QDialog):
|
|
|
71
80
|
def save_geometry(self):
|
|
72
81
|
"""Save dialog geometry"""
|
|
73
82
|
config = self.window.core.config
|
|
83
|
+
id = self._get_id()
|
|
74
84
|
item = {
|
|
75
85
|
"size": [self.size().width(), self.size().height()],
|
|
76
86
|
"pos": [self.pos().x(), self.pos().y()]
|
|
@@ -81,7 +91,7 @@ class BaseDialog(QDialog):
|
|
|
81
91
|
|
|
82
92
|
if not isinstance(data, dict):
|
|
83
93
|
data = {}
|
|
84
|
-
data[
|
|
94
|
+
data[id] = item
|
|
85
95
|
|
|
86
96
|
if self.store_geometry_enabled():
|
|
87
97
|
config.set_session("layout.dialog.geometry", data)
|
|
@@ -89,6 +99,7 @@ class BaseDialog(QDialog):
|
|
|
89
99
|
def restore_geometry(self):
|
|
90
100
|
"""Restore dialog geometry"""
|
|
91
101
|
# get available screen geometry
|
|
102
|
+
id = self._get_id()
|
|
92
103
|
screen = QApplication.primaryScreen()
|
|
93
104
|
available_geometry = screen.availableGeometry()
|
|
94
105
|
config = self.window.core.config
|
|
@@ -97,7 +108,7 @@ class BaseDialog(QDialog):
|
|
|
97
108
|
if self.store_geometry_enabled():
|
|
98
109
|
data = config.get_session("layout.dialog.geometry", {})
|
|
99
110
|
|
|
100
|
-
item = data.get(
|
|
111
|
+
item = data.get(id, {})
|
|
101
112
|
if isinstance(item, dict) and "size" in item and "pos" in item:
|
|
102
113
|
width, height = item["size"]
|
|
103
114
|
x, y = item["pos"]
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date:
|
|
9
|
+
# Updated Date: 2026.01.03 17:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtGui import QAction, QIcon
|
|
@@ -27,6 +27,17 @@ class ImageLabel(QLabel):
|
|
|
27
27
|
self.window = window
|
|
28
28
|
self.path = path
|
|
29
29
|
|
|
30
|
+
def _get_window(self):
|
|
31
|
+
"""
|
|
32
|
+
Get main window instance
|
|
33
|
+
|
|
34
|
+
:return: Window instance
|
|
35
|
+
"""
|
|
36
|
+
if self.window is not None:
|
|
37
|
+
if hasattr(self.window, 'window'):
|
|
38
|
+
return self.window.window
|
|
39
|
+
return self.window
|
|
40
|
+
|
|
30
41
|
def contextMenuEvent(self, event):
|
|
31
42
|
"""
|
|
32
43
|
Context menu event
|
|
@@ -36,6 +47,8 @@ class ImageLabel(QLabel):
|
|
|
36
47
|
if not self.path:
|
|
37
48
|
return
|
|
38
49
|
|
|
50
|
+
win = self._get_window()
|
|
51
|
+
|
|
39
52
|
actions = {}
|
|
40
53
|
use_actions = []
|
|
41
54
|
actions['open'] = QAction(QIcon(":/icons/fullscreen.svg"), trans('img.action.open'), self)
|
|
@@ -64,13 +77,13 @@ class ImageLabel(QLabel):
|
|
|
64
77
|
self,
|
|
65
78
|
)
|
|
66
79
|
actions['use_attachment'].triggered.connect(
|
|
67
|
-
lambda:
|
|
80
|
+
lambda: win.controller.files.use_attachment(self.path),
|
|
68
81
|
)
|
|
69
82
|
use_actions.append(actions['use_attachment'])
|
|
70
83
|
|
|
71
84
|
# use by filetype
|
|
72
|
-
if
|
|
73
|
-
extra_use_actions =
|
|
85
|
+
if win.core.filesystem.actions.has_use(self.path):
|
|
86
|
+
extra_use_actions = win.core.filesystem.actions.get_use(self, self.path)
|
|
74
87
|
for action in extra_use_actions:
|
|
75
88
|
use_actions.append(action)
|
|
76
89
|
|
|
@@ -97,7 +110,8 @@ class ImageLabel(QLabel):
|
|
|
97
110
|
|
|
98
111
|
:param event: mouse event
|
|
99
112
|
"""
|
|
100
|
-
self.
|
|
113
|
+
win = self._get_window()
|
|
114
|
+
win.tools.get("viewer").open(self.path)
|
|
101
115
|
|
|
102
116
|
def action_open_dir(self, event):
|
|
103
117
|
"""
|
|
@@ -105,7 +119,8 @@ class ImageLabel(QLabel):
|
|
|
105
119
|
|
|
106
120
|
:param event: mouse event
|
|
107
121
|
"""
|
|
108
|
-
self.
|
|
122
|
+
win = self._get_window()
|
|
123
|
+
win.tools.get("viewer").open_dir(self.path)
|
|
109
124
|
|
|
110
125
|
def action_save(self, event):
|
|
111
126
|
"""
|
|
@@ -113,7 +128,8 @@ class ImageLabel(QLabel):
|
|
|
113
128
|
|
|
114
129
|
:param event: mouse event
|
|
115
130
|
"""
|
|
116
|
-
self.
|
|
131
|
+
win = self._get_window()
|
|
132
|
+
win.tools.get("viewer").save(self.path)
|
|
117
133
|
|
|
118
134
|
def action_delete(self, event):
|
|
119
135
|
"""
|
|
@@ -121,4 +137,5 @@ class ImageLabel(QLabel):
|
|
|
121
137
|
|
|
122
138
|
:param event: mouse event
|
|
123
139
|
"""
|
|
124
|
-
self.
|
|
140
|
+
win = self._get_window()
|
|
141
|
+
win.tools.get("viewer").delete(self.path)
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date:
|
|
9
|
+
# Updated Date: 2026.01.03 17:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtWidgets import QTabWidget, QMenu, QPushButton, QToolButton, QTabBar
|
|
@@ -502,6 +502,14 @@ class OutputTabs(QTabWidget):
|
|
|
502
502
|
self.show_tool_menu(idx, column_idx, event.globalPos()) # tool
|
|
503
503
|
else:
|
|
504
504
|
self.show_default_menu(idx, column_idx, event.globalPos()) # default
|
|
505
|
+
|
|
506
|
+
# close on middle click
|
|
507
|
+
elif event.button() == Qt.MiddleButton:
|
|
508
|
+
idx = self.tabBar().tabAt(event.pos())
|
|
509
|
+
column_idx = self.column.get_idx()
|
|
510
|
+
self.window.controller.ui.tabs.close(idx, column_idx)
|
|
511
|
+
event.accept()
|
|
512
|
+
return
|
|
505
513
|
super(OutputTabs, self).mousePressEvent(event)
|
|
506
514
|
|
|
507
515
|
def prepare_menu(self, index: int, column_idx: int) -> QMenu:
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date:
|
|
9
|
+
# Updated Date: 2026.01.03 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtCore import Qt
|
|
@@ -88,6 +88,10 @@ class BaseCodeEditor(QTextEdit):
|
|
|
88
88
|
lambda: self.window.controller.chat.common.save_text(self.toPlainText()))
|
|
89
89
|
menu.addAction(action)
|
|
90
90
|
|
|
91
|
+
# Add zoom submenu
|
|
92
|
+
zoom_menu = self.window.ui.context_menu.get_zoom_menu(self, "editor", self.value, self.on_zoom_changed)
|
|
93
|
+
menu.addMenu(zoom_menu)
|
|
94
|
+
|
|
91
95
|
action = QAction(self._ICON_SEARCH, trans('text.context_menu.find'), menu)
|
|
92
96
|
action.triggered.connect(self.find_open)
|
|
93
97
|
action.setShortcut(self._FIND_SEQ)
|
|
@@ -137,6 +141,15 @@ class BaseCodeEditor(QTextEdit):
|
|
|
137
141
|
else:
|
|
138
142
|
super().wheelEvent(event)
|
|
139
143
|
|
|
144
|
+
def on_zoom_changed(self, value: int):
|
|
145
|
+
"""
|
|
146
|
+
On font size changed
|
|
147
|
+
|
|
148
|
+
:param value: New font size
|
|
149
|
+
"""
|
|
150
|
+
self.value = value
|
|
151
|
+
self.update_stylesheet(f"QTextEdit {{ font-size: {value}px }};")
|
|
152
|
+
|
|
140
153
|
def focusInEvent(self, e):
|
|
141
154
|
super().focusInEvent(e)
|
|
142
155
|
self.window.controller.finder.focus_in(self.finder)
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date:
|
|
9
|
+
# Updated Date: 2026.01.03 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import Optional
|
|
@@ -394,6 +394,10 @@ class ChatInput(QTextEdit):
|
|
|
394
394
|
action.triggered.connect(lambda: self.window.controller.chat.common.save_text(self.toPlainText()))
|
|
395
395
|
menu.addAction(action)
|
|
396
396
|
|
|
397
|
+
# Add zoom submenu
|
|
398
|
+
zoom_menu = self.window.ui.context_menu.get_zoom_menu(self, "font_size.input", self.value, self.on_zoom_changed)
|
|
399
|
+
menu.addMenu(zoom_menu)
|
|
400
|
+
|
|
397
401
|
try:
|
|
398
402
|
self.window.core.prompt.template.to_menu_options(menu, "input")
|
|
399
403
|
self.window.core.prompt.custom.to_menu_options(menu, "input")
|
|
@@ -458,8 +462,8 @@ class ChatInput(QTextEdit):
|
|
|
458
462
|
:param event: Event
|
|
459
463
|
"""
|
|
460
464
|
if event.modifiers() & Qt.ControlModifier:
|
|
461
|
-
prev = self.value
|
|
462
465
|
dy = event.angleDelta().y()
|
|
466
|
+
prev = self.value
|
|
463
467
|
if dy > 0:
|
|
464
468
|
if self.value < self.max_font_size:
|
|
465
469
|
self.value += 1
|
|
@@ -468,15 +472,24 @@ class ChatInput(QTextEdit):
|
|
|
468
472
|
self.value -= 1
|
|
469
473
|
|
|
470
474
|
if self.value != prev:
|
|
471
|
-
self.
|
|
472
|
-
self.window.core.config.save()
|
|
473
|
-
self.window.controller.ui.update_font_size()
|
|
474
|
-
# Reflow may change number of lines; adjust auto-height next tick
|
|
475
|
-
QTimer.singleShot(0, self._schedule_auto_resize)
|
|
475
|
+
self.on_zoom_changed(self.value)
|
|
476
476
|
event.accept()
|
|
477
477
|
return
|
|
478
478
|
super().wheelEvent(event)
|
|
479
479
|
|
|
480
|
+
def on_zoom_changed(self, value: int):
|
|
481
|
+
"""
|
|
482
|
+
Called when zoom level changes.
|
|
483
|
+
|
|
484
|
+
:param value: new zoom level
|
|
485
|
+
"""
|
|
486
|
+
self.value = value
|
|
487
|
+
self.window.core.config.data['font_size.input'] = value
|
|
488
|
+
self.window.core.config.save()
|
|
489
|
+
self.window.controller.ui.update_font_size()
|
|
490
|
+
# Reflow may change number of lines; adjust auto-height next tick
|
|
491
|
+
QTimer.singleShot(0, self._schedule_auto_resize)
|
|
492
|
+
|
|
480
493
|
def changeEvent(self, event):
|
|
481
494
|
super().changeEvent(event)
|
|
482
495
|
if event.type() == QEvent.FontChange:
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date:
|
|
9
|
+
# Updated Date: 2026.01.03 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtCore import Qt, QEvent, QTimer
|
|
@@ -214,6 +214,10 @@ class NotepadOutput(QTextEdit):
|
|
|
214
214
|
lambda: self.window.controller.chat.common.save_text(self.toPlainText()))
|
|
215
215
|
menu.addAction(action)
|
|
216
216
|
|
|
217
|
+
# Add zoom submenu
|
|
218
|
+
zoom_menu = self.window.ui.context_menu.get_zoom_menu(self, "font_size", self.value, self.on_zoom_changed)
|
|
219
|
+
menu.addMenu(zoom_menu)
|
|
220
|
+
|
|
217
221
|
action = QAction(self.ICON_SEARCH, trans('text.context_menu.find'), self)
|
|
218
222
|
action.triggered.connect(self.find_open)
|
|
219
223
|
action.setShortcut(QKeySequence("Ctrl+F"))
|
|
@@ -282,6 +286,25 @@ class NotepadOutput(QTextEdit):
|
|
|
282
286
|
super(NotepadOutput, self).wheelEvent(event)
|
|
283
287
|
self.last_scroll_pos = self._vscroll.value()
|
|
284
288
|
|
|
289
|
+
def on_zoom_changed(self, value: int):
|
|
290
|
+
"""
|
|
291
|
+
On font size changed
|
|
292
|
+
|
|
293
|
+
:param value: New font size
|
|
294
|
+
"""
|
|
295
|
+
self.value = value
|
|
296
|
+
self.window.core.config.data['font_size'] = value
|
|
297
|
+
self.window.core.config.save()
|
|
298
|
+
option = self.window.controller.settings.editor.get_option('font_size')
|
|
299
|
+
option['value'] = self.value
|
|
300
|
+
self.window.controller.config.apply(
|
|
301
|
+
parent_id='config',
|
|
302
|
+
key='font_size',
|
|
303
|
+
option=option,
|
|
304
|
+
)
|
|
305
|
+
self.window.controller.ui.update_font_size()
|
|
306
|
+
self.last_scroll_pos = self._vscroll.value()
|
|
307
|
+
|
|
285
308
|
def focusInEvent(self, e):
|
|
286
309
|
"""
|
|
287
310
|
Focus in event
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date:
|
|
9
|
+
# Updated Date: 2026.01.03 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtCore import Qt, QEvent
|
|
@@ -118,6 +118,10 @@ class ChatOutput(QTextBrowser):
|
|
|
118
118
|
action.triggered.connect(self._save_all_text)
|
|
119
119
|
menu.addAction(action)
|
|
120
120
|
|
|
121
|
+
# Add zoom submenu
|
|
122
|
+
zoom_menu = self.window.ui.context_menu.get_zoom_menu(self, "font_size", self.value, self.on_zoom_changed)
|
|
123
|
+
menu.addMenu(zoom_menu)
|
|
124
|
+
|
|
121
125
|
action = QAction(self.ICON_SEARCH, trans('text.context_menu.find'), self)
|
|
122
126
|
action.triggered.connect(self.find_open)
|
|
123
127
|
action.setShortcut(QKeySequence("Ctrl+F"))
|
|
@@ -193,6 +197,24 @@ class ChatOutput(QTextBrowser):
|
|
|
193
197
|
else:
|
|
194
198
|
super().wheelEvent(event)
|
|
195
199
|
|
|
200
|
+
def on_zoom_changed(self, value: int):
|
|
201
|
+
"""
|
|
202
|
+
On font size changed
|
|
203
|
+
|
|
204
|
+
:param value: New font size
|
|
205
|
+
"""
|
|
206
|
+
self.value = value
|
|
207
|
+
|
|
208
|
+
cfg = self.window.core.config
|
|
209
|
+
cfg.data['font_size'] = value
|
|
210
|
+
cfg.save()
|
|
211
|
+
|
|
212
|
+
ctrl = self.window.controller
|
|
213
|
+
option = ctrl.settings.editor.get_option('font_size')
|
|
214
|
+
option['value'] = value
|
|
215
|
+
ctrl.config.apply(parent_id='config', key='font_size', option=option)
|
|
216
|
+
ctrl.ui.update_font_size()
|
|
217
|
+
|
|
196
218
|
def focusInEvent(self, e):
|
|
197
219
|
"""
|
|
198
220
|
Focus in event
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date:
|
|
9
|
+
# Updated Date: 2026.01.03 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
from PySide6 import QtCore
|
|
12
12
|
from PySide6.QtCore import Qt, QObject, Signal, Slot, QEvent, QUrl, QCoreApplication, QEventLoop
|
|
@@ -345,12 +345,27 @@ class ChatWebOutput(QWebEngineView):
|
|
|
345
345
|
action.triggered.connect(self._save_as_html)
|
|
346
346
|
menu.addAction(action)
|
|
347
347
|
|
|
348
|
+
# Add zoom submenu
|
|
349
|
+
zoom_menu = self.window.ui.context_menu.get_zoom_menu(self, "zoom", self.get_zoom_value(), self.on_zoom_changed)
|
|
350
|
+
menu.addMenu(zoom_menu)
|
|
351
|
+
|
|
348
352
|
action = QAction(QIcon(":/icons/search.svg"), trans('text.context_menu.find'), self)
|
|
349
353
|
action.triggered.connect(self.find_open)
|
|
350
354
|
menu.addAction(action)
|
|
351
355
|
|
|
352
356
|
menu.exec_(self.mapToGlobal(position))
|
|
353
357
|
|
|
358
|
+
def on_zoom_changed(self, zoom: float):
|
|
359
|
+
"""
|
|
360
|
+
On zoom changed from context menu
|
|
361
|
+
|
|
362
|
+
:param zoom: float - new zoom factor
|
|
363
|
+
"""
|
|
364
|
+
p = self.page()
|
|
365
|
+
if p:
|
|
366
|
+
self.window.core.config.set("zoom", zoom)
|
|
367
|
+
self.update_zoom()
|
|
368
|
+
|
|
354
369
|
@Slot()
|
|
355
370
|
def _save_selected_txt(self):
|
|
356
371
|
"""Save selected content as text file"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pygpt-net
|
|
3
|
-
Version: 2.7.
|
|
3
|
+
Version: 2.7.7
|
|
4
4
|
Summary: Desktop AI Assistant powered by: OpenAI GPT-5, GPT-4, o1, o3, Gemini, Claude, Grok, DeepSeek, and other models supported by Llama Index, and Ollama. Chatbot, agents, completion, image generation, vision analysis, speech-to-text, plugins, MCP, internet access, file handling, command execution and more.
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: ai,api,api key,app,assistant,bielik,chat,chatbot,chatgpt,claude,dall-e,deepseek,desktop,gemini,gpt,gpt-3.5,gpt-4,gpt-4-vision,gpt-4o,gpt-5,gpt-oss,gpt3.5,gpt4,grok,langchain,llama-index,llama3,mistral,o1,o3,ollama,openai,presets,py-gpt,py_gpt,pygpt,pyside,qt,text completion,tts,ui,vision,whisper
|
|
@@ -120,7 +120,7 @@ Description-Content-Type: text/markdown
|
|
|
120
120
|
|
|
121
121
|
[](https://snapcraft.io/pygpt)
|
|
122
122
|
|
|
123
|
-
Release: **2.7.
|
|
123
|
+
Release: **2.7.7** | build: **2026-01-05** | Python: **>=3.10, <3.14**
|
|
124
124
|
|
|
125
125
|
> Official website: https://pygpt.net | Documentation: https://pygpt.readthedocs.io
|
|
126
126
|
>
|
|
@@ -3796,6 +3796,20 @@ may consume additional tokens that are not displayed in the main window.
|
|
|
3796
3796
|
|
|
3797
3797
|
## Recent changes:
|
|
3798
3798
|
|
|
3799
|
+
**2.7.7 (2026-01-05)**
|
|
3800
|
+
|
|
3801
|
+
- Added support for Responses API in xAI.
|
|
3802
|
+
- Added xAI remote tools: Remote MCP, Code Execution.
|
|
3803
|
+
- Added Anthropic remote tools: Remote MCP, Web Fetch, Code Execution.
|
|
3804
|
+
|
|
3805
|
+
**2.7.6 (2026-01-03)**
|
|
3806
|
+
|
|
3807
|
+
- Fixed compatibility with xAI SDK and resolved empty responses from Grok models.
|
|
3808
|
+
- Fixed missing libraries in the Snap package.
|
|
3809
|
+
- Added zoom and grab functionality in the Image Viewer.
|
|
3810
|
+
- Added a zoom menu to textarea and web widgets.
|
|
3811
|
+
- Added the ability to close tabs with a middle mouse button click.
|
|
3812
|
+
|
|
3799
3813
|
**2.7.5 (2026-01-03)**
|
|
3800
3814
|
|
|
3801
3815
|
- Added Sandbox/Playwright option to Computer Use mode.
|