pygpt-net 2.7.3__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 (157) hide show
  1. pygpt_net/CHANGELOG.txt +15 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/app.py +382 -350
  4. pygpt_net/app_core.py +4 -2
  5. pygpt_net/controller/__init__.py +5 -1
  6. pygpt_net/controller/assistant/assistant.py +1 -4
  7. pygpt_net/controller/assistant/batch.py +5 -504
  8. pygpt_net/controller/assistant/editor.py +5 -5
  9. pygpt_net/controller/assistant/files.py +16 -16
  10. pygpt_net/controller/chat/attachment.py +5 -1
  11. pygpt_net/controller/chat/handler/google_stream.py +307 -1
  12. pygpt_net/controller/chat/handler/worker.py +8 -1
  13. pygpt_net/controller/chat/image.py +15 -3
  14. pygpt_net/controller/dialogs/confirm.py +73 -101
  15. pygpt_net/controller/files/files.py +3 -1
  16. pygpt_net/controller/lang/mapping.py +9 -9
  17. pygpt_net/controller/layout/layout.py +2 -2
  18. pygpt_net/controller/painter/capture.py +50 -1
  19. pygpt_net/controller/presets/presets.py +2 -1
  20. pygpt_net/controller/remote_store/__init__.py +12 -0
  21. pygpt_net/{provider/core/assistant_file/db_sqlite → controller/remote_store/google}/__init__.py +2 -2
  22. pygpt_net/controller/remote_store/google/batch.py +402 -0
  23. pygpt_net/controller/remote_store/google/store.py +615 -0
  24. pygpt_net/controller/remote_store/openai/__init__.py +12 -0
  25. pygpt_net/controller/remote_store/openai/batch.py +524 -0
  26. pygpt_net/controller/{assistant → remote_store/openai}/store.py +63 -60
  27. pygpt_net/controller/remote_store/remote_store.py +35 -0
  28. pygpt_net/controller/theme/nodes.py +2 -1
  29. pygpt_net/controller/ui/mode.py +5 -1
  30. pygpt_net/controller/ui/ui.py +36 -2
  31. pygpt_net/core/assistants/assistants.py +3 -15
  32. pygpt_net/core/db/database.py +5 -3
  33. pygpt_net/core/filesystem/url.py +4 -1
  34. pygpt_net/core/locale/placeholder.py +35 -0
  35. pygpt_net/core/remote_store/__init__.py +12 -0
  36. pygpt_net/core/remote_store/google/__init__.py +11 -0
  37. pygpt_net/core/remote_store/google/files.py +224 -0
  38. pygpt_net/core/remote_store/google/store.py +248 -0
  39. pygpt_net/core/remote_store/openai/__init__.py +11 -0
  40. pygpt_net/core/{assistants → remote_store/openai}/files.py +26 -19
  41. pygpt_net/core/{assistants → remote_store/openai}/store.py +32 -15
  42. pygpt_net/core/remote_store/remote_store.py +24 -0
  43. pygpt_net/core/render/web/helpers.py +5 -0
  44. pygpt_net/data/config/config.json +8 -5
  45. pygpt_net/data/config/models.json +77 -3
  46. pygpt_net/data/config/settings.json +45 -14
  47. pygpt_net/data/css/web-blocks.css +3 -0
  48. pygpt_net/data/css/web-chatgpt.css +3 -0
  49. pygpt_net/data/locale/locale.de.ini +43 -41
  50. pygpt_net/data/locale/locale.en.ini +56 -44
  51. pygpt_net/data/locale/locale.es.ini +43 -41
  52. pygpt_net/data/locale/locale.fr.ini +43 -41
  53. pygpt_net/data/locale/locale.it.ini +43 -41
  54. pygpt_net/data/locale/locale.pl.ini +43 -41
  55. pygpt_net/data/locale/locale.uk.ini +43 -41
  56. pygpt_net/data/locale/locale.zh.ini +43 -41
  57. pygpt_net/data/locale/plugin.cmd_history.de.ini +1 -1
  58. pygpt_net/data/locale/plugin.cmd_history.en.ini +1 -1
  59. pygpt_net/data/locale/plugin.cmd_history.es.ini +1 -1
  60. pygpt_net/data/locale/plugin.cmd_history.fr.ini +1 -1
  61. pygpt_net/data/locale/plugin.cmd_history.it.ini +1 -1
  62. pygpt_net/data/locale/plugin.cmd_history.pl.ini +1 -1
  63. pygpt_net/data/locale/plugin.cmd_history.uk.ini +1 -1
  64. pygpt_net/data/locale/plugin.cmd_history.zh.ini +1 -1
  65. pygpt_net/data/locale/plugin.cmd_mouse_control.en.ini +14 -0
  66. pygpt_net/data/locale/plugin.cmd_web.de.ini +1 -1
  67. pygpt_net/data/locale/plugin.cmd_web.en.ini +1 -1
  68. pygpt_net/data/locale/plugin.cmd_web.es.ini +1 -1
  69. pygpt_net/data/locale/plugin.cmd_web.fr.ini +1 -1
  70. pygpt_net/data/locale/plugin.cmd_web.it.ini +1 -1
  71. pygpt_net/data/locale/plugin.cmd_web.pl.ini +1 -1
  72. pygpt_net/data/locale/plugin.cmd_web.uk.ini +1 -1
  73. pygpt_net/data/locale/plugin.cmd_web.zh.ini +1 -1
  74. pygpt_net/data/locale/plugin.idx_llama_index.de.ini +2 -2
  75. pygpt_net/data/locale/plugin.idx_llama_index.en.ini +2 -2
  76. pygpt_net/data/locale/plugin.idx_llama_index.es.ini +2 -2
  77. pygpt_net/data/locale/plugin.idx_llama_index.fr.ini +2 -2
  78. pygpt_net/data/locale/plugin.idx_llama_index.it.ini +2 -2
  79. pygpt_net/data/locale/plugin.idx_llama_index.pl.ini +2 -2
  80. pygpt_net/data/locale/plugin.idx_llama_index.uk.ini +2 -2
  81. pygpt_net/data/locale/plugin.idx_llama_index.zh.ini +2 -2
  82. pygpt_net/item/assistant.py +1 -211
  83. pygpt_net/item/ctx.py +3 -1
  84. pygpt_net/item/store.py +238 -0
  85. pygpt_net/launcher.py +115 -55
  86. pygpt_net/migrations/Version20260102190000.py +35 -0
  87. pygpt_net/migrations/__init__.py +3 -1
  88. pygpt_net/plugin/cmd_mouse_control/config.py +470 -1
  89. pygpt_net/plugin/cmd_mouse_control/plugin.py +488 -22
  90. pygpt_net/plugin/cmd_mouse_control/worker.py +464 -87
  91. pygpt_net/plugin/cmd_mouse_control/worker_sandbox.py +729 -0
  92. pygpt_net/plugin/idx_llama_index/config.py +2 -2
  93. pygpt_net/preload.py +243 -0
  94. pygpt_net/provider/api/google/__init__.py +16 -54
  95. pygpt_net/provider/api/google/chat.py +546 -129
  96. pygpt_net/provider/api/google/computer.py +190 -0
  97. pygpt_net/provider/api/google/image.py +74 -6
  98. pygpt_net/provider/api/google/realtime/realtime.py +2 -2
  99. pygpt_net/provider/api/google/remote_tools.py +93 -0
  100. pygpt_net/provider/api/google/store.py +546 -0
  101. pygpt_net/provider/api/google/video.py +9 -4
  102. pygpt_net/provider/api/google/worker/__init__.py +0 -0
  103. pygpt_net/provider/api/google/worker/importer.py +392 -0
  104. pygpt_net/provider/api/openai/computer.py +10 -1
  105. pygpt_net/provider/api/openai/image.py +42 -19
  106. pygpt_net/provider/api/openai/store.py +6 -6
  107. pygpt_net/provider/api/openai/video.py +27 -2
  108. pygpt_net/provider/api/openai/worker/importer.py +24 -24
  109. pygpt_net/provider/api/x_ai/image.py +25 -2
  110. pygpt_net/provider/core/config/patch.py +23 -1
  111. pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +3 -3
  112. pygpt_net/provider/core/model/patch.py +17 -3
  113. pygpt_net/provider/core/preset/json_file.py +13 -7
  114. pygpt_net/provider/core/{assistant_file → remote_file}/__init__.py +1 -1
  115. pygpt_net/provider/core/{assistant_file → remote_file}/base.py +9 -9
  116. pygpt_net/provider/core/remote_file/db_sqlite/__init__.py +12 -0
  117. pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/patch.py +1 -1
  118. pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/provider.py +23 -20
  119. pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/storage.py +35 -27
  120. pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/utils.py +5 -4
  121. pygpt_net/provider/core/{assistant_store → remote_store}/__init__.py +1 -1
  122. pygpt_net/provider/core/{assistant_store → remote_store}/base.py +10 -10
  123. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/__init__.py +1 -1
  124. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/patch.py +1 -1
  125. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/provider.py +16 -15
  126. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/storage.py +30 -23
  127. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/utils.py +5 -4
  128. pygpt_net/provider/core/{assistant_store → remote_store}/json_file.py +9 -9
  129. pygpt_net/provider/llms/google.py +2 -2
  130. pygpt_net/ui/base/config_dialog.py +3 -2
  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/chat/input.py +20 -2
  138. pygpt_net/ui/layout/chat/painter.py +6 -4
  139. pygpt_net/ui/layout/toolbox/computer_env.py +26 -8
  140. pygpt_net/ui/layout/toolbox/image.py +5 -5
  141. pygpt_net/ui/layout/toolbox/video.py +5 -4
  142. pygpt_net/ui/main.py +84 -3
  143. pygpt_net/ui/menu/tools.py +13 -5
  144. pygpt_net/ui/widget/dialog/base.py +3 -10
  145. pygpt_net/ui/widget/dialog/remote_store_google.py +56 -0
  146. pygpt_net/ui/widget/dialog/{assistant_store.py → remote_store_openai.py} +9 -9
  147. pygpt_net/ui/widget/element/button.py +4 -4
  148. pygpt_net/ui/widget/lists/remote_store_google.py +248 -0
  149. pygpt_net/ui/widget/lists/{assistant_store.py → remote_store_openai.py} +21 -21
  150. pygpt_net/ui/widget/option/checkbox_list.py +47 -9
  151. pygpt_net/ui/widget/option/combo.py +158 -4
  152. pygpt_net/ui/widget/textarea/input_extra.py +664 -0
  153. {pygpt_net-2.7.3.dist-info → pygpt_net-2.7.5.dist-info}/METADATA +48 -9
  154. {pygpt_net-2.7.3.dist-info → pygpt_net-2.7.5.dist-info}/RECORD +157 -130
  155. {pygpt_net-2.7.3.dist-info → pygpt_net-2.7.5.dist-info}/LICENSE +0 -0
  156. {pygpt_net-2.7.3.dist-info → pygpt_net-2.7.5.dist-info}/WHEEL +0 -0
  157. {pygpt_net-2.7.3.dist-info → pygpt_net-2.7.5.dist-info}/entry_points.txt +0 -0
@@ -9,11 +9,11 @@
9
9
  # Updated Date: 2025.12.30 22:00:00 #
10
10
  # ================================================== #
11
11
 
12
- from PySide6.QtWidgets import QVBoxLayout, QWidget, QCheckBox, QHBoxLayout
12
+ from PySide6.QtWidgets import QVBoxLayout, QWidget, QHBoxLayout
13
13
 
14
14
  from pygpt_net.ui.widget.option.combo import OptionCombo
15
15
  from pygpt_net.ui.widget.option.input import OptionInput
16
- from pygpt_net.ui.widget.option.slider import OptionSlider
16
+ from pygpt_net.ui.widget.option.toggle_label import ToggleLabel
17
17
  from pygpt_net.utils import trans
18
18
 
19
19
 
@@ -54,9 +54,9 @@ class Image:
54
54
  conf_global['img_resolution'] = OptionCombo(self.window, 'global', 'img_resolution', option_resolutions)
55
55
  conf_global['img_resolution'].setMinimumWidth(160)
56
56
 
57
- conf_global['img.remix'] = QCheckBox(trans("img.remix"), parent=container)
58
- conf_global['img.remix'].setToolTip(trans("img.remix.tooltip"))
59
- conf_global['img.remix'].toggled.connect(self.window.controller.media.toggle_remix_image)
57
+ conf_global['img.remix'] = ToggleLabel(trans("img.remix"), parent=self.window)
58
+ conf_global['img.remix'].box.setToolTip(trans("img.remix.tooltip"))
59
+ conf_global['img.remix'].box.toggled.connect(self.window.controller.media.toggle_remix_image)
60
60
 
61
61
  cols = QHBoxLayout()
62
62
  cols.addWidget(conf_global['img_resolution'], 3)
@@ -9,10 +9,11 @@
9
9
  # Updated Date: 2025.12.30 22:00:00 #
10
10
  # ================================================== #
11
11
 
12
- from PySide6.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QCheckBox
12
+ from PySide6.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout
13
13
 
14
14
  from pygpt_net.ui.widget.option.combo import OptionCombo
15
15
  from pygpt_net.ui.widget.option.input import OptionInput
16
+ from pygpt_net.ui.widget.option.toggle_label import ToggleLabel
16
17
  from pygpt_net.utils import trans
17
18
 
18
19
 
@@ -51,9 +52,9 @@ class Video:
51
52
  conf_global['video.resolution'].setMinimumWidth(120)
52
53
  conf_global['video.duration'].setMinimumWidth(50)
53
54
 
54
- conf_global['video.remix'] = QCheckBox(trans("video.remix"), parent=container)
55
- conf_global['video.remix'].setToolTip(trans("video.remix.tooltip"))
56
- conf_global['video.remix'].toggled.connect(self.window.controller.media.toggle_remix_video)
55
+ conf_global['video.remix'] = ToggleLabel(trans("video.remix"), parent=self.window)
56
+ conf_global['video.remix'].box.setToolTip(trans("video.remix.tooltip"))
57
+ conf_global['video.remix'].box.toggled.connect(self.window.controller.media.toggle_remix_video)
57
58
 
58
59
  cols = QHBoxLayout()
59
60
  cols.addWidget(conf_global['video.resolution'], 2)
pygpt_net/ui/main.py CHANGED
@@ -6,13 +6,13 @@
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: 2025.12.31 14:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
13
13
 
14
14
  from PySide6.QtCore import QTimer, Signal, Slot, QThreadPool, QEvent, Qt, QLoggingCategory, QEventLoop
15
- from PySide6.QtGui import QShortcut, QKeySequence
15
+ from PySide6.QtGui import QShortcut, QKeySequence, QKeyEvent
16
16
  from PySide6.QtWidgets import QMainWindow, QApplication
17
17
  from qt_material import QtStyleTools
18
18
 
@@ -37,6 +37,7 @@ class MainWindow(QMainWindow, QtStyleTools):
37
37
  stateChanged = Signal(str)
38
38
  logger_message = Signal(object)
39
39
  idx_logger_message = Signal(object)
40
+ appReady = Signal() # emitted after the first paint to indicate the window is ready on screen
40
41
 
41
42
  def __init__(self, app: QApplication, args: dict = None):
42
43
  """
@@ -61,6 +62,9 @@ class MainWindow(QMainWindow, QtStyleTools):
61
62
  self.prevState = None
62
63
  self.is_post_update = False
63
64
 
65
+ # app ready emission control
66
+ self._app_ready_emitted = False # ensures single-shot emission
67
+
64
68
  # load version info
65
69
  self.meta = get_app_meta()
66
70
 
@@ -92,6 +96,7 @@ class MainWindow(QMainWindow, QtStyleTools):
92
96
 
93
97
  # global shortcuts
94
98
  self.shortcuts = []
99
+ self._esc_shortcut = None # keep a direct handle to temporarily disable during rerouting
95
100
 
96
101
  # setup signals
97
102
  self.statusChanged.connect(self.update_status)
@@ -231,6 +236,18 @@ class MainWindow(QMainWindow, QtStyleTools):
231
236
  super().showEvent(e)
232
237
  QTimer.singleShot(0, self.ui.on_show)
233
238
 
239
+ def paintEvent(self, e):
240
+ """
241
+ On the first paint, announce that the window is actually visible and ready.
242
+ This is used to synchronize closing the external splash screen.
243
+ """
244
+ super().paintEvent(e)
245
+ if not self._app_ready_emitted:
246
+ self._app_ready_emitted = True
247
+ QTimer.singleShot(0, self.appReady.emit)
248
+ # set focus to main window after shown
249
+ QTimer.singleShot(0, self.activateWindow)
250
+
234
251
  def update(self):
235
252
  """Called on every update (real-time)"""
236
253
  # self.controller.on_update()
@@ -404,6 +421,67 @@ class MainWindow(QMainWindow, QtStyleTools):
404
421
  self.activateWindow()
405
422
  self.ui.tray_menu['restore'].setVisible(False)
406
423
 
424
+ # ----- Global ESC routing that preserves widget-level ESC handling -----
425
+
426
+ def _deliver_escape_to(self, target) -> bool:
427
+ """
428
+ Synthesize ESC keypress to the focused/popup widget so it can run its own close logic.
429
+ Temporarily disables the global ESC shortcut to avoid re-triggering itself.
430
+ """
431
+ if target is None or not target.isVisible():
432
+ return False
433
+ try:
434
+ if self._esc_shortcut is not None:
435
+ self._esc_shortcut.setEnabled(False)
436
+ press = QKeyEvent(QEvent.KeyPress, Qt.Key_Escape, Qt.NoModifier)
437
+ release = QKeyEvent(QEvent.KeyRelease, Qt.Key_Escape, Qt.NoModifier)
438
+ QApplication.sendEvent(target, press)
439
+ QApplication.sendEvent(target, release)
440
+ except Exception:
441
+ pass
442
+ finally:
443
+ if self._esc_shortcut is not None:
444
+ QTimer.singleShot(0, lambda: self._esc_shortcut.setEnabled(True))
445
+ return True
446
+
447
+ def _route_escape_to_focus_or_popup(self) -> bool:
448
+ """
449
+ Route ESC to the widget that currently owns focus (prefer) or active popup widget.
450
+ Returns True when ESC was delivered to a target.
451
+ """
452
+ popup = QApplication.activePopupWidget()
453
+ if popup is not None and popup.isVisible():
454
+ try:
455
+ fw = QApplication.focusWidget()
456
+ if self._deliver_escape_to(fw):
457
+ return True
458
+ except Exception:
459
+ pass
460
+ return True
461
+
462
+ modal = QApplication.activeModalWidget()
463
+ if modal is not None and modal.isVisible():
464
+ try:
465
+ modal.close()
466
+ except Exception:
467
+ pass
468
+ return True
469
+
470
+ # No popup or modal to close
471
+ return False
472
+
473
+ def _on_escape_shortcut(self):
474
+ """
475
+ Global ESC: deliver ESC to the focused/popup widget first so it can handle and cleanup correctly.
476
+ If nothing handles it, run the app-level escape handler.
477
+ """
478
+ if self._route_escape_to_focus_or_popup():
479
+ return
480
+ try:
481
+ self.controller.access.on_escape()
482
+ except Exception:
483
+ pass
484
+
407
485
  def setup_global_shortcuts(self):
408
486
  """Setup global shortcuts"""
409
487
  if not hasattr(self, 'core') or not hasattr(self.core, 'config'):
@@ -417,12 +495,15 @@ class MainWindow(QMainWindow, QtStyleTools):
417
495
  self.shortcuts.clear()
418
496
  else:
419
497
  self.shortcuts = []
498
+ self._esc_shortcut = None
420
499
 
421
500
  # Handle the Escape key
422
501
  escape_shortcut = QShortcut(QKeySequence(Qt.Key_Escape), self)
423
502
  escape_shortcut.setContext(Qt.ApplicationShortcut)
424
- escape_shortcut.activated.connect(self.controller.access.on_escape)
503
+ # escape_shortcut.setAutoRepeat(False) # avoid spamming when holding the key
504
+ escape_shortcut.activated.connect(self._on_escape_shortcut)
425
505
  self.shortcuts.append(escape_shortcut)
506
+ self._esc_shortcut = escape_shortcut
426
507
 
427
508
  config = self.core.config.get("access.shortcuts")
428
509
  if config is None:
@@ -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")
@@ -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: 2025.12.31 14:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtWidgets import QApplication
@@ -75,9 +75,8 @@ class BaseDialog(QDialog):
75
75
  "size": [self.size().width(), self.size().height()],
76
76
  "pos": [self.pos().x(), self.pos().y()]
77
77
  }
78
+ data = {}
78
79
  if self.store_geometry_enabled():
79
- data = config.get("layout.dialog.geometry", {})
80
- else:
81
80
  data = config.get_session("layout.dialog.geometry", {})
82
81
 
83
82
  if not isinstance(data, dict):
@@ -85,8 +84,6 @@ class BaseDialog(QDialog):
85
84
  data[self.id] = item
86
85
 
87
86
  if self.store_geometry_enabled():
88
- config.set("layout.dialog.geometry", data)
89
- else:
90
87
  config.set_session("layout.dialog.geometry", data)
91
88
 
92
89
  def restore_geometry(self):
@@ -96,14 +93,10 @@ class BaseDialog(QDialog):
96
93
  available_geometry = screen.availableGeometry()
97
94
  config = self.window.core.config
98
95
 
96
+ data = {}
99
97
  if self.store_geometry_enabled():
100
- data = config.get("layout.dialog.geometry", {})
101
- else:
102
98
  data = config.get_session("layout.dialog.geometry", {})
103
99
 
104
- if not isinstance(data, dict):
105
- data = {}
106
-
107
100
  item = data.get(self.id, {})
108
101
  if isinstance(item, dict) and "size" in item and "pos" in item:
109
102
  width, height = item["size"]
@@ -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()
@@ -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)