pygpt-net 2.7.4__py3-none-any.whl → 2.7.5__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 (133) hide show
  1. pygpt_net/CHANGELOG.txt +7 -0
  2. pygpt_net/__init__.py +3 -3
  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 +8 -1
  11. pygpt_net/controller/chat/image.py +2 -2
  12. pygpt_net/controller/dialogs/confirm.py +73 -101
  13. pygpt_net/controller/lang/mapping.py +9 -9
  14. pygpt_net/controller/painter/capture.py +50 -1
  15. pygpt_net/controller/presets/presets.py +2 -1
  16. pygpt_net/controller/remote_store/__init__.py +12 -0
  17. pygpt_net/{provider/core/assistant_file/db_sqlite → controller/remote_store/google}/__init__.py +2 -2
  18. pygpt_net/controller/remote_store/google/batch.py +402 -0
  19. pygpt_net/controller/remote_store/google/store.py +615 -0
  20. pygpt_net/controller/remote_store/openai/__init__.py +12 -0
  21. pygpt_net/controller/remote_store/openai/batch.py +524 -0
  22. pygpt_net/controller/{assistant → remote_store/openai}/store.py +63 -60
  23. pygpt_net/controller/remote_store/remote_store.py +35 -0
  24. pygpt_net/controller/ui/ui.py +20 -1
  25. pygpt_net/core/assistants/assistants.py +3 -15
  26. pygpt_net/core/db/database.py +5 -3
  27. pygpt_net/core/locale/placeholder.py +35 -0
  28. pygpt_net/core/remote_store/__init__.py +12 -0
  29. pygpt_net/core/remote_store/google/__init__.py +11 -0
  30. pygpt_net/core/remote_store/google/files.py +224 -0
  31. pygpt_net/core/remote_store/google/store.py +248 -0
  32. pygpt_net/core/remote_store/openai/__init__.py +11 -0
  33. pygpt_net/core/{assistants → remote_store/openai}/files.py +26 -19
  34. pygpt_net/core/{assistants → remote_store/openai}/store.py +32 -15
  35. pygpt_net/core/remote_store/remote_store.py +24 -0
  36. pygpt_net/data/config/config.json +8 -4
  37. pygpt_net/data/config/models.json +77 -3
  38. pygpt_net/data/config/settings.json +45 -0
  39. pygpt_net/data/locale/locale.de.ini +41 -41
  40. pygpt_net/data/locale/locale.en.ini +53 -43
  41. pygpt_net/data/locale/locale.es.ini +41 -41
  42. pygpt_net/data/locale/locale.fr.ini +41 -41
  43. pygpt_net/data/locale/locale.it.ini +41 -41
  44. pygpt_net/data/locale/locale.pl.ini +42 -42
  45. pygpt_net/data/locale/locale.uk.ini +41 -41
  46. pygpt_net/data/locale/locale.zh.ini +41 -41
  47. pygpt_net/data/locale/plugin.cmd_history.de.ini +1 -1
  48. pygpt_net/data/locale/plugin.cmd_history.en.ini +1 -1
  49. pygpt_net/data/locale/plugin.cmd_history.es.ini +1 -1
  50. pygpt_net/data/locale/plugin.cmd_history.fr.ini +1 -1
  51. pygpt_net/data/locale/plugin.cmd_history.it.ini +1 -1
  52. pygpt_net/data/locale/plugin.cmd_history.pl.ini +1 -1
  53. pygpt_net/data/locale/plugin.cmd_history.uk.ini +1 -1
  54. pygpt_net/data/locale/plugin.cmd_history.zh.ini +1 -1
  55. pygpt_net/data/locale/plugin.cmd_mouse_control.en.ini +14 -0
  56. pygpt_net/data/locale/plugin.cmd_web.de.ini +1 -1
  57. pygpt_net/data/locale/plugin.cmd_web.en.ini +1 -1
  58. pygpt_net/data/locale/plugin.cmd_web.es.ini +1 -1
  59. pygpt_net/data/locale/plugin.cmd_web.fr.ini +1 -1
  60. pygpt_net/data/locale/plugin.cmd_web.it.ini +1 -1
  61. pygpt_net/data/locale/plugin.cmd_web.pl.ini +1 -1
  62. pygpt_net/data/locale/plugin.cmd_web.uk.ini +1 -1
  63. pygpt_net/data/locale/plugin.cmd_web.zh.ini +1 -1
  64. pygpt_net/data/locale/plugin.idx_llama_index.de.ini +2 -2
  65. pygpt_net/data/locale/plugin.idx_llama_index.en.ini +2 -2
  66. pygpt_net/data/locale/plugin.idx_llama_index.es.ini +2 -2
  67. pygpt_net/data/locale/plugin.idx_llama_index.fr.ini +2 -2
  68. pygpt_net/data/locale/plugin.idx_llama_index.it.ini +2 -2
  69. pygpt_net/data/locale/plugin.idx_llama_index.pl.ini +2 -2
  70. pygpt_net/data/locale/plugin.idx_llama_index.uk.ini +2 -2
  71. pygpt_net/data/locale/plugin.idx_llama_index.zh.ini +2 -2
  72. pygpt_net/item/assistant.py +1 -211
  73. pygpt_net/item/ctx.py +3 -1
  74. pygpt_net/item/store.py +238 -0
  75. pygpt_net/migrations/Version20260102190000.py +35 -0
  76. pygpt_net/migrations/__init__.py +3 -1
  77. pygpt_net/plugin/cmd_mouse_control/config.py +470 -1
  78. pygpt_net/plugin/cmd_mouse_control/plugin.py +488 -22
  79. pygpt_net/plugin/cmd_mouse_control/worker.py +464 -87
  80. pygpt_net/plugin/cmd_mouse_control/worker_sandbox.py +729 -0
  81. pygpt_net/plugin/idx_llama_index/config.py +2 -2
  82. pygpt_net/provider/api/google/__init__.py +16 -54
  83. pygpt_net/provider/api/google/chat.py +546 -129
  84. pygpt_net/provider/api/google/computer.py +190 -0
  85. pygpt_net/provider/api/google/realtime/realtime.py +2 -2
  86. pygpt_net/provider/api/google/remote_tools.py +93 -0
  87. pygpt_net/provider/api/google/store.py +546 -0
  88. pygpt_net/provider/api/google/worker/__init__.py +0 -0
  89. pygpt_net/provider/api/google/worker/importer.py +392 -0
  90. pygpt_net/provider/api/openai/computer.py +10 -1
  91. pygpt_net/provider/api/openai/store.py +6 -6
  92. pygpt_net/provider/api/openai/worker/importer.py +24 -24
  93. pygpt_net/provider/core/config/patch.py +16 -1
  94. pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +3 -3
  95. pygpt_net/provider/core/model/patch.py +17 -3
  96. pygpt_net/provider/core/preset/json_file.py +13 -7
  97. pygpt_net/provider/core/{assistant_file → remote_file}/__init__.py +1 -1
  98. pygpt_net/provider/core/{assistant_file → remote_file}/base.py +9 -9
  99. pygpt_net/provider/core/remote_file/db_sqlite/__init__.py +12 -0
  100. pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/patch.py +1 -1
  101. pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/provider.py +23 -20
  102. pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/storage.py +35 -27
  103. pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/utils.py +5 -4
  104. pygpt_net/provider/core/{assistant_store → remote_store}/__init__.py +1 -1
  105. pygpt_net/provider/core/{assistant_store → remote_store}/base.py +10 -10
  106. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/__init__.py +1 -1
  107. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/patch.py +1 -1
  108. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/provider.py +16 -15
  109. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/storage.py +30 -23
  110. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/utils.py +5 -4
  111. pygpt_net/provider/core/{assistant_store → remote_store}/json_file.py +9 -9
  112. pygpt_net/provider/llms/google.py +2 -2
  113. pygpt_net/ui/base/config_dialog.py +3 -2
  114. pygpt_net/ui/dialog/assistant.py +3 -3
  115. pygpt_net/ui/dialog/plugins.py +3 -1
  116. pygpt_net/ui/dialog/remote_store_google.py +539 -0
  117. pygpt_net/ui/dialog/{assistant_store.py → remote_store_openai.py} +95 -95
  118. pygpt_net/ui/dialogs.py +5 -3
  119. pygpt_net/ui/layout/chat/attachments_uploaded.py +3 -3
  120. pygpt_net/ui/layout/toolbox/computer_env.py +26 -8
  121. pygpt_net/ui/menu/tools.py +13 -5
  122. pygpt_net/ui/widget/dialog/remote_store_google.py +56 -0
  123. pygpt_net/ui/widget/dialog/{assistant_store.py → remote_store_openai.py} +9 -9
  124. pygpt_net/ui/widget/element/button.py +4 -4
  125. pygpt_net/ui/widget/lists/remote_store_google.py +248 -0
  126. pygpt_net/ui/widget/lists/{assistant_store.py → remote_store_openai.py} +21 -21
  127. pygpt_net/ui/widget/option/checkbox_list.py +47 -9
  128. pygpt_net/ui/widget/option/combo.py +39 -3
  129. {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.5.dist-info}/METADATA +33 -2
  130. {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.5.dist-info}/RECORD +133 -108
  131. {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.5.dist-info}/LICENSE +0 -0
  132. {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.5.dist-info}/WHEEL +0 -0
  133. {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.5.dist-info}/entry_points.txt +0 -0
@@ -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()
@@ -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)
@@ -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.12.27 00:00:00 #
9
+ # Updated Date: 2026.01.02 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtGui import QAction, QIcon
@@ -18,7 +18,7 @@ from pygpt_net.utils import trans
18
18
  import pygpt_net.icons_rc
19
19
 
20
20
 
21
- class AssistantVectorStoreEditorList(BaseList):
21
+ class RemoteStoreOpenAIEditorList(BaseList):
22
22
  def __init__(self, window=None, id=None):
23
23
  """
24
24
  Store select menu (in editor)
@@ -26,7 +26,7 @@ class AssistantVectorStoreEditorList(BaseList):
26
26
  :param window: main window
27
27
  :param id: parent id
28
28
  """
29
- super(AssistantVectorStoreEditorList, self).__init__(window)
29
+ super(RemoteStoreOpenAIEditorList, self).__init__(window)
30
30
  self.window = window
31
31
  self.id = id
32
32
 
@@ -94,7 +94,7 @@ class AssistantVectorStoreEditorList(BaseList):
94
94
  self._suppress_item_click = True
95
95
  self._was_shift_click = True
96
96
  if idx.isValid():
97
- super(AssistantVectorStoreEditorList, self).mousePressEvent(event)
97
+ super(RemoteStoreOpenAIEditorList, self).mousePressEvent(event)
98
98
  else:
99
99
  event.accept()
100
100
  return
@@ -112,7 +112,7 @@ class AssistantVectorStoreEditorList(BaseList):
112
112
  return
113
113
  # continue with default single selection for clicked row
114
114
 
115
- super(AssistantVectorStoreEditorList, self).mousePressEvent(event)
115
+ super(RemoteStoreOpenAIEditorList, self).mousePressEvent(event)
116
116
  return
117
117
 
118
118
  # Right click: prepare selection for context menu
@@ -134,14 +134,14 @@ class AssistantVectorStoreEditorList(BaseList):
134
134
  event.accept()
135
135
  return
136
136
 
137
- super(AssistantVectorStoreEditorList, self).mousePressEvent(event)
137
+ super(RemoteStoreOpenAIEditorList, self).mousePressEvent(event)
138
138
 
139
139
  def mouseReleaseEvent(self, event):
140
140
  # If the click was a Shift-based range selection, bypass business click
141
141
  if event.button() == Qt.LeftButton and self._was_shift_click:
142
142
  self._was_shift_click = False
143
143
  self._suppress_item_click = False
144
- super(AssistantVectorStoreEditorList, self).mouseReleaseEvent(event)
144
+ super(RemoteStoreOpenAIEditorList, self).mouseReleaseEvent(event)
145
145
  return
146
146
 
147
147
  # Finish "virtual" Ctrl toggle on same row (no business click)
@@ -163,12 +163,12 @@ class AssistantVectorStoreEditorList(BaseList):
163
163
  idx = self.indexAt(event.pos())
164
164
  if not self._has_multi_selection():
165
165
  if idx.isValid() and not self._suppress_item_click:
166
- self.window.controller.assistant.store.select(idx.row())
166
+ self.window.controller.remote_store.openai.select(idx.row())
167
167
  self._suppress_item_click = False
168
- super(AssistantVectorStoreEditorList, self).mouseReleaseEvent(event)
168
+ super(RemoteStoreOpenAIEditorList, self).mouseReleaseEvent(event)
169
169
  return
170
170
 
171
- super(AssistantVectorStoreEditorList, self).mouseReleaseEvent(event)
171
+ super(RemoteStoreOpenAIEditorList, self).mouseReleaseEvent(event)
172
172
 
173
173
  def click(self, val):
174
174
  # Not used; single-selection business click is handled in mouseReleaseEvent
@@ -187,18 +187,18 @@ class AssistantVectorStoreEditorList(BaseList):
187
187
  actions = {}
188
188
  actions['refresh'] = QAction(
189
189
  QIcon(":/icons/reload.svg"),
190
- trans('dialog.assistant.store.menu.current.refresh_store'),
190
+ trans('dialog.remote_store.menu.current.refresh_store'),
191
191
  self
192
192
  )
193
193
  actions['delete'] = QAction(QIcon(":/icons/delete.svg"), trans('action.delete'), self)
194
194
  actions['clear'] = QAction(
195
195
  QIcon(":/icons/close.svg"),
196
- trans('dialog.assistant.store.menu.current.clear_files'),
196
+ trans('dialog.remote_store.menu.current.clear_files'),
197
197
  self
198
198
  )
199
199
  actions['truncate'] = QAction(
200
200
  QIcon(":/icons/delete.svg"),
201
- trans('dialog.assistant.store.menu.current.truncate_files'),
201
+ trans('dialog.remote_store.menu.current.truncate_files'),
202
202
  self
203
203
  )
204
204
 
@@ -263,12 +263,12 @@ class AssistantVectorStoreEditorList(BaseList):
263
263
  if isinstance(item, (list, tuple)):
264
264
  if item:
265
265
  self.restore_after_ctx_menu = False
266
- self.window.controller.assistant.store.delete_by_idx(list(item))
266
+ self.window.controller.remote_store.openai.delete_by_idx(list(item))
267
267
  return
268
268
  idx = int(item)
269
269
  if idx >= 0:
270
270
  self.restore_after_ctx_menu = False
271
- self.window.controller.assistant.store.delete_by_idx(idx)
271
+ self.window.controller.remote_store.openai.delete_by_idx(idx)
272
272
 
273
273
  def action_clear(self, item):
274
274
  """
@@ -279,12 +279,12 @@ class AssistantVectorStoreEditorList(BaseList):
279
279
  if isinstance(item, (list, tuple)):
280
280
  if item:
281
281
  self.restore_after_ctx_menu = False
282
- self.window.controller.assistant.batch.clear_store_files_by_idx(list(item))
282
+ self.window.controller.remote_store.openai.batch.clear_store_files_by_idx(list(item))
283
283
  return
284
284
  idx = int(item)
285
285
  if idx >= 0:
286
286
  self.restore_after_ctx_menu = False
287
- self.window.controller.assistant.batch.clear_store_files_by_idx(idx)
287
+ self.window.controller.remote_store.openai.batch.clear_store_files_by_idx(idx)
288
288
 
289
289
  def action_truncate(self, item):
290
290
  """
@@ -295,12 +295,12 @@ class AssistantVectorStoreEditorList(BaseList):
295
295
  if isinstance(item, (list, tuple)):
296
296
  if item:
297
297
  self.restore_after_ctx_menu = False
298
- self.window.controller.assistant.batch.truncate_store_files_by_idx(list(item))
298
+ self.window.controller.remote_store.openai.batch.truncate_store_files_by_idx(list(item))
299
299
  return
300
300
  idx = int(item)
301
301
  if idx >= 0:
302
302
  self.restore_after_ctx_menu = False
303
- self.window.controller.assistant.batch.truncate_store_files_by_idx(idx)
303
+ self.window.controller.remote_store.openai.batch.truncate_store_files_by_idx(idx)
304
304
 
305
305
  def action_refresh(self, item):
306
306
  """
@@ -309,9 +309,9 @@ class AssistantVectorStoreEditorList(BaseList):
309
309
  if isinstance(item, (list, tuple)):
310
310
  if item:
311
311
  self.restore_after_ctx_menu = False
312
- self.window.controller.assistant.store.refresh_by_idx(list(item))
312
+ self.window.controller.remote_store.openai.refresh_by_idx(list(item))
313
313
  return
314
314
  idx = int(item)
315
315
  if idx >= 0:
316
316
  self.restore_after_ctx_menu = False
317
- self.window.controller.assistant.store.refresh_by_idx(idx)
317
+ self.window.controller.remote_store.openai.refresh_by_idx(idx)
@@ -6,10 +6,12 @@
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.12.25 20:00:00 #
9
+ # Updated Date: 2026.01.01 15:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtWidgets import QCheckBox, QWidget, QPushButton
13
+ from PySide6.QtGui import QIcon
14
+ from PySide6.QtCore import QSize, Qt
13
15
 
14
16
  from pygpt_net.ui.base.flow_layout import FlowLayout
15
17
  from pygpt_net.utils import trans
@@ -43,6 +45,10 @@ class OptionCheckboxList(QWidget):
43
45
  self.keys = {}
44
46
  self.boxes = {}
45
47
 
48
+ # overlay button config
49
+ self._overlay_margin = 4 # px
50
+ self.btn_select = None
51
+
46
52
  # init from option data
47
53
  if self.option is not None:
48
54
  if "label" in self.option and self.option["label"] is not None \
@@ -84,20 +90,49 @@ class OptionCheckboxList(QWidget):
84
90
  for widget in widgets:
85
91
  self.layout.addWidget(widget)
86
92
 
87
- # select/unselect all button
88
- btn_select = QPushButton("X", self)
89
- btn_select.setToolTip(trans("action.select_unselect_all"))
90
- btn_select.clicked.connect(
93
+ # do not add the select/unselect all button to the flow layout
94
+ # it will be overlaid in the top-right corner to avoid affecting layout flow
95
+ self.layout.setContentsMargins(0, 0, 0, 0)
96
+ self.setLayout(self.layout)
97
+
98
+ # overlay select/unselect all button pinned to top-right corner
99
+ self.btn_select = QPushButton(self)
100
+ self.btn_select.setObjectName("btn_select_all_overlay")
101
+ self.btn_select.setToolTip(trans("action.select_unselect_all"))
102
+ self.btn_select.setIcon(QIcon(":/icons/asterisk.svg"))
103
+ self.btn_select.setIconSize(QSize(16, 16))
104
+ self.btn_select.setFixedSize(22, 22)
105
+ self.btn_select.setFocusPolicy(Qt.NoFocus)
106
+ self.btn_select.setCursor(Qt.PointingHandCursor)
107
+ self.btn_select.setStyleSheet("QPushButton { border: none; padding: 0; background: transparent; }")
108
+ self.btn_select.clicked.connect(
91
109
  lambda: self.window.controller.config.checkbox_list.on_select_all(
92
110
  self.parent_id,
93
111
  self.id,
94
112
  self.option
95
113
  )
96
114
  )
97
- self.layout.addWidget(btn_select)
115
+ self.btn_select.raise_()
116
+ self._place_select_button()
98
117
 
99
- self.layout.setContentsMargins(0, 0, 0, 0)
100
- self.setLayout(self.layout)
118
+ def _place_select_button(self) -> None:
119
+ """
120
+ Place the overlay select/unselect button in the top-right corner.
121
+ """
122
+ if not self.btn_select:
123
+ return
124
+ m = self._overlay_margin
125
+ w = self.btn_select.width()
126
+ x = max(0, self.width() - w - m)
127
+ y = m
128
+ self.btn_select.move(x, y)
129
+
130
+ def resizeEvent(self, event):
131
+ """
132
+ Keep the overlay button pinned to the top-right on resize.
133
+ """
134
+ super().resizeEvent(event)
135
+ self._place_select_button()
101
136
 
102
137
  def update_boxes_list(self, keys: dict) -> None:
103
138
  """
@@ -141,6 +176,9 @@ class OptionCheckboxList(QWidget):
141
176
  self.boxes[key] = checkbox
142
177
  self.layout.addWidget(checkbox)
143
178
 
179
+ # keep the overlay button in place after list update
180
+ self._place_select_button()
181
+
144
182
  def setIcon(self, icon: str):
145
183
  """
146
184
  Set icon
@@ -185,4 +223,4 @@ class OptionCheckboxList(QWidget):
185
223
  """
186
224
  if key in self.boxes:
187
225
  return self.boxes[key].isChecked()
188
- return False
226
+ return False
@@ -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.12.28 00:00:00 #
9
+ # Updated Date: 2026.01.01 15:00:00 #
10
10
  # ================================================== #
11
11
  import sys
12
12
 
@@ -742,6 +742,11 @@ class SearchableCombo(SeparatorComboBox):
742
742
  self.hidePopup()
743
743
  event.accept()
744
744
  return
745
+ if event.key() == Qt.Key_Right:
746
+ # Inject highlighted item's text into the search header
747
+ self._apply_highlighted_to_search_input()
748
+ event.accept()
749
+ return
745
750
  super().keyPressEvent(event)
746
751
 
747
752
  # ----- Event filter -----
@@ -821,6 +826,10 @@ class SearchableCombo(SeparatorComboBox):
821
826
  # Explicitly close popup from header
822
827
  self.hidePopup()
823
828
  return True
829
+ if key == Qt.Key_Right:
830
+ # Insert highlighted item's value into the search input
831
+ self._apply_highlighted_to_search_input()
832
+ return True
824
833
  if key in (Qt.Key_Up, Qt.Key_Down, Qt.Key_PageUp, Qt.Key_PageDown, Qt.Key_Home, Qt.Key_End):
825
834
  self._handle_navigation_key(key)
826
835
  return True
@@ -832,13 +841,18 @@ class SearchableCombo(SeparatorComboBox):
832
841
  view = self.view()
833
842
  if view is not None and (obj is view or obj is getattr(view, "viewport", lambda: None)()):
834
843
  if event.type() == QEvent.KeyPress:
835
- if event.key() in (Qt.Key_Return, Qt.Key_Enter):
844
+ key = event.key()
845
+ if key in (Qt.Key_Return, Qt.Key_Enter):
836
846
  self._commit_view_current()
837
847
  return True
838
- if event.key() == Qt.Key_Escape:
848
+ if key == Qt.Key_Escape:
839
849
  # Close from view/viewport ESC and keep control flow consistent
840
850
  self.hidePopup()
841
851
  return True
852
+ if key == Qt.Key_Right:
853
+ # Insert highlighted item's value into the search input
854
+ self._apply_highlighted_to_search_input()
855
+ return True
842
856
  return False
843
857
 
844
858
  return super().eventFilter(obj, event)
@@ -1406,6 +1420,28 @@ class SearchableCombo(SeparatorComboBox):
1406
1420
  except Exception:
1407
1421
  pass
1408
1422
 
1423
+ # ----- Convenience: inject highlighted row text into the search header -----
1424
+
1425
+ def _apply_highlighted_to_search_input(self):
1426
+ """
1427
+ Put the currently highlighted row's display text into the popup search input.
1428
+ Does nothing if the popup or header is not available. The normal textChanged
1429
+ handler will update scrolling and internal state.
1430
+ """
1431
+ if not self._popup_open or self._popup_header is None:
1432
+ return
1433
+ view = self.view()
1434
+ if view is None:
1435
+ return
1436
+ idx = view.currentIndex()
1437
+ row = self._sanitize_index(idx.row() if idx.isValid() else -1)
1438
+ if row == -1:
1439
+ return
1440
+ text = self.itemText(row) or ""
1441
+ self._popup_header.setText(text)
1442
+ self._ensure_clear_button_visible(self._popup_header)
1443
+ self._popup_header.setCursorPosition(len(text))
1444
+
1409
1445
 
1410
1446
  class NoScrollCombo(SearchableCombo):
1411
1447
  """A combo box that disables mouse wheel scrolling, extended with optional search support."""