pygpt-net 2.6.20__py3-none-any.whl → 2.6.22__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.
Files changed (221) hide show
  1. pygpt_net/CHANGELOG.txt +13 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/app.py +3 -1
  4. pygpt_net/controller/__init__.py +4 -8
  5. pygpt_net/controller/access/voice.py +2 -2
  6. pygpt_net/controller/agent/agent.py +130 -2
  7. pygpt_net/controller/agent/experts.py +93 -96
  8. pygpt_net/controller/agent/llama.py +2 -1
  9. pygpt_net/controller/assistant/assistant.py +18 -1
  10. pygpt_net/controller/assistant/batch.py +2 -3
  11. pygpt_net/controller/assistant/editor.py +2 -2
  12. pygpt_net/controller/assistant/files.py +2 -3
  13. pygpt_net/controller/assistant/store.py +2 -2
  14. pygpt_net/controller/attachment/attachment.py +17 -1
  15. pygpt_net/controller/audio/audio.py +2 -2
  16. pygpt_net/controller/camera/camera.py +15 -7
  17. pygpt_net/controller/chat/chat.py +2 -2
  18. pygpt_net/controller/chat/common.py +50 -33
  19. pygpt_net/controller/chat/image.py +67 -77
  20. pygpt_net/controller/chat/input.py +94 -166
  21. pygpt_net/controller/chat/output.py +83 -140
  22. pygpt_net/controller/chat/response.py +83 -102
  23. pygpt_net/controller/chat/text.py +116 -149
  24. pygpt_net/controller/ctx/common.py +2 -1
  25. pygpt_net/controller/ctx/ctx.py +87 -6
  26. pygpt_net/controller/files/files.py +13 -1
  27. pygpt_net/controller/idx/idx.py +26 -2
  28. pygpt_net/controller/idx/indexer.py +85 -76
  29. pygpt_net/controller/kernel/reply.py +53 -66
  30. pygpt_net/controller/kernel/stack.py +16 -16
  31. pygpt_net/controller/lang/lang.py +52 -34
  32. pygpt_net/controller/model/importer.py +3 -2
  33. pygpt_net/controller/model/model.py +62 -3
  34. pygpt_net/controller/notepad/notepad.py +86 -84
  35. pygpt_net/controller/plugins/settings.py +3 -4
  36. pygpt_net/controller/settings/editor.py +4 -4
  37. pygpt_net/controller/settings/profile.py +105 -124
  38. pygpt_net/controller/theme/menu.py +154 -57
  39. pygpt_net/controller/theme/nodes.py +51 -44
  40. pygpt_net/controller/theme/theme.py +33 -9
  41. pygpt_net/controller/tools/tools.py +2 -2
  42. pygpt_net/controller/ui/tabs.py +2 -3
  43. pygpt_net/controller/ui/ui.py +16 -2
  44. pygpt_net/core/agents/observer/evaluation.py +3 -3
  45. pygpt_net/core/agents/provider.py +25 -3
  46. pygpt_net/core/agents/runner.py +4 -1
  47. pygpt_net/core/agents/runners/llama_workflow.py +19 -7
  48. pygpt_net/core/agents/runners/loop.py +3 -1
  49. pygpt_net/core/agents/runners/openai_workflow.py +17 -3
  50. pygpt_net/core/agents/tools.py +4 -1
  51. pygpt_net/core/bridge/context.py +34 -37
  52. pygpt_net/core/ctx/container.py +13 -12
  53. pygpt_net/core/ctx/ctx.py +1 -1
  54. pygpt_net/core/ctx/output.py +7 -4
  55. pygpt_net/core/db/database.py +2 -2
  56. pygpt_net/core/debug/console/console.py +2 -2
  57. pygpt_net/core/debug/debug.py +12 -1
  58. pygpt_net/core/dispatcher/dispatcher.py +24 -1
  59. pygpt_net/core/events/app.py +7 -7
  60. pygpt_net/core/events/control.py +26 -26
  61. pygpt_net/core/events/event.py +6 -3
  62. pygpt_net/core/events/kernel.py +2 -2
  63. pygpt_net/core/events/render.py +13 -13
  64. pygpt_net/core/experts/experts.py +76 -82
  65. pygpt_net/core/experts/worker.py +12 -12
  66. pygpt_net/core/filesystem/actions.py +1 -2
  67. pygpt_net/core/models/models.py +5 -1
  68. pygpt_net/core/models/ollama.py +14 -5
  69. pygpt_net/core/render/plain/helpers.py +2 -5
  70. pygpt_net/core/render/plain/renderer.py +26 -30
  71. pygpt_net/core/render/web/body.py +1 -1
  72. pygpt_net/core/render/web/helpers.py +2 -2
  73. pygpt_net/core/render/web/renderer.py +4 -4
  74. pygpt_net/core/settings/settings.py +43 -13
  75. pygpt_net/core/tabs/tabs.py +20 -13
  76. pygpt_net/core/types/__init__.py +2 -1
  77. pygpt_net/core/types/agent.py +4 -4
  78. pygpt_net/core/types/base.py +19 -0
  79. pygpt_net/core/types/console.py +6 -6
  80. pygpt_net/core/types/mode.py +8 -8
  81. pygpt_net/core/types/multimodal.py +3 -3
  82. pygpt_net/core/types/openai.py +2 -1
  83. pygpt_net/data/config/config.json +5 -5
  84. pygpt_net/data/config/models.json +19 -3
  85. pygpt_net/data/config/settings.json +14 -14
  86. pygpt_net/data/locale/locale.de.ini +4 -1
  87. pygpt_net/data/locale/locale.en.ini +6 -3
  88. pygpt_net/data/locale/locale.es.ini +4 -1
  89. pygpt_net/data/locale/locale.fr.ini +4 -1
  90. pygpt_net/data/locale/locale.it.ini +4 -1
  91. pygpt_net/data/locale/locale.pl.ini +5 -4
  92. pygpt_net/data/locale/locale.uk.ini +4 -1
  93. pygpt_net/data/locale/locale.zh.ini +4 -1
  94. pygpt_net/item/ctx.py +256 -240
  95. pygpt_net/item/model.py +59 -116
  96. pygpt_net/item/preset.py +122 -105
  97. pygpt_net/plugin/twitter/plugin.py +2 -2
  98. pygpt_net/provider/agents/llama_index/workflow/planner.py +3 -3
  99. pygpt_net/provider/agents/openai/agent.py +4 -12
  100. pygpt_net/provider/agents/openai/agent_b2b.py +10 -15
  101. pygpt_net/provider/agents/openai/agent_planner.py +4 -4
  102. pygpt_net/provider/agents/openai/agent_with_experts.py +3 -7
  103. pygpt_net/provider/agents/openai/agent_with_experts_feedback.py +4 -8
  104. pygpt_net/provider/agents/openai/agent_with_feedback.py +4 -8
  105. pygpt_net/provider/agents/openai/bot_researcher.py +2 -18
  106. pygpt_net/provider/agents/openai/bots/__init__.py +0 -0
  107. pygpt_net/provider/agents/openai/bots/research_bot/__init__.py +0 -0
  108. pygpt_net/provider/agents/openai/bots/research_bot/agents/__init__.py +0 -0
  109. pygpt_net/provider/agents/openai/bots/research_bot/agents/planner_agent.py +1 -1
  110. pygpt_net/provider/agents/openai/bots/research_bot/agents/search_agent.py +1 -0
  111. pygpt_net/provider/agents/openai/bots/research_bot/agents/writer_agent.py +1 -1
  112. pygpt_net/provider/agents/openai/bots/research_bot/manager.py +1 -10
  113. pygpt_net/provider/agents/openai/evolve.py +5 -9
  114. pygpt_net/provider/agents/openai/supervisor.py +4 -8
  115. pygpt_net/provider/core/config/patch.py +10 -3
  116. pygpt_net/provider/core/ctx/db_sqlite/utils.py +43 -43
  117. pygpt_net/provider/core/model/patch.py +11 -1
  118. pygpt_net/provider/core/preset/json_file.py +47 -49
  119. pygpt_net/provider/gpt/agents/experts.py +2 -2
  120. pygpt_net/tools/audio_transcriber/ui/dialogs.py +44 -54
  121. pygpt_net/tools/code_interpreter/body.py +1 -2
  122. pygpt_net/tools/code_interpreter/tool.py +7 -4
  123. pygpt_net/tools/code_interpreter/ui/html.py +1 -3
  124. pygpt_net/tools/code_interpreter/ui/widgets.py +2 -3
  125. pygpt_net/tools/html_canvas/ui/widgets.py +1 -3
  126. pygpt_net/tools/image_viewer/ui/dialogs.py +40 -37
  127. pygpt_net/tools/indexer/ui/widgets.py +2 -4
  128. pygpt_net/tools/media_player/tool.py +2 -5
  129. pygpt_net/tools/media_player/ui/widgets.py +60 -36
  130. pygpt_net/tools/text_editor/ui/widgets.py +18 -19
  131. pygpt_net/tools/translator/ui/widgets.py +39 -35
  132. pygpt_net/ui/base/context_menu.py +9 -4
  133. pygpt_net/ui/dialog/db.py +1 -3
  134. pygpt_net/ui/dialog/models.py +1 -3
  135. pygpt_net/ui/dialog/models_importer.py +2 -4
  136. pygpt_net/ui/dialogs.py +34 -30
  137. pygpt_net/ui/layout/chat/attachments.py +72 -84
  138. pygpt_net/ui/layout/chat/attachments_ctx.py +40 -44
  139. pygpt_net/ui/layout/chat/attachments_uploaded.py +36 -39
  140. pygpt_net/ui/layout/chat/calendar.py +100 -70
  141. pygpt_net/ui/layout/chat/chat.py +23 -17
  142. pygpt_net/ui/layout/chat/input.py +95 -118
  143. pygpt_net/ui/layout/chat/output.py +100 -162
  144. pygpt_net/ui/layout/chat/painter.py +89 -61
  145. pygpt_net/ui/layout/ctx/ctx_list.py +43 -52
  146. pygpt_net/ui/layout/status.py +23 -14
  147. pygpt_net/ui/layout/toolbox/agent.py +27 -38
  148. pygpt_net/ui/layout/toolbox/agent_llama.py +42 -45
  149. pygpt_net/ui/layout/toolbox/assistants.py +42 -38
  150. pygpt_net/ui/layout/toolbox/computer_env.py +32 -23
  151. pygpt_net/ui/layout/toolbox/footer.py +13 -16
  152. pygpt_net/ui/layout/toolbox/image.py +18 -21
  153. pygpt_net/ui/layout/toolbox/indexes.py +46 -89
  154. pygpt_net/ui/layout/toolbox/mode.py +20 -7
  155. pygpt_net/ui/layout/toolbox/model.py +12 -10
  156. pygpt_net/ui/layout/toolbox/presets.py +68 -52
  157. pygpt_net/ui/layout/toolbox/prompt.py +31 -58
  158. pygpt_net/ui/layout/toolbox/toolbox.py +25 -21
  159. pygpt_net/ui/layout/toolbox/vision.py +20 -22
  160. pygpt_net/ui/main.py +2 -4
  161. pygpt_net/ui/menu/about.py +64 -84
  162. pygpt_net/ui/menu/audio.py +87 -63
  163. pygpt_net/ui/menu/config.py +121 -127
  164. pygpt_net/ui/menu/debug.py +69 -76
  165. pygpt_net/ui/menu/file.py +32 -35
  166. pygpt_net/ui/menu/menu.py +2 -3
  167. pygpt_net/ui/menu/plugins.py +69 -33
  168. pygpt_net/ui/menu/theme.py +45 -46
  169. pygpt_net/ui/menu/tools.py +56 -60
  170. pygpt_net/ui/menu/video.py +20 -25
  171. pygpt_net/ui/tray.py +1 -2
  172. pygpt_net/ui/widget/audio/bar.py +1 -3
  173. pygpt_net/ui/widget/audio/input_button.py +3 -4
  174. pygpt_net/ui/widget/calendar/select.py +1 -2
  175. pygpt_net/ui/widget/dialog/base.py +12 -9
  176. pygpt_net/ui/widget/dialog/editor_file.py +20 -23
  177. pygpt_net/ui/widget/dialog/find.py +25 -24
  178. pygpt_net/ui/widget/dialog/profile.py +57 -53
  179. pygpt_net/ui/widget/draw/painter.py +62 -93
  180. pygpt_net/ui/widget/element/button.py +42 -30
  181. pygpt_net/ui/widget/element/checkbox.py +23 -15
  182. pygpt_net/ui/widget/element/group.py +6 -5
  183. pygpt_net/ui/widget/element/labels.py +1 -2
  184. pygpt_net/ui/widget/filesystem/explorer.py +93 -102
  185. pygpt_net/ui/widget/image/display.py +1 -2
  186. pygpt_net/ui/widget/lists/assistant.py +1 -2
  187. pygpt_net/ui/widget/lists/attachment.py +1 -2
  188. pygpt_net/ui/widget/lists/attachment_ctx.py +1 -2
  189. pygpt_net/ui/widget/lists/context.py +2 -4
  190. pygpt_net/ui/widget/lists/index.py +1 -2
  191. pygpt_net/ui/widget/lists/model.py +1 -2
  192. pygpt_net/ui/widget/lists/model_editor.py +1 -2
  193. pygpt_net/ui/widget/lists/model_importer.py +1 -2
  194. pygpt_net/ui/widget/lists/preset.py +1 -2
  195. pygpt_net/ui/widget/lists/preset_plugins.py +1 -2
  196. pygpt_net/ui/widget/lists/profile.py +1 -2
  197. pygpt_net/ui/widget/lists/uploaded.py +1 -2
  198. pygpt_net/ui/widget/option/checkbox.py +2 -4
  199. pygpt_net/ui/widget/option/checkbox_list.py +1 -4
  200. pygpt_net/ui/widget/option/cmd.py +1 -4
  201. pygpt_net/ui/widget/option/dictionary.py +25 -28
  202. pygpt_net/ui/widget/option/input.py +1 -3
  203. pygpt_net/ui/widget/tabs/Input.py +16 -12
  204. pygpt_net/ui/widget/tabs/body.py +5 -3
  205. pygpt_net/ui/widget/tabs/layout.py +36 -25
  206. pygpt_net/ui/widget/tabs/output.py +96 -74
  207. pygpt_net/ui/widget/textarea/calendar_note.py +1 -2
  208. pygpt_net/ui/widget/textarea/editor.py +41 -73
  209. pygpt_net/ui/widget/textarea/find.py +11 -10
  210. pygpt_net/ui/widget/textarea/html.py +3 -6
  211. pygpt_net/ui/widget/textarea/input.py +63 -64
  212. pygpt_net/ui/widget/textarea/notepad.py +54 -38
  213. pygpt_net/ui/widget/textarea/output.py +65 -54
  214. pygpt_net/ui/widget/textarea/search_input.py +5 -4
  215. pygpt_net/ui/widget/textarea/web.py +2 -4
  216. pygpt_net/ui/widget/vision/camera.py +2 -31
  217. {pygpt_net-2.6.20.dist-info → pygpt_net-2.6.22.dist-info}/METADATA +25 -154
  218. {pygpt_net-2.6.20.dist-info → pygpt_net-2.6.22.dist-info}/RECORD +218 -217
  219. {pygpt_net-2.6.20.dist-info → pygpt_net-2.6.22.dist-info}/LICENSE +0 -0
  220. {pygpt_net-2.6.20.dist-info → pygpt_net-2.6.22.dist-info}/WHEEL +0 -0
  221. {pygpt_net-2.6.20.dist-info → pygpt_net-2.6.22.dist-info}/entry_points.txt +0 -0
@@ -6,21 +6,38 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.12.14 07:00:00 #
9
+ # Updated Date: 2025.08.24 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtWidgets import QTabWidget, QMenu, QPushButton
13
13
  from PySide6.QtCore import Qt, Slot
14
- from PySide6.QtGui import QAction, QIcon
14
+ from PySide6.QtGui import QAction, QIcon, QGuiApplication
15
15
 
16
16
  from pygpt_net.core.tabs.tab import Tab
17
17
  from pygpt_net.utils import trans
18
- import pygpt_net.icons_rc
18
+
19
+ _ICON_CACHE = {}
20
+
21
+ def icon(path: str) -> QIcon:
22
+ if QGuiApplication.instance() is None:
23
+ return QIcon()
24
+ cached = _ICON_CACHE.get(path)
25
+ if cached is None:
26
+ cached = QIcon(path)
27
+ _ICON_CACHE[path] = cached
28
+ return cached
29
+
30
+ ICON_PATH_ADD = ':/icons/add.svg'
31
+ ICON_PATH_EDIT = ':/icons/edit.svg'
32
+ ICON_PATH_CLOSE = ':/icons/close.svg'
33
+ ICON_PATH_RELOAD = ':/icons/reload.svg'
34
+ ICON_PATH_FORWARD = ':/icons/forward'
35
+ ICON_PATH_BACK = ':/icons/back'
19
36
 
20
37
 
21
38
  class AddButton(QPushButton):
22
39
  def __init__(self, window=None, column=None, tabs=None):
23
- super(AddButton, self).__init__(QIcon(":/icons/add.svg"), "", window)
40
+ super(AddButton, self).__init__(icon(ICON_PATH_ADD), "", window)
24
41
  self.window = window
25
42
  self.column = column
26
43
  self.tabs = tabs
@@ -65,23 +82,21 @@ class AddButton(QPushButton):
65
82
  :return: menu
66
83
  """
67
84
  menu = QMenu(self)
85
+ menu.setAttribute(Qt.WA_DeleteOnClose, True)
68
86
 
69
- actions = {}
70
- actions['add_chat'] = QAction(QIcon(":/icons/add.svg"), trans('action.tab.add.chat'), self)
71
- actions['add_chat'].triggered.connect(
72
- lambda: self.tabs.add_tab(index, column_idx, Tab.TAB_CHAT)
87
+ add_chat = QAction(icon(ICON_PATH_ADD), trans('action.tab.add.chat'), menu)
88
+ add_chat.triggered.connect(
89
+ lambda: self.tabs.add_tab(-2, column_idx, Tab.TAB_CHAT)
73
90
  )
74
- actions['add_notepad'] = QAction(QIcon(":/icons/add.svg"), trans('action.tab.add.notepad'), self)
75
- actions['add_notepad'].triggered.connect(
76
- lambda: self.tabs.add_tab(index, column_idx, Tab.TAB_NOTEPAD)
91
+ add_notepad = QAction(icon(ICON_PATH_ADD), trans('action.tab.add.notepad'), menu)
92
+ add_notepad.triggered.connect(
93
+ lambda: self.tabs.add_tab(-2, column_idx, Tab.TAB_NOTEPAD)
77
94
  )
78
95
 
79
- # add chat, add notepad
80
- menu.addAction(actions['add_chat'])
81
- menu.addAction(actions['add_notepad'])
96
+ menu.addAction(add_chat)
97
+ menu.addAction(add_notepad)
82
98
 
83
- # add tools submenu
84
- self.window.controller.tools.append_tab_menu(self, menu, index, column_idx, self.tabs)
99
+ self.window.controller.tools.append_tab_menu(self, menu, -2, column_idx, self.tabs)
85
100
 
86
101
  return menu
87
102
 
@@ -113,21 +128,11 @@ class OutputTabs(QTabWidget):
113
128
  self.setCornerWidget(add_button, corner=Qt.TopRightCorner)
114
129
 
115
130
  # connect signals
116
- self.currentChanged.connect(
117
- lambda: self.window.controller.ui.tabs.on_tab_changed(self.currentIndex(), self.column.get_idx())
118
- )
119
- self.tabBarClicked.connect(
120
- lambda: self.window.controller.ui.tabs.on_tab_clicked(self.currentIndex(), self.column.get_idx())
121
- )
122
- self.tabBarDoubleClicked.connect(
123
- lambda: self.window.controller.ui.tabs.on_tab_dbl_clicked(self.currentIndex(), self.column.get_idx())
124
- )
125
- self.tabCloseRequested.connect(
126
- lambda: self.window.controller.ui.tabs.on_tab_closed(self.currentIndex(), self.column.get_idx())
127
- )
128
- self.tabBar().tabMoved.connect(
129
- lambda: self.window.controller.ui.tabs.on_tab_moved(self.currentIndex(), self.column.get_idx())
130
- )
131
+ self.currentChanged.connect(self._on_current_changed)
132
+ self.tabBarClicked.connect(self._on_tabbar_clicked)
133
+ self.tabBarDoubleClicked.connect(self._on_tabbar_dbl_clicked)
134
+ self.tabCloseRequested.connect(self._on_tab_close_requested)
135
+ self.tabBar().tabMoved.connect(self._on_tab_moved)
131
136
 
132
137
  def get_column(self):
133
138
  """
@@ -177,44 +182,40 @@ class OutputTabs(QTabWidget):
177
182
  :return: menu
178
183
  """
179
184
  menu = QMenu(self)
185
+ menu.setAttribute(Qt.WA_DeleteOnClose, True)
180
186
 
181
- actions = {}
182
- actions['add_chat'] = QAction(QIcon(":/icons/add.svg"), trans('action.tab.add.chat'), self)
183
- actions['add_chat'].triggered.connect(
187
+ add_chat = QAction(icon(ICON_PATH_ADD), trans('action.tab.add.chat'), menu)
188
+ add_chat.triggered.connect(
184
189
  lambda: self.add_tab(index, column_idx, Tab.TAB_CHAT)
185
190
  )
186
- actions['add_notepad'] = QAction(QIcon(":/icons/add.svg"), trans('action.tab.add.notepad'), self)
187
- actions['add_notepad'].triggered.connect(
191
+ add_notepad = QAction(icon(ICON_PATH_ADD), trans('action.tab.add.notepad'), menu)
192
+ add_notepad.triggered.connect(
188
193
  lambda: self.add_tab(index, column_idx, Tab.TAB_NOTEPAD)
189
194
  )
190
- actions['edit'] = QAction(QIcon(":/icons/edit.svg"), trans('action.rename'), self)
191
- actions['edit'].triggered.connect(
195
+ edit = QAction(icon(ICON_PATH_EDIT), trans('action.rename'), menu)
196
+ edit.triggered.connect(
192
197
  lambda: self.rename_tab(index, column_idx)
193
198
  )
194
- actions['move_right'] = QAction(QIcon(":/icons/forward"), trans('action.tab.move.right'), self)
195
- actions['move_right'].triggered.connect(
199
+ move_right = QAction(icon(ICON_PATH_FORWARD), trans('action.tab.move.right'), menu)
200
+ move_right.triggered.connect(
196
201
  lambda: self.window.controller.ui.tabs.move_tab(index, column_idx, 1)
197
202
  )
198
- actions['move_left'] = QAction(QIcon(":/icons/back"), trans('action.tab.move.left'), self)
199
- actions['move_left'].triggered.connect(
203
+ move_left = QAction(icon(ICON_PATH_BACK), trans('action.tab.move.left'), menu)
204
+ move_left.triggered.connect(
200
205
  lambda: self.window.controller.ui.tabs.move_tab(index, column_idx, 0)
201
206
  )
202
207
 
203
- # add chat, add notepad
204
- menu.addAction(actions['add_chat'])
205
- menu.addAction(actions['add_notepad'])
208
+ menu.addAction(add_chat)
209
+ menu.addAction(add_notepad)
206
210
 
207
- # add tools submenu
208
211
  self.window.controller.tools.append_tab_menu(self, menu, index, column_idx, self)
209
212
 
210
- # rename tab
211
- menu.addAction(actions['edit'])
213
+ menu.addAction(edit)
212
214
 
213
- # move tab left, move tab right
214
215
  if column_idx != 0:
215
- menu.addAction(actions['move_left'])
216
+ menu.addAction(move_left)
216
217
  if column_idx != 1:
217
- menu.addAction(actions['move_right'])
218
+ menu.addAction(move_right)
218
219
 
219
220
  return menu
220
221
 
@@ -227,19 +228,18 @@ class OutputTabs(QTabWidget):
227
228
  :param global_pos: global position
228
229
  """
229
230
  context_menu = self.prepare_menu(index, column_idx)
230
- actions = {}
231
- actions['close'] = QAction(QIcon(":/icons/close.svg"), trans('action.tab.close'), self)
232
- actions['close'].triggered.connect(
231
+ close_act = QAction(icon(ICON_PATH_CLOSE), trans('action.tab.close'), context_menu)
232
+ close_act.triggered.connect(
233
233
  lambda: self.close_tab(index, column_idx)
234
234
  )
235
- actions['close_all'] = QAction(QIcon(":/icons/close.svg"), trans('action.tab.close_all.notepad'), self)
236
- actions['close_all'].triggered.connect(
235
+ close_all_act = QAction(icon(ICON_PATH_CLOSE), trans('action.tab.close_all.notepad'), context_menu)
236
+ close_all_act.triggered.connect(
237
237
  lambda: self.close_all(Tab.TAB_NOTEPAD, column_idx)
238
238
  )
239
- context_menu.addAction(actions['close'])
239
+ context_menu.addAction(close_act)
240
240
 
241
241
  if self.window.core.tabs.count_by_type(Tab.TAB_NOTEPAD) > 1:
242
- context_menu.addAction(actions['close_all'])
242
+ context_menu.addAction(close_all_act)
243
243
 
244
244
  context_menu.exec(global_pos)
245
245
 
@@ -252,20 +252,19 @@ class OutputTabs(QTabWidget):
252
252
  :param global_pos: global position
253
253
  """
254
254
  context_menu = self.prepare_menu(index, column_idx)
255
- actions = {}
256
- actions['close'] = QAction(QIcon(":/icons/close.svg"), trans('action.tab.close'), self)
257
- actions['close'].triggered.connect(
255
+ close_act = QAction(icon(ICON_PATH_CLOSE), trans('action.tab.close'), context_menu)
256
+ close_act.triggered.connect(
258
257
  lambda: self.close_tab(index, column_idx)
259
258
  )
260
- actions['close_all'] = QAction(QIcon(":/icons/close.svg"), trans('action.tab.close_all.chat'), self)
261
- actions['close_all'].triggered.connect(
259
+ close_all_act = QAction(icon(ICON_PATH_CLOSE), trans('action.tab.close_all.chat'), context_menu)
260
+ close_all_act.triggered.connect(
262
261
  lambda: self.close_all(Tab.TAB_CHAT, column_idx)
263
262
  )
264
263
 
265
264
  # at least one chat tab must be open
266
265
  if self.window.core.tabs.count_by_type(Tab.TAB_CHAT) > 1:
267
- context_menu.addAction(actions['close'])
268
- context_menu.addAction(actions['close_all'])
266
+ context_menu.addAction(close_act)
267
+ context_menu.addAction(close_all_act)
269
268
 
270
269
  context_menu.exec(global_pos)
271
270
 
@@ -278,12 +277,11 @@ class OutputTabs(QTabWidget):
278
277
  :param global_pos: global position
279
278
  """
280
279
  context_menu = self.prepare_menu(index, column_idx)
281
- actions = {}
282
- actions['refresh'] = QAction(QIcon(":/icons/reload.svg"), trans('action.refresh'), self)
283
- actions['refresh'].triggered.connect(
280
+ refresh = QAction(icon(ICON_PATH_RELOAD), trans('action.refresh'), context_menu)
281
+ refresh.triggered.connect(
284
282
  lambda: self.window.controller.files.update_explorer()
285
283
  )
286
- context_menu.addAction(actions['refresh'])
284
+ context_menu.addAction(refresh)
287
285
  context_menu.exec(global_pos)
288
286
 
289
287
  def show_tool_menu(self, index: int, column_idx: int, global_pos):
@@ -295,12 +293,11 @@ class OutputTabs(QTabWidget):
295
293
  :param global_pos: global position
296
294
  """
297
295
  context_menu = self.prepare_menu(index, column_idx)
298
- actions = {}
299
- actions['close'] = QAction(QIcon(":/icons/close.svg"), trans('action.tab.close'), self)
300
- actions['close'].triggered.connect(
296
+ close_act = QAction(icon(ICON_PATH_CLOSE), trans('action.tab.close'), context_menu)
297
+ close_act.triggered.connect(
301
298
  lambda: self.close_tab(index, column_idx)
302
299
  )
303
- context_menu.addAction(actions['close'])
300
+ context_menu.addAction(close_act)
304
301
  context_menu.exec(global_pos)
305
302
 
306
303
  def show_default_menu(self, index: int, column_idx: int, global_pos):
@@ -314,6 +311,26 @@ class OutputTabs(QTabWidget):
314
311
  context_menu = self.prepare_menu(index, column_idx)
315
312
  context_menu.exec(global_pos)
316
313
 
314
+ @Slot(int)
315
+ def _on_current_changed(self, _idx: int):
316
+ self.window.controller.ui.tabs.on_tab_changed(self.currentIndex(), self.column.get_idx())
317
+
318
+ @Slot(int)
319
+ def _on_tabbar_clicked(self, _idx: int):
320
+ self.window.controller.ui.tabs.on_tab_clicked(self.currentIndex(), self.column.get_idx())
321
+
322
+ @Slot(int)
323
+ def _on_tabbar_dbl_clicked(self, _idx: int):
324
+ self.window.controller.ui.tabs.on_tab_dbl_clicked(self.currentIndex(), self.column.get_idx())
325
+
326
+ @Slot(int)
327
+ def _on_tab_close_requested(self, _idx: int):
328
+ self.window.controller.ui.tabs.on_tab_closed(self.currentIndex(), self.column.get_idx())
329
+
330
+ @Slot(int, int)
331
+ def _on_tab_moved(self, _from: int, _to: int):
332
+ self.window.controller.ui.tabs.on_tab_moved(self.currentIndex(), self.column.get_idx())
333
+
317
334
  @Slot()
318
335
  def rename_tab(self, index: int, column_idx: int):
319
336
  """
@@ -354,9 +371,14 @@ class OutputTabs(QTabWidget):
354
371
  :param type: type
355
372
  :param tool_id: tool id
356
373
  """
374
+ if index == -2: # new btn
375
+ index = self.window.core.tabs.get_max_idx_by_column(column_idx)
376
+ if index == -1:
377
+ index = 0
378
+
357
379
  self.window.controller.ui.tabs.append(
358
380
  type=type,
359
381
  tool_id=tool_id,
360
382
  idx=index,
361
383
  column_idx=column_idx,
362
- )
384
+ )
@@ -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: 2025.01.19 02:00:00 #
9
+ # Updated Date: 2025.08.24 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import Qt
@@ -16,7 +16,6 @@ from PySide6.QtWidgets import QTextEdit
16
16
  from pygpt_net.core.tabs.tab import Tab
17
17
  from pygpt_net.core.text.finder import Finder
18
18
  from pygpt_net.utils import trans
19
- import pygpt_net.icons_rc
20
19
 
21
20
 
22
21
  class CalendarNote(QTextEdit):
@@ -6,27 +6,32 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.04.11 16:00:00 #
9
+ # Updated Date: 2025.08.24 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import Qt
13
- from PySide6.QtGui import QAction, QIcon, QKeySequence, QTextCursor, QFontMetrics
13
+ from PySide6.QtGui import QAction, QIcon, QKeySequence, QFontMetrics
14
14
  from PySide6.QtWidgets import QTextEdit
15
15
 
16
16
  from pygpt_net.core.text.finder import Finder
17
17
  from pygpt_net.utils import trans
18
18
 
19
- import pygpt_net.icons_rc
20
-
21
19
 
22
20
  class BaseCodeEditor(QTextEdit):
21
+
22
+ _ICON_VOLUME = QIcon(":/icons/volume.svg")
23
+ _ICON_SAVE = QIcon(":/icons/save.svg")
24
+ _ICON_SEARCH = QIcon(":/icons/search.svg")
25
+ _ICON_CLOSE = QIcon(":/icons/close.svg")
26
+ _FIND_SEQ = QKeySequence("Ctrl+F")
27
+
23
28
  def __init__(self, window=None):
24
29
  """
25
30
  Base code editor
26
31
 
27
32
  :param window: main window
28
33
  """
29
- super(BaseCodeEditor, self).__init__(window)
34
+ super().__init__(window)
30
35
  self.window = window
31
36
  self.finder = Finder(window, self)
32
37
  self.setReadOnly(True)
@@ -40,114 +45,89 @@ class BaseCodeEditor(QTextEdit):
40
45
  self.excluded_copy_to = []
41
46
  self.textChanged.connect(self.text_changed)
42
47
 
43
- # tabulation
44
48
  metrics = QFontMetrics(self.font())
45
49
  space_width = metrics.horizontalAdvance(" ")
46
50
  self.setTabStopDistance(4 * space_width)
47
51
 
48
52
  def text_changed(self):
49
- """On text changed"""
50
53
  self.finder.text_changed()
51
54
 
52
55
  def update_stylesheet(self, data: str):
53
- """
54
- Update stylesheet
55
-
56
- :param data: stylesheet CSS
57
- """
58
56
  self.setStyleSheet(self.default_stylesheet + data)
59
57
 
60
58
  def contextMenuEvent(self, event):
61
- """
62
- Context menu event
63
-
64
- :param event: Event
65
- """
66
59
  menu = self.createStandardContextMenu()
67
- selected_text = self.textCursor().selectedText()
60
+ cursor = self.textCursor()
61
+ selected_text = cursor.selectedText()
68
62
 
69
63
  if selected_text:
70
- # plain text
71
- plain_text = self.textCursor().selection().toPlainText()
64
+ plain_text = cursor.selection().toPlainText()
72
65
 
73
- # audio read
74
- action = QAction(QIcon(":/icons/volume.svg"), trans('text.context_menu.audio.read'), self)
66
+ action = QAction(self._ICON_VOLUME, trans('text.context_menu.audio.read'), menu)
75
67
  action.triggered.connect(self.audio_read_selection)
76
68
  menu.addAction(action)
77
69
 
78
- # copy to
79
70
  copy_to_menu = self.window.ui.context_menu.get_copy_to_menu(
80
- self,
71
+ menu,
81
72
  selected_text,
82
73
  excluded=self.excluded_copy_to
83
74
  )
75
+ try:
76
+ copy_to_menu.setParent(menu)
77
+ except Exception:
78
+ pass
84
79
  menu.addMenu(copy_to_menu)
85
80
 
86
- # save as (selected)
87
- action = QAction(QIcon(":/icons/save.svg"), trans('action.save_selection_as'), self)
81
+ action = QAction(self._ICON_SAVE, trans('action.save_selection_as'), menu)
88
82
  action.triggered.connect(
89
83
  lambda: self.window.controller.chat.common.save_text(plain_text))
90
84
  menu.addAction(action)
91
85
  else:
92
- # save as (all)
93
- action = QAction(QIcon(":/icons/save.svg"), trans('action.save_as'), self)
86
+ action = QAction(self._ICON_SAVE, trans('action.save_as'), menu)
94
87
  action.triggered.connect(
95
88
  lambda: self.window.controller.chat.common.save_text(self.toPlainText()))
96
89
  menu.addAction(action)
97
90
 
98
- action = QAction(QIcon(":/icons/search.svg"), trans('text.context_menu.find'), self)
91
+ action = QAction(self._ICON_SEARCH, trans('text.context_menu.find'), menu)
99
92
  action.triggered.connect(self.find_open)
100
- action.setShortcut(QKeySequence("Ctrl+F"))
93
+ action.setShortcut(self._FIND_SEQ)
101
94
  menu.addAction(action)
102
95
 
103
- # clear
104
- action = QAction(QIcon(":/icons/close.svg"), trans('action.clear'), self)
105
- action.triggered.connect(
106
- lambda: self.clear_content())
96
+ action = QAction(self._ICON_CLOSE, trans('action.clear'), menu)
97
+ action.triggered.connect(self.clear_content)
107
98
  menu.addAction(action)
108
99
 
109
- menu.exec_(event.globalPos())
100
+ menu.exec(event.globalPos())
101
+ menu.deleteLater()
110
102
 
111
103
  def clear_content(self):
112
- """Clear content"""
113
- cursor = self.textCursor()
114
- cursor.select(QTextCursor.Document)
115
- cursor.removeSelectedText()
104
+ self.clear()
116
105
 
117
106
  def audio_read_selection(self):
118
- """Read selected text (audio)"""
119
107
  self.window.controller.audio.read_text(self.textCursor().selectedText())
120
108
 
121
109
  def find_open(self):
122
- """Open find dialog"""
123
110
  self.window.controller.finder.open(self.finder)
124
111
 
125
112
  def on_update(self):
126
- """On content update"""
127
- self.finder.clear() # clear finder
113
+ self.finder.clear()
128
114
 
129
115
  def on_destroy(self):
130
- """On destroy"""
131
- self.window.controller.finder.unset(self.finder) # unregister finder from memory
116
+ try:
117
+ self.textChanged.disconnect(self.text_changed)
118
+ except Exception:
119
+ pass
120
+ self.window.controller.finder.unset(self.finder)
132
121
 
133
122
  def keyPressEvent(self, e):
134
- """
135
- Key press event
136
-
137
- :param e: Event
138
- """
139
123
  if e.key() == Qt.Key_F and e.modifiers() & Qt.ControlModifier:
140
124
  self.find_open()
141
125
  else:
142
- super(BaseCodeEditor, self).keyPressEvent(e)
126
+ super().keyPressEvent(e)
143
127
 
144
128
  def wheelEvent(self, event):
145
- """
146
- Wheel event: set font size
147
-
148
- :param event: Event
149
- """
150
129
  if event.modifiers() & Qt.ControlModifier:
130
+ prev = self.value
151
131
  if event.angleDelta().y() > 0:
152
132
  if self.value < self.max_font_size:
153
133
  self.value += 1
@@ -155,18 +135,14 @@ class BaseCodeEditor(QTextEdit):
155
135
  if self.value > self.min_font_size:
156
136
  self.value -= 1
157
137
 
158
- self.update_stylesheet(f"QTextEdit {{ font-size: {self.value}px }};")
138
+ if self.value != prev:
139
+ self.update_stylesheet(f"QTextEdit {{ font-size: {self.value}px }};")
159
140
  event.accept()
160
141
  else:
161
- super(BaseCodeEditor, self).wheelEvent(event)
142
+ super().wheelEvent(event)
162
143
 
163
144
  def focusInEvent(self, e):
164
- """
165
- Focus in event
166
-
167
- :param e: focus event
168
- """
169
- super(BaseCodeEditor, self).focusInEvent(e)
145
+ super().focusInEvent(e)
170
146
  self.window.controller.finder.focus_in(self.finder)
171
147
 
172
148
 
@@ -177,12 +153,4 @@ class CodeEditor(BaseCodeEditor):
177
153
 
178
154
  :param window: main window
179
155
  """
180
- super(CodeEditor, self).__init__(window)
181
- self.window = window
182
- self.setReadOnly(True)
183
- self.value = 12
184
- self.max_font_size = 42
185
- self.min_font_size = 8
186
- self.setProperty('class', 'code-editor')
187
- self.default_stylesheet = ""
188
- self.setStyleSheet(self.default_stylesheet)
156
+ super().__init__(window)
@@ -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: 2024.04.10 23:00:00 #
9
+ # Updated Date: 2025.08.24 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6 import QtCore
@@ -21,13 +21,15 @@ class FindInput(QLineEdit):
21
21
  :param window: main window
22
22
  :param id: info window id
23
23
  """
24
- super(FindInput, self).__init__(window)
24
+ super().__init__(window)
25
25
 
26
26
  self.window = window
27
27
  self.id = id
28
- self.textChanged.connect(
29
- lambda: self.window.controller.finder.search_text_changed(self.text()),
30
- )
28
+ self.textChanged.connect(self._on_text_changed)
29
+
30
+ @QtCore.Slot(str)
31
+ def _on_text_changed(self, text):
32
+ self.window.controller.finder.search_text_changed(text)
31
33
 
32
34
  def keyPressEvent(self, event):
33
35
  """
@@ -35,10 +37,10 @@ class FindInput(QLineEdit):
35
37
 
36
38
  :param event: key event
37
39
  """
38
- super(FindInput, self).keyPressEvent(event)
40
+ super().keyPressEvent(event)
39
41
 
40
42
  # update on Enter
41
- if event.key() == QtCore.Qt.Key_Return or event.key() == QtCore.Qt.Key_Enter:
43
+ if event.key() in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
42
44
  self.window.controller.finder.focus_input(self.text())
43
45
 
44
46
  def focusInEvent(self, e):
@@ -47,6 +49,5 @@ class FindInput(QLineEdit):
47
49
 
48
50
  :param e: focus event
49
51
  """
50
- super(FindInput, self).focusInEvent(e)
51
- self.window.controller.finder.focus_input(self.text())
52
-
52
+ super().focusInEvent(e)
53
+ self.window.controller.finder.focus_input(self.text())
@@ -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: 2025.08.19 07:00:00 #
9
+ # Updated Date: 2025.08.24 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import re
@@ -15,16 +15,13 @@ from PySide6.QtCore import Qt, QObject, Signal, Slot, QEvent
15
15
  from PySide6.QtWebChannel import QWebChannel
16
16
  from PySide6.QtWebEngineCore import QWebEngineSettings, QWebEnginePage
17
17
  from PySide6.QtWebEngineWidgets import QWebEngineView
18
- from PySide6.QtGui import QAction, QIcon, QKeySequence
18
+ from PySide6.QtGui import QAction, QIcon
19
19
  from PySide6.QtWidgets import QMenu
20
20
 
21
- from pygpt_net.core.events import RenderEvent
22
21
  from pygpt_net.item.ctx import CtxMeta
23
22
  from pygpt_net.core.text.web_finder import WebFinder
24
23
  from pygpt_net.utils import trans
25
24
 
26
- import pygpt_net.icons_rc
27
-
28
25
 
29
26
  class HtmlOutput(QWebEngineView):
30
27
  def __init__(self, window=None):
@@ -142,7 +139,7 @@ class HtmlOutput(QWebEngineView):
142
139
  menu.addAction(action)
143
140
 
144
141
  # copy to
145
- copy_to_menu = self.window.ui.context_menu.get_copy_to_menu(self, selected_text)
142
+ copy_to_menu = self.window.ui.context_menu.get_copy_to_menu(menu, selected_text)
146
143
  menu.addMenu(copy_to_menu)
147
144
 
148
145
  # save as (selected)