pygpt-net 2.4.37__py3-none-any.whl → 2.4.41__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 (255) hide show
  1. CHANGELOG.md +29 -0
  2. README.md +44 -115
  3. pygpt_net/CHANGELOG.txt +29 -0
  4. pygpt_net/__init__.py +3 -3
  5. pygpt_net/controller/__init__.py +7 -3
  6. pygpt_net/controller/access/control.py +1 -1
  7. pygpt_net/controller/access/voice.py +11 -5
  8. pygpt_net/controller/agent/experts.py +11 -6
  9. pygpt_net/controller/agent/legacy.py +8 -6
  10. pygpt_net/controller/agent/llama.py +4 -2
  11. pygpt_net/controller/assistant/__init__.py +9 -4
  12. pygpt_net/controller/assistant/batch.py +38 -21
  13. pygpt_net/controller/assistant/editor.py +7 -6
  14. pygpt_net/controller/assistant/files.py +23 -7
  15. pygpt_net/controller/assistant/store.py +20 -7
  16. pygpt_net/controller/assistant/threads.py +34 -8
  17. pygpt_net/controller/attachment.py +29 -10
  18. pygpt_net/controller/audio/__init__.py +25 -4
  19. pygpt_net/controller/calendar/__init__.py +23 -4
  20. pygpt_net/controller/calendar/note.py +57 -11
  21. pygpt_net/controller/camera.py +3 -2
  22. pygpt_net/controller/chat/__init__.py +5 -3
  23. pygpt_net/controller/chat/attachment.py +34 -7
  24. pygpt_net/controller/chat/command.py +4 -2
  25. pygpt_net/controller/chat/common.py +11 -4
  26. pygpt_net/controller/chat/files.py +10 -3
  27. pygpt_net/controller/chat/image.py +17 -5
  28. pygpt_net/controller/chat/input.py +10 -7
  29. pygpt_net/controller/chat/output.py +21 -6
  30. pygpt_net/controller/chat/render.py +100 -21
  31. pygpt_net/controller/chat/response.py +34 -7
  32. pygpt_net/controller/chat/stream.py +4 -2
  33. pygpt_net/controller/chat/text.py +6 -4
  34. pygpt_net/controller/command.py +11 -3
  35. pygpt_net/controller/config/__init__.py +34 -6
  36. pygpt_net/controller/config/field/checkbox.py +7 -4
  37. pygpt_net/controller/config/field/cmd.py +7 -5
  38. pygpt_net/controller/config/field/combo.py +14 -6
  39. pygpt_net/controller/config/field/dictionary.py +14 -11
  40. pygpt_net/controller/config/field/input.py +9 -6
  41. pygpt_net/controller/config/field/slider.py +11 -8
  42. pygpt_net/controller/config/field/textarea.py +8 -5
  43. pygpt_net/controller/config/placeholder.py +52 -21
  44. pygpt_net/controller/ctx/__init__.py +138 -49
  45. pygpt_net/controller/ctx/common.py +15 -4
  46. pygpt_net/controller/ctx/extra.py +11 -3
  47. pygpt_net/controller/ctx/summarizer.py +24 -5
  48. pygpt_net/controller/debug/__init__.py +27 -6
  49. pygpt_net/controller/dialogs/confirm.py +34 -7
  50. pygpt_net/controller/dialogs/debug.py +4 -2
  51. pygpt_net/controller/dialogs/info.py +7 -2
  52. pygpt_net/controller/files.py +48 -10
  53. pygpt_net/controller/finder.py +11 -5
  54. pygpt_net/controller/idx/__init__.py +10 -3
  55. pygpt_net/controller/idx/common.py +4 -2
  56. pygpt_net/controller/idx/indexer.py +25 -17
  57. pygpt_net/controller/idx/settings.py +9 -3
  58. pygpt_net/controller/kernel/__init__.py +34 -8
  59. pygpt_net/controller/kernel/reply.py +12 -3
  60. pygpt_net/controller/kernel/stack.py +5 -3
  61. pygpt_net/controller/lang/custom.py +2 -7
  62. pygpt_net/controller/lang/mapping.py +5 -3
  63. pygpt_net/controller/layout.py +2 -2
  64. pygpt_net/controller/mode.py +16 -4
  65. pygpt_net/controller/model/__init__.py +14 -3
  66. pygpt_net/controller/model/editor.py +8 -3
  67. pygpt_net/controller/notepad.py +26 -12
  68. pygpt_net/controller/painter/capture.py +23 -4
  69. pygpt_net/controller/painter/common.py +9 -7
  70. pygpt_net/controller/plugins/__init__.py +19 -5
  71. pygpt_net/controller/plugins/presets.py +15 -6
  72. pygpt_net/controller/plugins/settings.py +9 -3
  73. pygpt_net/controller/presets/__init__.py +55 -16
  74. pygpt_net/controller/presets/editor.py +26 -10
  75. pygpt_net/controller/settings/__init__.py +3 -2
  76. pygpt_net/controller/settings/editor.py +29 -7
  77. pygpt_net/controller/settings/profile.py +22 -5
  78. pygpt_net/controller/theme/__init__.py +54 -12
  79. pygpt_net/controller/theme/common.py +24 -2
  80. pygpt_net/controller/theme/markdown.py +32 -16
  81. pygpt_net/controller/theme/menu.py +26 -5
  82. pygpt_net/controller/theme/nodes.py +2 -5
  83. pygpt_net/controller/tools/__init__.py +40 -2
  84. pygpt_net/controller/ui/__init__.py +4 -6
  85. pygpt_net/controller/ui/tabs.py +363 -65
  86. pygpt_net/core/access/actions.py +6 -4
  87. pygpt_net/core/access/shortcuts.py +4 -3
  88. pygpt_net/core/access/voice.py +6 -5
  89. pygpt_net/core/agents/legacy.py +4 -2
  90. pygpt_net/core/agents/memory.py +7 -2
  91. pygpt_net/core/agents/observer/evaluation.py +15 -7
  92. pygpt_net/core/agents/provider.py +9 -4
  93. pygpt_net/core/agents/runner.py +61 -15
  94. pygpt_net/core/agents/tools.py +23 -5
  95. pygpt_net/core/assistants/__init__.py +6 -4
  96. pygpt_net/core/assistants/files.py +35 -12
  97. pygpt_net/core/assistants/store.py +20 -10
  98. pygpt_net/core/attachments/__init__.py +54 -15
  99. pygpt_net/core/attachments/context.py +92 -29
  100. pygpt_net/core/audio/__init__.py +71 -3
  101. pygpt_net/core/audio/context.py +7 -2
  102. pygpt_net/core/bridge/__init__.py +22 -6
  103. pygpt_net/core/bridge/context.py +5 -3
  104. pygpt_net/core/calendar/__init__.py +57 -11
  105. pygpt_net/core/chain/__init__.py +8 -2
  106. pygpt_net/core/chain/chat.py +10 -8
  107. pygpt_net/core/chain/completion.py +10 -7
  108. pygpt_net/core/command.py +62 -17
  109. pygpt_net/core/ctx/__init__.py +260 -58
  110. pygpt_net/core/ctx/bag.py +25 -4
  111. pygpt_net/core/ctx/container.py +28 -17
  112. pygpt_net/core/ctx/idx.py +45 -8
  113. pygpt_net/core/ctx/output.py +95 -74
  114. pygpt_net/core/ctx/reply.py +5 -2
  115. pygpt_net/core/db/__init__.py +8 -7
  116. pygpt_net/core/db/viewer.py +17 -11
  117. pygpt_net/core/debug/__init__.py +10 -9
  118. pygpt_net/core/debug/tabs.py +5 -2
  119. pygpt_net/core/docker/__init__.py +11 -5
  120. pygpt_net/core/docker/builder.py +11 -3
  121. pygpt_net/core/events/app.py +5 -3
  122. pygpt_net/core/events/base.py +11 -5
  123. pygpt_net/core/events/control.py +5 -3
  124. pygpt_net/core/events/event.py +17 -7
  125. pygpt_net/core/events/kernel.py +5 -3
  126. pygpt_net/core/events/render.py +5 -3
  127. pygpt_net/core/experts/__init__.py +5 -4
  128. pygpt_net/core/filesystem/__init__.py +52 -34
  129. pygpt_net/core/filesystem/actions.py +8 -5
  130. pygpt_net/core/filesystem/editor.py +13 -3
  131. pygpt_net/core/filesystem/types.py +12 -7
  132. pygpt_net/core/filesystem/url.py +7 -3
  133. pygpt_net/core/idx/__init__.py +34 -25
  134. pygpt_net/core/idx/chat.py +40 -16
  135. pygpt_net/core/idx/context.py +6 -2
  136. pygpt_net/core/idx/indexing.py +84 -35
  137. pygpt_net/core/idx/llm.py +11 -3
  138. pygpt_net/core/idx/metadata.py +13 -3
  139. pygpt_net/core/idx/types/ctx.py +32 -6
  140. pygpt_net/core/idx/types/external.py +41 -7
  141. pygpt_net/core/idx/types/files.py +31 -6
  142. pygpt_net/core/image.py +15 -4
  143. pygpt_net/core/llm/__init__.py +13 -3
  144. pygpt_net/core/locale.py +34 -8
  145. pygpt_net/core/models.py +4 -3
  146. pygpt_net/core/notepad.py +9 -4
  147. pygpt_net/core/plugins.py +7 -6
  148. pygpt_net/core/presets.py +19 -10
  149. pygpt_net/core/profile.py +12 -6
  150. pygpt_net/core/prompt/__init__.py +10 -3
  151. pygpt_net/core/prompt/custom.py +7 -6
  152. pygpt_net/core/prompt/template.py +9 -3
  153. pygpt_net/core/render/base.py +117 -22
  154. pygpt_net/core/render/markdown/body.py +27 -7
  155. pygpt_net/core/render/markdown/renderer.py +119 -22
  156. pygpt_net/core/render/plain/body.py +22 -5
  157. pygpt_net/core/render/plain/renderer.py +97 -21
  158. pygpt_net/core/render/web/body.py +75 -25
  159. pygpt_net/core/render/web/renderer.py +313 -63
  160. pygpt_net/core/settings.py +9 -4
  161. pygpt_net/core/tabs/__init__.py +290 -103
  162. pygpt_net/core/tabs/tab.py +17 -4
  163. pygpt_net/core/tokens.py +44 -11
  164. pygpt_net/core/updater/__init__.py +20 -7
  165. pygpt_net/core/vision/analyzer.py +29 -6
  166. pygpt_net/core/web.py +130 -2
  167. pygpt_net/data/config/config.json +15 -4
  168. pygpt_net/data/config/models.json +3 -3
  169. pygpt_net/data/config/modes.json +3 -3
  170. pygpt_net/data/config/settings.json +55 -10
  171. pygpt_net/data/config/settings_section.json +3 -0
  172. pygpt_net/data/css/style.light.css +1 -0
  173. pygpt_net/data/css/{web.css → web-blocks.css} +162 -133
  174. pygpt_net/data/css/{web.light.css → web-blocks.light.css} +7 -0
  175. pygpt_net/data/css/web-chatgpt.css +350 -0
  176. pygpt_net/data/css/web-chatgpt.dark.css +64 -0
  177. pygpt_net/data/css/web-chatgpt.light.css +75 -0
  178. pygpt_net/data/css/web-chatgpt_wide.css +350 -0
  179. pygpt_net/data/css/web-chatgpt_wide.dark.css +64 -0
  180. pygpt_net/data/css/web-chatgpt_wide.light.css +75 -0
  181. pygpt_net/data/icons/split_screen.svg +1 -0
  182. pygpt_net/data/locale/locale.de.ini +12 -0
  183. pygpt_net/data/locale/locale.en.ini +17 -2
  184. pygpt_net/data/locale/locale.es.ini +12 -0
  185. pygpt_net/data/locale/locale.fr.ini +12 -0
  186. pygpt_net/data/locale/locale.it.ini +12 -0
  187. pygpt_net/data/locale/locale.pl.ini +12 -0
  188. pygpt_net/data/locale/locale.uk.ini +12 -0
  189. pygpt_net/data/locale/locale.zh.ini +12 -0
  190. pygpt_net/data/locale/plugin.cmd_web.de.ini +2 -0
  191. pygpt_net/data/locale/plugin.cmd_web.en.ini +20 -10
  192. pygpt_net/data/locale/plugin.cmd_web.es.ini +2 -0
  193. pygpt_net/data/locale/plugin.cmd_web.fr.ini +2 -0
  194. pygpt_net/data/locale/plugin.cmd_web.it.ini +2 -0
  195. pygpt_net/data/locale/plugin.cmd_web.pl.ini +2 -0
  196. pygpt_net/data/locale/plugin.cmd_web.uk.ini +2 -0
  197. pygpt_net/data/locale/plugin.cmd_web.zh.ini +2 -0
  198. pygpt_net/icons.qrc +1 -0
  199. pygpt_net/icons_rc.py +165 -136
  200. pygpt_net/item/ctx.py +46 -24
  201. pygpt_net/plugin/audio_input/simple.py +21 -5
  202. pygpt_net/plugin/audio_output/__init__.py +4 -1
  203. pygpt_net/plugin/base/config.py +4 -2
  204. pygpt_net/plugin/base/plugin.py +26 -6
  205. pygpt_net/plugin/base/worker.py +37 -9
  206. pygpt_net/plugin/cmd_code_interpreter/__init__.py +39 -37
  207. pygpt_net/plugin/cmd_code_interpreter/runner.py +25 -12
  208. pygpt_net/plugin/cmd_web/__init__.py +46 -6
  209. pygpt_net/plugin/cmd_web/config.py +74 -48
  210. pygpt_net/plugin/cmd_web/websearch.py +61 -28
  211. pygpt_net/plugin/cmd_web/worker.py +79 -13
  212. pygpt_net/provider/core/config/patch.py +43 -1
  213. pygpt_net/provider/core/ctx/base.py +4 -1
  214. pygpt_net/provider/core/ctx/db_sqlite/__init__.py +10 -1
  215. pygpt_net/provider/core/ctx/db_sqlite/storage.py +22 -1
  216. pygpt_net/provider/gpt/assistants.py +10 -9
  217. pygpt_net/provider/gpt/audio.py +3 -2
  218. pygpt_net/provider/gpt/chat.py +8 -7
  219. pygpt_net/provider/gpt/completion.py +6 -4
  220. pygpt_net/provider/gpt/image.py +9 -2
  221. pygpt_net/provider/gpt/store.py +14 -13
  222. pygpt_net/provider/gpt/vision.py +6 -5
  223. pygpt_net/tools/__init__.py +9 -1
  224. pygpt_net/tools/base.py +15 -1
  225. pygpt_net/tools/code_interpreter/__init__.py +174 -75
  226. pygpt_net/tools/code_interpreter/ui/dialogs.py +21 -103
  227. pygpt_net/tools/code_interpreter/ui/widgets.py +284 -9
  228. pygpt_net/tools/html_canvas/__init__.py +78 -23
  229. pygpt_net/tools/html_canvas/ui/dialogs.py +46 -62
  230. pygpt_net/tools/html_canvas/ui/widgets.py +96 -3
  231. pygpt_net/ui/base/context_menu.py +2 -2
  232. pygpt_net/ui/layout/chat/input.py +10 -18
  233. pygpt_net/ui/layout/chat/output.py +26 -44
  234. pygpt_net/ui/layout/ctx/ctx_list.py +13 -4
  235. pygpt_net/ui/layout/toolbox/footer.py +18 -2
  236. pygpt_net/ui/main.py +2 -2
  237. pygpt_net/ui/menu/config.py +7 -11
  238. pygpt_net/ui/menu/debug.py +11 -1
  239. pygpt_net/ui/menu/theme.py +9 -2
  240. pygpt_net/ui/widget/filesystem/explorer.py +2 -2
  241. pygpt_net/ui/widget/lists/context.py +27 -5
  242. pygpt_net/ui/widget/tabs/Input.py +2 -2
  243. pygpt_net/ui/widget/tabs/body.py +2 -1
  244. pygpt_net/ui/widget/tabs/layout.py +195 -0
  245. pygpt_net/ui/widget/tabs/output.py +218 -55
  246. pygpt_net/ui/widget/textarea/html.py +11 -1
  247. pygpt_net/ui/widget/textarea/output.py +10 -1
  248. pygpt_net/ui/widget/textarea/search_input.py +4 -1
  249. pygpt_net/ui/widget/textarea/web.py +49 -9
  250. {pygpt_net-2.4.37.dist-info → pygpt_net-2.4.41.dist-info}/METADATA +45 -116
  251. {pygpt_net-2.4.37.dist-info → pygpt_net-2.4.41.dist-info}/RECORD +255 -247
  252. /pygpt_net/data/css/{web.dark.css → web-blocks.dark.css} +0 -0
  253. {pygpt_net-2.4.37.dist-info → pygpt_net-2.4.41.dist-info}/LICENSE +0 -0
  254. {pygpt_net-2.4.37.dist-info → pygpt_net-2.4.41.dist-info}/WHEEL +0 -0
  255. {pygpt_net-2.4.37.dist-info → pygpt_net-2.4.41.dist-info}/entry_points.txt +0 -0
@@ -6,10 +6,10 @@
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.11.05 23:00:00 #
9
+ # Updated Date: 2024.12.14 07:00:00 #
10
10
  # ================================================== #
11
11
 
12
- from PySide6.QtWidgets import QTabWidget, QMenu
12
+ from PySide6.QtWidgets import QTabWidget, QMenu, QPushButton
13
13
  from PySide6.QtCore import Qt, Slot
14
14
  from PySide6.QtGui import QAction, QIcon
15
15
 
@@ -18,17 +18,128 @@ from pygpt_net.utils import trans
18
18
  import pygpt_net.icons_rc
19
19
 
20
20
 
21
+ class AddButton(QPushButton):
22
+ def __init__(self, window=None, column=None, tabs=None):
23
+ super(AddButton, self).__init__(QIcon(":/icons/add.svg"), "", window)
24
+ self.window = window
25
+ self.column = column
26
+ self.tabs = tabs
27
+ self.setFixedSize(30, 25)
28
+ self.setFlat(True)
29
+ self.clicked.connect(
30
+ lambda: self.window.controller.ui.tabs.new_tab(self.column.get_idx())
31
+ )
32
+ self.setObjectName('tab-add')
33
+ self.setProperty('tabAdd', True)
34
+ self.setToolTip(trans('action.tab.add.chat'))
35
+
36
+ def mousePressEvent(self, event):
37
+ """
38
+ Mouse press event
39
+
40
+ :param event: event
41
+ """
42
+ if event.button() == Qt.RightButton:
43
+ idx = 0
44
+ column_idx = self.column.get_idx()
45
+ self.show_menu(idx, column_idx, event.globalPos())
46
+ super(AddButton, self).mousePressEvent(event)
47
+
48
+ def show_menu(self, index: int, column_idx: int, global_pos):
49
+ """
50
+ Show context menu
51
+
52
+ :param index: index
53
+ :param column_idx: column index
54
+ :param global_pos: global position
55
+ """
56
+ context_menu = self.prepare_menu(index, column_idx)
57
+ context_menu.exec(global_pos)
58
+
59
+ def prepare_menu(self, index: int, column_idx: int) -> QMenu:
60
+ """
61
+ Prepare and return context menu
62
+
63
+ :param index: index
64
+ :param column_idx: column index
65
+ :return: menu
66
+ """
67
+ menu = QMenu(self)
68
+
69
+ actions = {}
70
+ actions['add_chat'] = QAction(QIcon(":/icons/add.svg"), trans('action.tab.add.chat'), self)
71
+ actions['add_chat'].triggered.connect(
72
+ lambda: self.tabs.add_tab(index, column_idx, Tab.TAB_CHAT)
73
+ )
74
+ actions['add_notepad'] = QAction(QIcon(":/icons/add.svg"), trans('action.tab.add.notepad'), self)
75
+ actions['add_notepad'].triggered.connect(
76
+ lambda: self.tabs.add_tab(index, column_idx, Tab.TAB_NOTEPAD)
77
+ )
78
+
79
+ # add chat, add notepad
80
+ menu.addAction(actions['add_chat'])
81
+ menu.addAction(actions['add_notepad'])
82
+
83
+ # add tools submenu
84
+ self.window.controller.tools.append_tab_menu(self, menu, index, column_idx)
85
+
86
+ return menu
87
+
21
88
  class OutputTabs(QTabWidget):
22
- def __init__(self, window=None):
89
+ def __init__(self, window=None, column=None):
23
90
  super(OutputTabs, self).__init__(window)
24
91
  self.window = window
92
+ self.active = True
93
+ self.column = column
25
94
  self.setMinimumHeight(1)
26
95
  self.owner = None
27
96
  self.setMovable(True)
97
+ self.init()
98
+
99
+ def set_active(self, active: bool):
100
+ """Set the active state of the tab bar."""
101
+ self.active = active
102
+ if self.active:
103
+ self.setStyleSheet("QTabBar::tab { border-bottom-width: 2px; }")
104
+ else:
105
+ self.setStyleSheet("QTabBar::tab { border-bottom-width: 0px; }")
106
+
107
+ def init(self):
108
+ """Initialize"""
109
+ # create the [+] button
110
+ add_button = AddButton(self.window, self.column, self)
111
+
112
+ # add the button to the top right corner of the tab bar
113
+ self.setCornerWidget(add_button, corner=Qt.TopRightCorner)
114
+
115
+ # connect signals
116
+ self.currentChanged.connect(
117
+ lambda: self.window.controller.ui.tabs.on_tab_changed(self.currentIndex(), self.column.get_idx())
118
+ )
119
+ self.tabBarClicked.connect(
120
+ lambda: self.window.controller.ui.tabs.on_tab_clicked(self.currentIndex(), self.column.get_idx())
121
+ )
122
+ self.tabBarDoubleClicked.connect(
123
+ lambda: self.window.controller.ui.tabs.on_tab_dbl_clicked(self.currentIndex(), self.column.get_idx())
124
+ )
125
+ self.tabCloseRequested.connect(
126
+ lambda: self.window.controller.ui.tabs.on_tab_closed(self.currentIndex(), self.column.get_idx())
127
+ )
128
+ self.tabBar().tabMoved.connect(
129
+ lambda: self.window.controller.ui.tabs.on_tab_moved(self.currentIndex(), self.column.get_idx())
130
+ )
131
+
132
+ def get_column(self):
133
+ """
134
+ Get column
135
+
136
+ :return: OutputColumn
137
+ """
138
+ return self.column
28
139
 
29
140
  def setOwner(self, owner: Tab):
30
141
  """
31
- Set parent
142
+ Set internal tab instance
32
143
 
33
144
  :param owner: parent tab instance
34
145
  """
@@ -42,158 +153,210 @@ class OutputTabs(QTabWidget):
42
153
  """
43
154
  if event.button() == Qt.RightButton:
44
155
  idx = self.tabBar().tabAt(event.pos())
45
- tab = self.window.core.tabs.get_tab_by_index(idx)
156
+ column_idx = self.column.get_idx()
157
+ tab = self.window.core.tabs.get_tab_by_index(idx, column_idx)
46
158
  if tab is not None:
47
159
  if tab.type == Tab.TAB_NOTEPAD:
48
- self.show_notepad_menu(idx, event.globalPos()) # notepad
160
+ self.show_notepad_menu(idx, column_idx, event.globalPos()) # notepad
49
161
  elif tab.type == Tab.TAB_CHAT:
50
- self.show_chat_menu(idx, event.globalPos()) # chat
162
+ self.show_chat_menu(idx, column_idx, event.globalPos()) # chat
51
163
  elif tab.type == Tab.TAB_FILES:
52
- self.show_files_menu(idx, event.globalPos()) # files
164
+ self.show_files_menu(idx, column_idx, event.globalPos()) # files
165
+ elif tab.type == Tab.TAB_TOOL:
166
+ self.show_tool_menu(idx, column_idx, event.globalPos()) # tool
53
167
  else:
54
- self.show_default_menu(idx, event.globalPos()) # default
168
+ self.show_default_menu(idx, column_idx, event.globalPos()) # default
55
169
  super(OutputTabs, self).mousePressEvent(event)
56
170
 
57
- def get_common_actions(self, index):
171
+ def prepare_menu(self, index: int, column_idx: int) -> QMenu:
58
172
  """
59
- Get common actions
173
+ Prepare and return context menu
60
174
 
61
175
  :param index: index
62
- :return: dict
176
+ :param column_idx: column index
177
+ :return: menu
63
178
  """
179
+ menu = QMenu(self)
180
+
64
181
  actions = {}
65
182
  actions['add_chat'] = QAction(QIcon(":/icons/add.svg"), trans('action.tab.add.chat'), self)
66
183
  actions['add_chat'].triggered.connect(
67
- lambda: self.add_tab(index, Tab.TAB_CHAT)
184
+ lambda: self.add_tab(index, column_idx, Tab.TAB_CHAT)
68
185
  )
69
186
  actions['add_notepad'] = QAction(QIcon(":/icons/add.svg"), trans('action.tab.add.notepad'), self)
70
187
  actions['add_notepad'].triggered.connect(
71
- lambda: self.add_tab(index, Tab.TAB_NOTEPAD)
188
+ lambda: self.add_tab(index, column_idx, Tab.TAB_NOTEPAD)
72
189
  )
73
190
  actions['edit'] = QAction(QIcon(":/icons/edit.svg"), trans('action.rename'), self)
74
191
  actions['edit'].triggered.connect(
75
- lambda: self.rename_tab(index)
192
+ lambda: self.rename_tab(index, column_idx)
193
+ )
194
+ actions['move_right'] = QAction(QIcon(":/icons/forward"), trans('action.tab.move.right'), self)
195
+ actions['move_right'].triggered.connect(
196
+ lambda: self.window.controller.ui.tabs.move_tab(index, column_idx, 1)
76
197
  )
77
- return actions
198
+ actions['move_left'] = QAction(QIcon(":/icons/back"), trans('action.tab.move.left'), self)
199
+ actions['move_left'].triggered.connect(
200
+ lambda: self.window.controller.ui.tabs.move_tab(index, column_idx, 0)
201
+ )
202
+
203
+ # add chat, add notepad
204
+ menu.addAction(actions['add_chat'])
205
+ menu.addAction(actions['add_notepad'])
206
+
207
+ # add tools submenu
208
+ self.window.controller.tools.append_tab_menu(self, menu, index, column_idx)
209
+
210
+ # rename tab
211
+ menu.addAction(actions['edit'])
212
+
213
+ # move tab left, move tab right
214
+ if column_idx != 0:
215
+ menu.addAction(actions['move_left'])
216
+ if column_idx != 1:
217
+ menu.addAction(actions['move_right'])
78
218
 
79
- def show_notepad_menu(self, index, global_pos):
219
+ return menu
220
+
221
+ def show_notepad_menu(self, index: int, column_idx: int, global_pos):
80
222
  """
81
223
  Show notepad menu
82
224
 
83
225
  :param index: index
226
+ :param column_idx: column index
84
227
  :param global_pos: global position
85
228
  """
86
- context_menu = QMenu()
87
- actions = self.get_common_actions(index)
229
+ context_menu = self.prepare_menu(index, column_idx)
230
+ actions = {}
88
231
  actions['close'] = QAction(QIcon(":/icons/close.svg"), trans('action.tab.close'), self)
89
232
  actions['close'].triggered.connect(
90
- lambda: self.close_tab(index)
233
+ lambda: self.close_tab(index, column_idx)
91
234
  )
92
235
  actions['close_all'] = QAction(QIcon(":/icons/close.svg"), trans('action.tab.close_all.notepad'), self)
93
236
  actions['close_all'].triggered.connect(
94
- lambda: self.close_all(Tab.TAB_NOTEPAD)
237
+ lambda: self.close_all(Tab.TAB_NOTEPAD, column_idx)
95
238
  )
96
- context_menu.addAction(actions['add_chat'])
97
- context_menu.addAction(actions['add_notepad'])
98
- context_menu.addAction(actions['edit'])
99
239
  context_menu.addAction(actions['close'])
100
240
 
101
241
  if self.window.core.tabs.count_by_type(Tab.TAB_NOTEPAD) > 1:
102
242
  context_menu.addAction(actions['close_all'])
243
+
103
244
  context_menu.exec(global_pos)
104
245
 
105
- def show_chat_menu(self, index, global_pos):
246
+ def show_chat_menu(self, index: int, column_idx: int, global_pos):
106
247
  """
107
248
  Show chat menu
108
249
 
109
250
  :param index: index
251
+ :param column_idx: column index
110
252
  :param global_pos: global position
111
253
  """
112
- context_menu = QMenu()
113
- actions = self.get_common_actions(index)
254
+ context_menu = self.prepare_menu(index, column_idx)
255
+ actions = {}
114
256
  actions['close'] = QAction(QIcon(":/icons/close.svg"), trans('action.tab.close'), self)
115
257
  actions['close'].triggered.connect(
116
- lambda: self.close_tab(index)
258
+ lambda: self.close_tab(index, column_idx)
117
259
  )
118
260
  actions['close_all'] = QAction(QIcon(":/icons/close.svg"), trans('action.tab.close_all.chat'), self)
119
261
  actions['close_all'].triggered.connect(
120
- lambda: self.close_all(Tab.TAB_CHAT)
262
+ lambda: self.close_all(Tab.TAB_CHAT, column_idx)
121
263
  )
122
- context_menu.addAction(actions['add_chat'])
123
- context_menu.addAction(actions['add_notepad'])
124
- context_menu.addAction(actions['edit'])
125
264
 
126
265
  # at least one chat tab must be open
127
266
  if self.window.core.tabs.count_by_type(Tab.TAB_CHAT) > 1:
128
267
  context_menu.addAction(actions['close'])
129
-
130
- if self.window.core.tabs.count_by_type(Tab.TAB_CHAT) > 1:
131
268
  context_menu.addAction(actions['close_all'])
132
269
 
133
270
  context_menu.exec(global_pos)
134
271
 
135
- def show_files_menu(self, index, global_pos):
272
+ def show_files_menu(self, index: int, column_idx: int, global_pos):
136
273
  """
137
274
  Show files menu
138
275
 
139
276
  :param index: index
277
+ :param column_idx: column index
140
278
  :param global_pos: global position
141
279
  """
142
- context_menu = QMenu()
143
- actions = self.get_common_actions(index)
280
+ context_menu = self.prepare_menu(index, column_idx)
281
+ actions = {}
144
282
  actions['refresh'] = QAction(QIcon(":/icons/reload.svg"), trans('action.refresh'), self)
145
283
  actions['refresh'].triggered.connect(
146
284
  lambda: self.window.controller.files.update_explorer()
147
285
  )
148
- context_menu.addAction(actions['add_chat'])
149
- context_menu.addAction(actions['add_notepad'])
150
286
  context_menu.addAction(actions['refresh'])
151
- context_menu.addAction(actions['edit'])
152
287
  context_menu.exec(global_pos)
153
288
 
154
- def show_default_menu(self, index, global_pos):
289
+ def show_tool_menu(self, index: int, column_idx: int, global_pos):
290
+ """
291
+ Show tool menu
292
+
293
+ :param index: index
294
+ :param column_idx: column index
295
+ :param global_pos: global position
296
+ """
297
+ context_menu = self.prepare_menu(index, column_idx)
298
+ actions = {}
299
+ actions['close'] = QAction(QIcon(":/icons/close.svg"), trans('action.tab.close'), self)
300
+ actions['close'].triggered.connect(
301
+ lambda: self.close_tab(index, column_idx)
302
+ )
303
+ context_menu.addAction(actions['close'])
304
+ context_menu.exec(global_pos)
305
+
306
+ def show_default_menu(self, index: int, column_idx: int, global_pos):
155
307
  """
156
308
  Show default menu
157
309
 
158
310
  :param index: index
311
+ :param column_idx: column index
159
312
  :param global_pos: global position
160
313
  """
161
- context_menu = QMenu()
162
- actions = self.get_common_actions(index)
163
- context_menu.addAction(actions['add_chat'])
164
- context_menu.addAction(actions['add_notepad'])
165
- context_menu.addAction(actions['edit'])
314
+ context_menu = self.prepare_menu(index, column_idx)
166
315
  context_menu.exec(global_pos)
167
316
 
168
317
  @Slot()
169
- def rename_tab(self, index):
318
+ def rename_tab(self, index: int, column_idx: int):
170
319
  """
171
320
  Rename tab
321
+
172
322
  :param index: index
323
+ :param column_idx: column index
173
324
  """
174
- self.window.controller.ui.tabs.rename(index)
325
+ self.window.controller.ui.tabs.rename(index, column_idx)
175
326
 
176
327
  @Slot()
177
- def close_tab(self, index):
328
+ def close_tab(self, index: int, column_idx: int):
178
329
  """
179
330
  Close tab
331
+
180
332
  :param index: index
333
+ :param column_idx: column index
181
334
  """
182
- self.window.controller.ui.tabs.close(index)
335
+ self.window.controller.ui.tabs.close(index, column_idx)
183
336
 
184
337
  @Slot()
185
- def close_all(self, type):
338
+ def close_all(self, type, column_idx: int):
186
339
  """
187
340
  Close all tabs
341
+
188
342
  :param type: type
343
+ :param column_idx: column index
189
344
  """
190
- self.window.controller.ui.tabs.close_all(type)
345
+ self.window.controller.ui.tabs.close_all(type, column_idx)
191
346
 
192
347
  @Slot()
193
- def add_tab(self, index, type):
348
+ def add_tab(self, index: int, column_idx: int, type: int, tool_id: str = None):
194
349
  """
195
- Add tab
350
+ Add a new tab
351
+
196
352
  :param index: index
353
+ :param column_idx: column index
197
354
  :param type: type
355
+ :param tool_id: tool id
198
356
  """
199
- self.window.controller.ui.tabs.append(type, index)
357
+ self.window.controller.ui.tabs.append(
358
+ type=type,
359
+ tool_id=tool_id,
360
+ idx=index,
361
+ column_idx=column_idx,
362
+ )
@@ -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.11.20 21:00:00 #
9
+ # Updated Date: 2024.12.09 00:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import re
@@ -42,6 +42,15 @@ class HtmlOutput(QWebEngineView):
42
42
  self.plain = ""
43
43
  self.html_content = ""
44
44
  self.meta = None
45
+ self.tab = None
46
+
47
+ def set_tab(self, tab):
48
+ """
49
+ Set tab
50
+
51
+ :param tab: Tab
52
+ """
53
+ self.tab = tab
45
54
 
46
55
  def set_meta(self, meta: CtxMeta):
47
56
  """
@@ -163,6 +172,7 @@ class HtmlOutput(QWebEngineView):
163
172
  if success:
164
173
  event = RenderEvent(RenderEvent.ON_PAGE_LOAD, {
165
174
  "meta": self.meta,
175
+ "tab": self.tab,
166
176
  })
167
177
  self.window.dispatch(event)
168
178
 
@@ -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.24 01:00:00 #
9
+ # Updated Date: 2024.12.09 00:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import Qt
@@ -38,6 +38,15 @@ class ChatOutput(QTextBrowser):
38
38
  self.setOpenLinks(False)
39
39
  self.anchorClicked.connect(self.open_external_link)
40
40
  self.setWordWrapMode(QTextOption.WordWrap)
41
+ self.tab = None
42
+
43
+ def set_tab(self, tab):
44
+ """
45
+ Set tab
46
+
47
+ :param tab: Tab
48
+ """
49
+ self.tab = tab
41
50
 
42
51
  def open_external_link(self, url):
43
52
  """
@@ -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.12 10:00:00 #
9
+ # Updated Date: 2024.12.07 21:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtGui import QAction, QIcon
@@ -32,6 +32,9 @@ class CtxSearchInput(QLineEdit):
32
32
  self.clear_action.triggered.connect(self.clear_search_string)
33
33
  self.addAction(self.clear_action, QLineEdit.TrailingPosition)
34
34
  self.clear_action.setVisible(False)
35
+ action = QAction(self)
36
+ action.setIcon(QIcon(":/icons/search.svg"))
37
+ self.addAction(action, QLineEdit.LeadingPosition)
35
38
 
36
39
  self.textChanged.connect(self.on_text_changed)
37
40
 
@@ -6,21 +6,24 @@
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.11.20 21:00:00 #
9
+ # Updated Date: 2024.12.09 00:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import re
13
13
 
14
- from PySide6.QtCore import Qt, QObject, Signal, Slot
14
+ from PySide6.QtCore import Qt, QObject, Signal, Slot, QEvent
15
15
  from PySide6.QtWebChannel import QWebChannel
16
16
  from PySide6.QtWebEngineCore import QWebEngineSettings, QWebEnginePage
17
17
  from PySide6.QtWebEngineWidgets import QWebEngineView
18
18
  from PySide6.QtGui import QAction, QIcon, QKeySequence
19
+ from PySide6.QtWidgets import QMenu
19
20
 
20
21
  from pygpt_net.core.events import RenderEvent
21
22
  from pygpt_net.item.ctx import CtxMeta
22
23
  from pygpt_net.core.text.web_finder import WebFinder
24
+ from pygpt_net.ui.widget.tabs.layout import FocusEventFilter
23
25
  from pygpt_net.utils import trans
26
+
24
27
  import pygpt_net.icons_rc
25
28
 
26
29
 
@@ -38,9 +41,46 @@ class ChatWebOutput(QWebEngineView):
38
41
  self.customContextMenuRequested.connect(self.on_context_menu)
39
42
  self.signals = WebEngineSignals()
40
43
  self.setContextMenuPolicy(Qt.CustomContextMenu)
44
+ self.filter = FocusEventFilter(self, self.on_focus)
45
+ self.installEventFilter(self)
41
46
  self.plain = ""
42
47
  self.html_content = ""
43
48
  self.meta = None
49
+ self.tab = None
50
+
51
+ def eventFilter(self, source, event):
52
+ """
53
+ Focus event filter
54
+
55
+ :param source: source
56
+ :param event: event
57
+ """
58
+ if (event.type() == QEvent.ChildAdded and
59
+ source is self and
60
+ event.child().isWidgetType()):
61
+ self._glwidget = event.child()
62
+ self._glwidget.installEventFilter(self)
63
+ elif (event.type() == event.Type.MouseButtonPress):
64
+ col_idx = self.tab.column_idx
65
+ self.window.controller.ui.tabs.on_column_focus(col_idx)
66
+ return super().eventFilter(source, event)
67
+
68
+ def on_focus(self, widget):
69
+ """
70
+ On widget clicked
71
+
72
+ :param widget: widget
73
+ """
74
+ self.window.controller.ui.tabs.on_column_focus(self.tab.column_idx)
75
+ self.setFocus()
76
+
77
+ def set_tab(self, tab):
78
+ """
79
+ Set tab
80
+
81
+ :param tab: Tab
82
+ """
83
+ self.tab = tab
44
84
 
45
85
  def set_meta(self, meta: CtxMeta):
46
86
  """
@@ -72,12 +112,7 @@ class ChatWebOutput(QWebEngineView):
72
112
 
73
113
  :param position: position
74
114
  """
75
- menu = self.createStandardContextMenu()
76
-
77
- # remove defaults
78
- for action in menu.actions()[::-1]:
79
- menu.removeAction(action)
80
-
115
+ menu = QMenu(self)
81
116
  selected_text = ""
82
117
  is_selection = self.page().hasSelection()
83
118
  if is_selection:
@@ -138,6 +173,10 @@ class ChatWebOutput(QWebEngineView):
138
173
  if self.window.core.config.has("zoom"):
139
174
  self.page().setZoomFactor(self.window.core.config.get("zoom"))
140
175
 
176
+ def on_focus_js(self):
177
+ """Focus JavaScript"""
178
+ self.window.controller.ui.tabs.on_column_focus(self.tab.column_idx)
179
+
141
180
  def get_zoom_value(self) -> float:
142
181
  """
143
182
  Get zoom value
@@ -165,6 +204,7 @@ class ChatWebOutput(QWebEngineView):
165
204
  if success:
166
205
  event = RenderEvent(RenderEvent.ON_PAGE_LOAD, {
167
206
  "meta": self.meta,
207
+ "tab": self.tab,
168
208
  })
169
209
  self.window.dispatch(event)
170
210
 
@@ -318,4 +358,4 @@ class WebEngineSignals(QObject):
318
358
 
319
359
 
320
360
  class WebEnginePageSignals(QObject):
321
- js_message = Signal(int, str, str) # on Javascript message
361
+ js_message = Signal(int, str, str) # on Javascript message