pygpt-net 2.7.4__py3-none-any.whl → 2.7.6__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 (159) hide show
  1. pygpt_net/CHANGELOG.txt +15 -0
  2. pygpt_net/__init__.py +4 -4
  3. pygpt_net/app_core.py +4 -2
  4. pygpt_net/controller/__init__.py +5 -1
  5. pygpt_net/controller/assistant/assistant.py +1 -4
  6. pygpt_net/controller/assistant/batch.py +5 -504
  7. pygpt_net/controller/assistant/editor.py +5 -5
  8. pygpt_net/controller/assistant/files.py +16 -16
  9. pygpt_net/controller/chat/handler/google_stream.py +307 -1
  10. pygpt_net/controller/chat/handler/worker.py +10 -25
  11. pygpt_net/controller/chat/handler/xai_stream.py +621 -52
  12. pygpt_net/controller/chat/image.py +2 -2
  13. pygpt_net/controller/debug/fixtures.py +3 -2
  14. pygpt_net/controller/dialogs/confirm.py +73 -101
  15. pygpt_net/controller/files/files.py +65 -4
  16. pygpt_net/controller/lang/mapping.py +9 -9
  17. pygpt_net/controller/painter/capture.py +50 -1
  18. pygpt_net/controller/presets/presets.py +2 -1
  19. pygpt_net/controller/remote_store/__init__.py +12 -0
  20. pygpt_net/{provider/core/assistant_file/db_sqlite → controller/remote_store/google}/__init__.py +2 -2
  21. pygpt_net/controller/remote_store/google/batch.py +402 -0
  22. pygpt_net/controller/remote_store/google/store.py +615 -0
  23. pygpt_net/controller/remote_store/openai/__init__.py +12 -0
  24. pygpt_net/controller/remote_store/openai/batch.py +524 -0
  25. pygpt_net/controller/{assistant → remote_store/openai}/store.py +63 -60
  26. pygpt_net/controller/remote_store/remote_store.py +35 -0
  27. pygpt_net/controller/ui/ui.py +20 -1
  28. pygpt_net/core/assistants/assistants.py +3 -15
  29. pygpt_net/core/db/database.py +5 -3
  30. pygpt_net/core/filesystem/url.py +4 -1
  31. pygpt_net/core/locale/placeholder.py +35 -0
  32. pygpt_net/core/remote_store/__init__.py +12 -0
  33. pygpt_net/core/remote_store/google/__init__.py +11 -0
  34. pygpt_net/core/remote_store/google/files.py +224 -0
  35. pygpt_net/core/remote_store/google/store.py +248 -0
  36. pygpt_net/core/remote_store/openai/__init__.py +11 -0
  37. pygpt_net/core/{assistants → remote_store/openai}/files.py +26 -19
  38. pygpt_net/core/{assistants → remote_store/openai}/store.py +32 -15
  39. pygpt_net/core/remote_store/remote_store.py +24 -0
  40. pygpt_net/core/render/web/body.py +3 -2
  41. pygpt_net/core/types/chunk.py +27 -0
  42. pygpt_net/data/config/config.json +8 -4
  43. pygpt_net/data/config/models.json +77 -3
  44. pygpt_net/data/config/settings.json +45 -0
  45. pygpt_net/data/js/app/template.js +1 -1
  46. pygpt_net/data/js/app.min.js +2 -2
  47. pygpt_net/data/locale/locale.de.ini +44 -41
  48. pygpt_net/data/locale/locale.en.ini +56 -43
  49. pygpt_net/data/locale/locale.es.ini +44 -41
  50. pygpt_net/data/locale/locale.fr.ini +44 -41
  51. pygpt_net/data/locale/locale.it.ini +44 -41
  52. pygpt_net/data/locale/locale.pl.ini +45 -42
  53. pygpt_net/data/locale/locale.uk.ini +44 -41
  54. pygpt_net/data/locale/locale.zh.ini +44 -41
  55. pygpt_net/data/locale/plugin.cmd_history.de.ini +1 -1
  56. pygpt_net/data/locale/plugin.cmd_history.en.ini +1 -1
  57. pygpt_net/data/locale/plugin.cmd_history.es.ini +1 -1
  58. pygpt_net/data/locale/plugin.cmd_history.fr.ini +1 -1
  59. pygpt_net/data/locale/plugin.cmd_history.it.ini +1 -1
  60. pygpt_net/data/locale/plugin.cmd_history.pl.ini +1 -1
  61. pygpt_net/data/locale/plugin.cmd_history.uk.ini +1 -1
  62. pygpt_net/data/locale/plugin.cmd_history.zh.ini +1 -1
  63. pygpt_net/data/locale/plugin.cmd_mouse_control.en.ini +14 -0
  64. pygpt_net/data/locale/plugin.cmd_web.de.ini +1 -1
  65. pygpt_net/data/locale/plugin.cmd_web.en.ini +1 -1
  66. pygpt_net/data/locale/plugin.cmd_web.es.ini +1 -1
  67. pygpt_net/data/locale/plugin.cmd_web.fr.ini +1 -1
  68. pygpt_net/data/locale/plugin.cmd_web.it.ini +1 -1
  69. pygpt_net/data/locale/plugin.cmd_web.pl.ini +1 -1
  70. pygpt_net/data/locale/plugin.cmd_web.uk.ini +1 -1
  71. pygpt_net/data/locale/plugin.cmd_web.zh.ini +1 -1
  72. pygpt_net/data/locale/plugin.idx_llama_index.de.ini +2 -2
  73. pygpt_net/data/locale/plugin.idx_llama_index.en.ini +2 -2
  74. pygpt_net/data/locale/plugin.idx_llama_index.es.ini +2 -2
  75. pygpt_net/data/locale/plugin.idx_llama_index.fr.ini +2 -2
  76. pygpt_net/data/locale/plugin.idx_llama_index.it.ini +2 -2
  77. pygpt_net/data/locale/plugin.idx_llama_index.pl.ini +2 -2
  78. pygpt_net/data/locale/plugin.idx_llama_index.uk.ini +2 -2
  79. pygpt_net/data/locale/plugin.idx_llama_index.zh.ini +2 -2
  80. pygpt_net/item/assistant.py +1 -211
  81. pygpt_net/item/ctx.py +3 -3
  82. pygpt_net/item/store.py +238 -0
  83. pygpt_net/js_rc.py +2449 -2447
  84. pygpt_net/migrations/Version20260102190000.py +35 -0
  85. pygpt_net/migrations/__init__.py +3 -1
  86. pygpt_net/plugin/cmd_mouse_control/config.py +471 -1
  87. pygpt_net/plugin/cmd_mouse_control/plugin.py +487 -22
  88. pygpt_net/plugin/cmd_mouse_control/worker.py +464 -87
  89. pygpt_net/plugin/cmd_mouse_control/worker_sandbox.py +729 -0
  90. pygpt_net/plugin/idx_llama_index/config.py +2 -2
  91. pygpt_net/provider/api/anthropic/__init__.py +10 -8
  92. pygpt_net/provider/api/google/__init__.py +21 -58
  93. pygpt_net/provider/api/google/chat.py +545 -129
  94. pygpt_net/provider/api/google/computer.py +190 -0
  95. pygpt_net/provider/api/google/realtime/realtime.py +2 -2
  96. pygpt_net/provider/api/google/remote_tools.py +93 -0
  97. pygpt_net/provider/api/google/store.py +546 -0
  98. pygpt_net/provider/api/google/worker/__init__.py +0 -0
  99. pygpt_net/provider/api/google/worker/importer.py +392 -0
  100. pygpt_net/provider/api/openai/__init__.py +7 -3
  101. pygpt_net/provider/api/openai/computer.py +10 -1
  102. pygpt_net/provider/api/openai/responses.py +0 -0
  103. pygpt_net/provider/api/openai/store.py +6 -6
  104. pygpt_net/provider/api/openai/worker/importer.py +24 -24
  105. pygpt_net/provider/api/x_ai/__init__.py +10 -9
  106. pygpt_net/provider/api/x_ai/chat.py +272 -102
  107. pygpt_net/provider/core/config/patch.py +16 -1
  108. pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +3 -3
  109. pygpt_net/provider/core/model/patch.py +17 -3
  110. pygpt_net/provider/core/preset/json_file.py +13 -7
  111. pygpt_net/provider/core/{assistant_file → remote_file}/__init__.py +1 -1
  112. pygpt_net/provider/core/{assistant_file → remote_file}/base.py +9 -9
  113. pygpt_net/provider/core/remote_file/db_sqlite/__init__.py +12 -0
  114. pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/patch.py +1 -1
  115. pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/provider.py +23 -20
  116. pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/storage.py +35 -27
  117. pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/utils.py +5 -4
  118. pygpt_net/provider/core/{assistant_store → remote_store}/__init__.py +1 -1
  119. pygpt_net/provider/core/{assistant_store → remote_store}/base.py +10 -10
  120. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/__init__.py +1 -1
  121. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/patch.py +1 -1
  122. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/provider.py +16 -15
  123. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/storage.py +30 -23
  124. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/utils.py +5 -4
  125. pygpt_net/provider/core/{assistant_store → remote_store}/json_file.py +9 -9
  126. pygpt_net/provider/llms/google.py +2 -2
  127. pygpt_net/tools/image_viewer/ui/dialogs.py +298 -12
  128. pygpt_net/tools/text_editor/ui/widgets.py +5 -1
  129. pygpt_net/ui/base/config_dialog.py +3 -2
  130. pygpt_net/ui/base/context_menu.py +44 -1
  131. pygpt_net/ui/dialog/assistant.py +3 -3
  132. pygpt_net/ui/dialog/plugins.py +3 -1
  133. pygpt_net/ui/dialog/remote_store_google.py +539 -0
  134. pygpt_net/ui/dialog/{assistant_store.py → remote_store_openai.py} +95 -95
  135. pygpt_net/ui/dialogs.py +5 -3
  136. pygpt_net/ui/layout/chat/attachments_uploaded.py +3 -3
  137. pygpt_net/ui/layout/toolbox/computer_env.py +26 -8
  138. pygpt_net/ui/layout/toolbox/indexes.py +22 -19
  139. pygpt_net/ui/layout/toolbox/model.py +28 -5
  140. pygpt_net/ui/menu/tools.py +13 -5
  141. pygpt_net/ui/widget/dialog/remote_store_google.py +56 -0
  142. pygpt_net/ui/widget/dialog/{assistant_store.py → remote_store_openai.py} +9 -9
  143. pygpt_net/ui/widget/element/button.py +4 -4
  144. pygpt_net/ui/widget/image/display.py +25 -8
  145. pygpt_net/ui/widget/lists/remote_store_google.py +248 -0
  146. pygpt_net/ui/widget/lists/{assistant_store.py → remote_store_openai.py} +21 -21
  147. pygpt_net/ui/widget/option/checkbox_list.py +47 -9
  148. pygpt_net/ui/widget/option/combo.py +39 -3
  149. pygpt_net/ui/widget/tabs/output.py +9 -1
  150. pygpt_net/ui/widget/textarea/editor.py +14 -1
  151. pygpt_net/ui/widget/textarea/input.py +20 -7
  152. pygpt_net/ui/widget/textarea/notepad.py +24 -1
  153. pygpt_net/ui/widget/textarea/output.py +23 -1
  154. pygpt_net/ui/widget/textarea/web.py +16 -1
  155. {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.6.dist-info}/METADATA +41 -2
  156. {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.6.dist-info}/RECORD +158 -132
  157. {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.6.dist-info}/LICENSE +0 -0
  158. {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.6.dist-info}/WHEEL +0 -0
  159. {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.6.dist-info}/entry_points.txt +0 -0
@@ -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: 2025.08.24 23:00:00 #
9
+ # Updated Date: 2026.01.03 17:00:00 #
10
10
  # ================================================== #
11
-
12
- from PySide6.QtWidgets import QVBoxLayout, QWidget
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.addWidget(combo)
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,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.24 23:00:00 #
9
+ # Updated Date: 2026.01.02 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtGui import QAction, QIcon
@@ -26,8 +26,11 @@ class Tools:
26
26
  action = self.window.sender()
27
27
  self.window.controller.tools.open_tab(action.data())
28
28
 
29
- def _toggle_assistant_store(self, checked=False):
30
- self.window.controller.assistant.store.toggle_editor()
29
+ def _toggle_remote_store_openai(self, checked=False):
30
+ self.window.controller.remote_store.openai.toggle_editor()
31
+
32
+ def _toggle_remote_store_google(self, checked=False):
33
+ self.window.controller.remote_store.google.toggle_editor()
31
34
 
32
35
  def _rebuild_ipython(self, checked=False):
33
36
  self.window.core.plugins.get("cmd_code_interpreter").builder.build_and_restart()
@@ -69,9 +72,14 @@ class Tools:
69
72
 
70
73
  menu_tools.addSeparator()
71
74
  db_icon = QIcon(":/icons/db.svg")
72
- ui_menu['menu.tools.openai.stores'] = QAction(db_icon, trans("dialog.assistant.store"), window)
75
+ ui_menu['menu.tools.openai.stores'] = QAction(db_icon, trans("dialog.remote_store.openai"), window)
73
76
  menu_tools.addAction(ui_menu['menu.tools.openai.stores'])
74
- ui_menu['menu.tools.openai.stores'].triggered.connect(self._toggle_assistant_store)
77
+ ui_menu['menu.tools.openai.stores'].triggered.connect(self._toggle_remote_store_openai)
78
+
79
+ ui_menu['menu.tools.google.stores'] = QAction(db_icon, trans("dialog.remote_store.google"), window)
80
+ menu_tools.addAction(ui_menu['menu.tools.google.stores'])
81
+ ui_menu['menu.tools.google.stores'].triggered.connect(self._toggle_remote_store_google)
82
+ # ------------------------------------------------- #
75
83
 
76
84
  menu_tools.addSeparator()
77
85
  reload_icon = QIcon(":/icons/reload.svg")
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # ================================================== #
4
+ # This file is a part of PYGPT package #
5
+ # Website: https://pygpt.net #
6
+ # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
+ # MIT License #
8
+ # Created By : Marcin Szczygliński #
9
+ # Updated Date: 2026.01.02 20:00:00 #
10
+ # ================================================== #
11
+
12
+ from PySide6.QtCore import Qt
13
+
14
+ from .base import BaseDialog
15
+
16
+
17
+ class RemoteStoreGoogleDialog(BaseDialog):
18
+ def __init__(self, window=None, id=None):
19
+ """
20
+ Models dialog (Google File Search)
21
+
22
+ :param window: main window
23
+ :param id: settings id
24
+ """
25
+ super(RemoteStoreGoogleDialog, self).__init__(window, id)
26
+ self.window = window
27
+ self.id = id
28
+
29
+ def closeEvent(self, event):
30
+ """
31
+ Close event
32
+
33
+ :param event: close event
34
+ """
35
+ self.window.controller.remote_store.google.dialog = False
36
+ self.window.controller.remote_store.google.update()
37
+ super(RemoteStoreGoogleDialog, self).closeEvent(event)
38
+
39
+ def keyPressEvent(self, event):
40
+ """
41
+ Key press event
42
+
43
+ :param event: key press event
44
+ """
45
+ if event.key() == Qt.Key_Escape:
46
+ self.cleanup()
47
+ self.close()
48
+ else:
49
+ super(RemoteStoreGoogleDialog, self).keyPressEvent(event)
50
+
51
+ def cleanup(self):
52
+ """
53
+ Cleanup on close
54
+ """
55
+ self.window.controller.remote_store.google.dialog = False
56
+ self.window.controller.remote_store.google.update()
@@ -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.26 23:00:00 #
9
+ # Updated Date: 2026.01.02 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import Qt
@@ -14,7 +14,7 @@ from PySide6.QtCore import Qt
14
14
  from .base import BaseDialog
15
15
 
16
16
 
17
- class AssistantVectorStoreDialog(BaseDialog):
17
+ class RemoteStoreOpenAIDialog(BaseDialog):
18
18
  def __init__(self, window=None, id=None):
19
19
  """
20
20
  Models dialog
@@ -22,7 +22,7 @@ class AssistantVectorStoreDialog(BaseDialog):
22
22
  :param window: main window
23
23
  :param id: settings id
24
24
  """
25
- super(AssistantVectorStoreDialog, self).__init__(window, id)
25
+ super(RemoteStoreOpenAIDialog, self).__init__(window, id)
26
26
  self.window = window
27
27
  self.id = id
28
28
 
@@ -32,9 +32,9 @@ class AssistantVectorStoreDialog(BaseDialog):
32
32
 
33
33
  :param event: close event
34
34
  """
35
- self.window.controller.assistant.store.dialog = False
36
- self.window.controller.assistant.store.update()
37
- super(AssistantVectorStoreDialog, self).closeEvent(event)
35
+ self.window.controller.remote_store.openai.dialog = False
36
+ self.window.controller.remote_store.openai.update()
37
+ super(RemoteStoreOpenAIDialog, self).closeEvent(event)
38
38
 
39
39
  def keyPressEvent(self, event):
40
40
  """
@@ -46,11 +46,11 @@ class AssistantVectorStoreDialog(BaseDialog):
46
46
  self.cleanup()
47
47
  self.close() # close dialog when the Esc key is pressed.
48
48
  else:
49
- super(AssistantVectorStoreDialog, self).keyPressEvent(event)
49
+ super(RemoteStoreOpenAIDialog, self).keyPressEvent(event)
50
50
 
51
51
  def cleanup(self):
52
52
  """
53
53
  Cleanup on close
54
54
  """
55
- self.window.controller.assistant.store.dialog = False
56
- self.window.controller.assistant.store.update()
55
+ self.window.controller.remote_store.openai.dialog = False
56
+ self.window.controller.remote_store.openai.update()
@@ -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.24 23:00:00 #
9
+ # Updated Date: 2026.01.02 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import Qt
@@ -87,7 +87,7 @@ class SyncButton(QPushButton):
87
87
  super().__init__(title)
88
88
  self.window = window
89
89
  self.clicked.connect(
90
- lambda: self.window.controller.assistant.batch.import_files_current()
90
+ lambda: self.window.controller.remote_store.openai.batch.import_files_current()
91
91
  )
92
92
 
93
93
  @classmethod
@@ -112,11 +112,11 @@ class SyncButton(QPushButton):
112
112
  menu = QMenu(parent)
113
113
  act_current = menu.addAction(type(self)._icon_download, trans('attachments_uploaded.btn.sync.current'))
114
114
  act_current.triggered.connect(
115
- lambda checked=False: self.window.controller.assistant.batch.import_files_current()
115
+ lambda checked=False: self.window.controller.remote_store.openai.batch.import_files_current()
116
116
  )
117
117
  act_all = menu.addAction(type(self)._icon_download, trans('attachments_uploaded.btn.sync.all'))
118
118
  act_all.triggered.connect(
119
- lambda checked=False: self.window.controller.assistant.batch.import_files()
119
+ lambda checked=False: self.window.controller.remote_store.openai.batch.import_files()
120
120
  )
121
121
  menu.exec_(parent.mapToGlobal(pos))
122
122
  menu.deleteLater()
@@ -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.24 23:00:00 #
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: self.window.controller.files.use_attachment(self.path),
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 self.window.core.filesystem.actions.has_use(self.path):
73
- extra_use_actions = self.window.core.filesystem.actions.get_use(self, self.path)
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.window.tools.get("viewer").open(self.path)
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.window.tools.get("viewer").open_dir(self.path)
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.window.tools.get("viewer").save(self.path)
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.window.tools.get("viewer").delete(self.path)
140
+ win = self._get_window()
141
+ win.tools.get("viewer").delete(self.path)
@@ -0,0 +1,248 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # ================================================== #
4
+ # This file is a part of PYGPT package #
5
+ # Website: https://pygpt.net #
6
+ # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
+ # MIT License #
8
+ # Created By : Marcin Szczygliński #
9
+ # Updated Date: 2026.01.02 20:00:00 #
10
+ # ================================================== #
11
+
12
+ from PySide6.QtGui import QAction, QIcon
13
+ from PySide6.QtCore import Qt, QItemSelectionModel
14
+ from PySide6.QtWidgets import QMenu, QAbstractItemView
15
+
16
+ from pygpt_net.ui.widget.lists.base import BaseList
17
+ from pygpt_net.utils import trans
18
+ import pygpt_net.icons_rc
19
+
20
+
21
+ class RemoteStoreGoogleEditorList(BaseList):
22
+ def __init__(self, window=None, id=None):
23
+ """
24
+ Store select menu (in editor) - Google File Search
25
+
26
+ :param window: main window
27
+ :param id: parent id
28
+ """
29
+ super(RemoteStoreGoogleEditorList, self).__init__(window)
30
+ self.window = window
31
+ self.id = id
32
+
33
+ self._suppress_item_click = False
34
+ self._ctrl_multi_active = False
35
+ self._ctrl_multi_index = None
36
+ self._was_shift_click = False
37
+
38
+ self._backup_selection = None
39
+ self.restore_after_ctx_menu = True
40
+
41
+ self.setSelectionBehavior(QAbstractItemView.SelectRows)
42
+ self.setSelectionMode(QAbstractItemView.ExtendedSelection)
43
+
44
+ # Disable BaseList click handler; business action handled manually
45
+ self.clicked.disconnect(self.click)
46
+
47
+ def _selected_rows(self) -> list[int]:
48
+ try:
49
+ return sorted([ix.row() for ix in self.selectionModel().selectedRows()])
50
+ except Exception:
51
+ return []
52
+
53
+ def _has_multi_selection(self) -> bool:
54
+ try:
55
+ return len(self.selectionModel().selectedRows()) > 1
56
+ except Exception:
57
+ return False
58
+
59
+ def mousePressEvent(self, event):
60
+ if event.button() == Qt.LeftButton and (event.modifiers() & Qt.ControlModifier):
61
+ idx = self.indexAt(event.pos())
62
+ if idx.isValid():
63
+ self._ctrl_multi_active = True
64
+ self._ctrl_multi_index = idx
65
+ self._suppress_item_click = True
66
+ event.accept()
67
+ return
68
+ self._suppress_item_click = True
69
+ event.accept()
70
+ return
71
+
72
+ if event.button() == Qt.LeftButton and (event.modifiers() & Qt.ShiftModifier):
73
+ idx = self.indexAt(event.pos())
74
+ self._suppress_item_click = True
75
+ self._was_shift_click = True
76
+ if idx.isValid():
77
+ super(RemoteStoreGoogleEditorList, self).mousePressEvent(event)
78
+ else:
79
+ event.accept()
80
+ return
81
+
82
+ if event.button() == Qt.LeftButton:
83
+ idx = self.indexAt(event.pos())
84
+ if self._has_multi_selection():
85
+ sel_model = self.selectionModel()
86
+ sel_model.clearSelection()
87
+ if not idx.isValid():
88
+ event.accept()
89
+ return
90
+ super(RemoteStoreGoogleEditorList, self).mousePressEvent(event)
91
+ return
92
+
93
+ if event.button() == Qt.RightButton:
94
+ idx = self.indexAt(event.pos())
95
+ sel_model = self.selectionModel()
96
+ selected_rows = [ix.row() for ix in sel_model.selectedRows()]
97
+ multi = len(selected_rows) > 1
98
+
99
+ if idx.isValid():
100
+ if multi and idx.row() in selected_rows:
101
+ self._backup_selection = None
102
+ else:
103
+ self._backup_selection = list(sel_model.selectedIndexes())
104
+ sel_model.clearSelection()
105
+ sel_model.select(idx, QItemSelectionModel.Select | QItemSelectionModel.Rows)
106
+ event.accept()
107
+ return
108
+
109
+ super(RemoteStoreGoogleEditorList, self).mousePressEvent(event)
110
+
111
+ def mouseReleaseEvent(self, event):
112
+ if event.button() == Qt.LeftButton and self._was_shift_click:
113
+ self._was_shift_click = False
114
+ self._suppress_item_click = False
115
+ super(RemoteStoreGoogleEditorList, self).mouseReleaseEvent(event)
116
+ return
117
+
118
+ if event.button() == Qt.LeftButton and self._ctrl_multi_active:
119
+ try:
120
+ idx = self.indexAt(event.pos())
121
+ if idx.isValid() and self._ctrl_multi_index and idx == self._ctrl_multi_index:
122
+ sel_model = self.selectionModel()
123
+ sel_model.select(idx, QItemSelectionModel.Toggle | QItemSelectionModel.Rows)
124
+ finally:
125
+ self._ctrl_multi_active = False
126
+ self._ctrl_multi_index = None
127
+ self._suppress_item_click = False
128
+ event.accept()
129
+ return
130
+
131
+ if event.button() == Qt.LeftButton:
132
+ idx = self.indexAt(event.pos())
133
+ if not self._has_multi_selection():
134
+ if idx.isValid() and not self._suppress_item_click:
135
+ self.window.controller.remote_store.google.select(idx.row())
136
+ self._suppress_item_click = False
137
+ super(RemoteStoreGoogleEditorList, self).mouseReleaseEvent(event)
138
+ return
139
+
140
+ super(RemoteStoreGoogleEditorList, self).mouseReleaseEvent(event)
141
+
142
+ def click(self, val):
143
+ pass
144
+
145
+ def contextMenuEvent(self, event):
146
+ actions = {}
147
+ actions['refresh'] = QAction(
148
+ QIcon(":/icons/reload.svg"),
149
+ trans('dialog.remote_store.menu.current.refresh_store'),
150
+ self
151
+ )
152
+ actions['delete'] = QAction(QIcon(":/icons/delete.svg"), trans('action.delete'), self)
153
+ actions['clear'] = QAction(
154
+ QIcon(":/icons/close.svg"),
155
+ trans('dialog.remote_store.menu.current.clear_files'),
156
+ self
157
+ )
158
+ actions['truncate'] = QAction(
159
+ QIcon(":/icons/delete.svg"),
160
+ trans('dialog.remote_store.menu.current.truncate_files'),
161
+ self
162
+ )
163
+
164
+ menu = QMenu(self)
165
+ menu.addAction(actions['refresh'])
166
+ menu.addAction(actions['delete'])
167
+ menu.addAction(actions['clear'])
168
+ menu.addAction(actions['truncate'])
169
+
170
+ index = self.indexAt(event.pos())
171
+ idx = index.row() if index.isValid() else -1
172
+
173
+ selected_rows = self._selected_rows()
174
+ multi = len(selected_rows) > 1
175
+
176
+ if not index.isValid() and not multi:
177
+ if self._backup_selection is not None and self.restore_after_ctx_menu:
178
+ sel_model = self.selectionModel()
179
+ sel_model.clearSelection()
180
+ for i in self._backup_selection:
181
+ sel_model.select(i, QItemSelectionModel.Select | QItemSelectionModel.Rows)
182
+ self._backup_selection = None
183
+ return
184
+
185
+ if multi:
186
+ actions['refresh'].triggered.connect(lambda: self.action_refresh(list(selected_rows)))
187
+ actions['delete'].triggered.connect(lambda: self.action_delete(list(selected_rows)))
188
+ actions['clear'].triggered.connect(lambda: self.action_clear(list(selected_rows)))
189
+ actions['truncate'].triggered.connect(lambda: self.action_truncate(list(selected_rows)))
190
+ else:
191
+ actions['refresh'].triggered.connect(lambda: self.action_refresh(idx))
192
+ actions['delete'].triggered.connect(lambda: self.action_delete(idx))
193
+ actions['clear'].triggered.connect(lambda: self.action_clear(idx))
194
+ actions['truncate'].triggered.connect(lambda: self.action_truncate(idx))
195
+
196
+ menu.exec_(event.globalPos())
197
+
198
+ if self.restore_after_ctx_menu and self._backup_selection is not None:
199
+ sel_model = self.selectionModel()
200
+ sel_model.clearSelection()
201
+ for i in self._backup_selection:
202
+ sel_model.select(i, QItemSelectionModel.Select | QItemSelectionModel.Rows)
203
+ self._backup_selection = None
204
+ self.restore_after_ctx_menu = True
205
+
206
+ def action_delete(self, item):
207
+ if isinstance(item, (list, tuple)):
208
+ if item:
209
+ self.restore_after_ctx_menu = False
210
+ self.window.controller.remote_store.google.delete_by_idx(list(item))
211
+ return
212
+ idx = int(item)
213
+ if idx >= 0:
214
+ self.restore_after_ctx_menu = False
215
+ self.window.controller.remote_store.google.delete_by_idx(idx)
216
+
217
+ def action_clear(self, item):
218
+ if isinstance(item, (list, tuple)):
219
+ if item:
220
+ self.restore_after_ctx_menu = False
221
+ self.window.controller.remote_store.google.batch.clear_store_files_by_idx(list(item))
222
+ return
223
+ idx = int(item)
224
+ if idx >= 0:
225
+ self.restore_after_ctx_menu = False
226
+ self.window.controller.remote_store.google.batch.clear_store_files_by_idx(idx)
227
+
228
+ def action_truncate(self, item):
229
+ if isinstance(item, (list, tuple)):
230
+ if item:
231
+ self.restore_after_ctx_menu = False
232
+ self.window.controller.remote_store.google.batch.truncate_store_files_by_idx(list(item))
233
+ return
234
+ idx = int(item)
235
+ if idx >= 0:
236
+ self.restore_after_ctx_menu = False
237
+ self.window.controller.remote_store.google.batch.truncate_store_files_by_idx(idx)
238
+
239
+ def action_refresh(self, item):
240
+ if isinstance(item, (list, tuple)):
241
+ if item:
242
+ self.restore_after_ctx_menu = False
243
+ self.window.controller.remote_store.google.refresh_by_idx(list(item))
244
+ return
245
+ idx = int(item)
246
+ if idx >= 0:
247
+ self.restore_after_ctx_menu = False
248
+ self.window.controller.remote_store.google.refresh_by_idx(idx)