pygpt-net 2.4.36.post1__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 (258) hide show
  1. CHANGELOG.md +38 -1
  2. README.md +71 -114
  3. pygpt_net/CHANGELOG.txt +38 -1
  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 +39 -44
  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 +217 -64
  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/bridge/worker.py +16 -2
  105. pygpt_net/core/calendar/__init__.py +57 -11
  106. pygpt_net/core/chain/__init__.py +8 -2
  107. pygpt_net/core/chain/chat.py +10 -8
  108. pygpt_net/core/chain/completion.py +10 -7
  109. pygpt_net/core/command.py +62 -17
  110. pygpt_net/core/ctx/__init__.py +260 -58
  111. pygpt_net/core/ctx/bag.py +25 -4
  112. pygpt_net/core/ctx/container.py +28 -17
  113. pygpt_net/core/ctx/idx.py +45 -8
  114. pygpt_net/core/ctx/output.py +95 -74
  115. pygpt_net/core/ctx/reply.py +5 -2
  116. pygpt_net/core/db/__init__.py +8 -7
  117. pygpt_net/core/db/viewer.py +17 -11
  118. pygpt_net/core/debug/__init__.py +10 -9
  119. pygpt_net/core/debug/tabs.py +5 -2
  120. pygpt_net/core/docker/__init__.py +11 -5
  121. pygpt_net/core/docker/builder.py +11 -3
  122. pygpt_net/core/events/app.py +5 -3
  123. pygpt_net/core/events/base.py +11 -5
  124. pygpt_net/core/events/control.py +5 -3
  125. pygpt_net/core/events/event.py +18 -7
  126. pygpt_net/core/events/kernel.py +5 -3
  127. pygpt_net/core/events/render.py +5 -3
  128. pygpt_net/core/experts/__init__.py +5 -4
  129. pygpt_net/core/filesystem/__init__.py +52 -34
  130. pygpt_net/core/filesystem/actions.py +8 -5
  131. pygpt_net/core/filesystem/editor.py +13 -3
  132. pygpt_net/core/filesystem/types.py +12 -7
  133. pygpt_net/core/filesystem/url.py +7 -3
  134. pygpt_net/core/idx/__init__.py +34 -25
  135. pygpt_net/core/idx/chat.py +60 -38
  136. pygpt_net/core/idx/context.py +6 -2
  137. pygpt_net/core/idx/indexing.py +84 -35
  138. pygpt_net/core/idx/llm.py +11 -3
  139. pygpt_net/core/idx/metadata.py +13 -3
  140. pygpt_net/core/idx/types/ctx.py +32 -6
  141. pygpt_net/core/idx/types/external.py +41 -7
  142. pygpt_net/core/idx/types/files.py +31 -6
  143. pygpt_net/core/image.py +15 -4
  144. pygpt_net/core/llm/__init__.py +13 -3
  145. pygpt_net/core/locale.py +34 -8
  146. pygpt_net/core/models.py +4 -3
  147. pygpt_net/core/notepad.py +9 -4
  148. pygpt_net/core/plugins.py +7 -6
  149. pygpt_net/core/presets.py +19 -10
  150. pygpt_net/core/profile.py +12 -6
  151. pygpt_net/core/prompt/__init__.py +10 -3
  152. pygpt_net/core/prompt/custom.py +7 -6
  153. pygpt_net/core/prompt/template.py +9 -3
  154. pygpt_net/core/render/base.py +117 -22
  155. pygpt_net/core/render/markdown/body.py +27 -7
  156. pygpt_net/core/render/markdown/renderer.py +119 -22
  157. pygpt_net/core/render/plain/body.py +22 -5
  158. pygpt_net/core/render/plain/renderer.py +97 -21
  159. pygpt_net/core/render/web/body.py +75 -25
  160. pygpt_net/core/render/web/renderer.py +313 -63
  161. pygpt_net/core/settings.py +9 -4
  162. pygpt_net/core/tabs/__init__.py +290 -103
  163. pygpt_net/core/tabs/tab.py +17 -4
  164. pygpt_net/core/tokens.py +44 -11
  165. pygpt_net/core/updater/__init__.py +20 -7
  166. pygpt_net/core/vision/analyzer.py +29 -6
  167. pygpt_net/core/web.py +130 -2
  168. pygpt_net/data/config/config.json +19 -6
  169. pygpt_net/data/config/models.json +3 -3
  170. pygpt_net/data/config/modes.json +3 -3
  171. pygpt_net/data/config/settings.json +81 -10
  172. pygpt_net/data/config/settings_section.json +3 -0
  173. pygpt_net/data/css/style.light.css +1 -0
  174. pygpt_net/data/css/{web.css → web-blocks.css} +162 -133
  175. pygpt_net/data/css/{web.light.css → web-blocks.light.css} +7 -0
  176. pygpt_net/data/css/web-chatgpt.css +350 -0
  177. pygpt_net/data/css/web-chatgpt.dark.css +64 -0
  178. pygpt_net/data/css/web-chatgpt.light.css +75 -0
  179. pygpt_net/data/css/web-chatgpt_wide.css +350 -0
  180. pygpt_net/data/css/web-chatgpt_wide.dark.css +64 -0
  181. pygpt_net/data/css/web-chatgpt_wide.light.css +75 -0
  182. pygpt_net/data/icons/split_screen.svg +1 -0
  183. pygpt_net/data/locale/locale.de.ini +19 -3
  184. pygpt_net/data/locale/locale.en.ini +30 -11
  185. pygpt_net/data/locale/locale.es.ini +19 -3
  186. pygpt_net/data/locale/locale.fr.ini +19 -3
  187. pygpt_net/data/locale/locale.it.ini +19 -3
  188. pygpt_net/data/locale/locale.pl.ini +20 -4
  189. pygpt_net/data/locale/locale.uk.ini +19 -3
  190. pygpt_net/data/locale/locale.zh.ini +20 -4
  191. pygpt_net/data/locale/plugin.cmd_web.de.ini +2 -0
  192. pygpt_net/data/locale/plugin.cmd_web.en.ini +20 -10
  193. pygpt_net/data/locale/plugin.cmd_web.es.ini +2 -0
  194. pygpt_net/data/locale/plugin.cmd_web.fr.ini +2 -0
  195. pygpt_net/data/locale/plugin.cmd_web.it.ini +2 -0
  196. pygpt_net/data/locale/plugin.cmd_web.pl.ini +2 -0
  197. pygpt_net/data/locale/plugin.cmd_web.uk.ini +2 -0
  198. pygpt_net/data/locale/plugin.cmd_web.zh.ini +2 -0
  199. pygpt_net/icons.qrc +1 -0
  200. pygpt_net/icons_rc.py +165 -136
  201. pygpt_net/item/ctx.py +46 -24
  202. pygpt_net/plugin/audio_input/simple.py +21 -5
  203. pygpt_net/plugin/audio_output/__init__.py +4 -1
  204. pygpt_net/plugin/base/config.py +4 -2
  205. pygpt_net/plugin/base/plugin.py +26 -6
  206. pygpt_net/plugin/base/worker.py +37 -9
  207. pygpt_net/plugin/cmd_code_interpreter/__init__.py +39 -37
  208. pygpt_net/plugin/cmd_code_interpreter/runner.py +25 -12
  209. pygpt_net/plugin/cmd_web/__init__.py +46 -6
  210. pygpt_net/plugin/cmd_web/config.py +74 -48
  211. pygpt_net/plugin/cmd_web/websearch.py +61 -28
  212. pygpt_net/plugin/cmd_web/worker.py +79 -13
  213. pygpt_net/plugin/idx_llama_index/__init__.py +2 -2
  214. pygpt_net/plugin/real_time/__init__.py +2 -2
  215. pygpt_net/provider/core/config/patch.py +54 -1
  216. pygpt_net/provider/core/ctx/base.py +4 -1
  217. pygpt_net/provider/core/ctx/db_sqlite/__init__.py +10 -1
  218. pygpt_net/provider/core/ctx/db_sqlite/storage.py +22 -1
  219. pygpt_net/provider/gpt/assistants.py +10 -9
  220. pygpt_net/provider/gpt/audio.py +3 -2
  221. pygpt_net/provider/gpt/chat.py +8 -7
  222. pygpt_net/provider/gpt/completion.py +6 -4
  223. pygpt_net/provider/gpt/image.py +9 -2
  224. pygpt_net/provider/gpt/store.py +14 -13
  225. pygpt_net/provider/gpt/vision.py +6 -5
  226. pygpt_net/tools/__init__.py +9 -1
  227. pygpt_net/tools/base.py +15 -1
  228. pygpt_net/tools/code_interpreter/__init__.py +174 -75
  229. pygpt_net/tools/code_interpreter/ui/dialogs.py +21 -103
  230. pygpt_net/tools/code_interpreter/ui/widgets.py +284 -9
  231. pygpt_net/tools/html_canvas/__init__.py +78 -23
  232. pygpt_net/tools/html_canvas/ui/dialogs.py +46 -62
  233. pygpt_net/tools/html_canvas/ui/widgets.py +96 -3
  234. pygpt_net/ui/base/context_menu.py +2 -2
  235. pygpt_net/ui/layout/chat/input.py +10 -18
  236. pygpt_net/ui/layout/chat/output.py +26 -44
  237. pygpt_net/ui/layout/ctx/ctx_list.py +13 -4
  238. pygpt_net/ui/layout/toolbox/footer.py +18 -2
  239. pygpt_net/ui/main.py +2 -2
  240. pygpt_net/ui/menu/config.py +7 -11
  241. pygpt_net/ui/menu/debug.py +11 -1
  242. pygpt_net/ui/menu/theme.py +9 -2
  243. pygpt_net/ui/widget/filesystem/explorer.py +2 -2
  244. pygpt_net/ui/widget/lists/context.py +27 -5
  245. pygpt_net/ui/widget/tabs/Input.py +2 -2
  246. pygpt_net/ui/widget/tabs/body.py +2 -1
  247. pygpt_net/ui/widget/tabs/layout.py +195 -0
  248. pygpt_net/ui/widget/tabs/output.py +218 -55
  249. pygpt_net/ui/widget/textarea/html.py +11 -1
  250. pygpt_net/ui/widget/textarea/output.py +10 -1
  251. pygpt_net/ui/widget/textarea/search_input.py +4 -1
  252. pygpt_net/ui/widget/textarea/web.py +49 -9
  253. {pygpt_net-2.4.36.post1.dist-info → pygpt_net-2.4.41.dist-info}/METADATA +72 -115
  254. {pygpt_net-2.4.36.post1.dist-info → pygpt_net-2.4.41.dist-info}/RECORD +258 -250
  255. /pygpt_net/data/css/{web.dark.css → web-blocks.dark.css} +0 -0
  256. {pygpt_net-2.4.36.post1.dist-info → pygpt_net-2.4.41.dist-info}/LICENSE +0 -0
  257. {pygpt_net-2.4.36.post1.dist-info → pygpt_net-2.4.41.dist-info}/WHEEL +0 -0
  258. {pygpt_net-2.4.36.post1.dist-info → pygpt_net-2.4.41.dist-info}/entry_points.txt +0 -0
@@ -6,20 +6,277 @@
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.03.26 15:00:00 #
9
+ # Updated Date: 2024.12.12 01:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6 import QtCore
13
- from PySide6.QtCore import Qt
14
- from PySide6.QtWidgets import QTextEdit, QApplication
13
+ from PySide6.QtCore import Qt, QObject, Signal, Slot
14
+ from PySide6.QtGui import QTextCursor
15
+ from PySide6.QtWidgets import QTextEdit, QApplication, QVBoxLayout, QLabel, QCheckBox, QPushButton, QWidget, QSplitter, \
16
+ QHBoxLayout
15
17
 
16
18
  from pygpt_net.ui.widget.textarea.editor import BaseCodeEditor
17
19
 
20
+ from pygpt_net.utils import trans
18
21
  import pygpt_net.icons_rc
19
22
 
20
23
 
24
+ class ToolWidget:
25
+ def __init__(self, window=None, tool=None):
26
+ """
27
+ Python Code Interpreter
28
+
29
+ :param window: Window instance
30
+ :param tool: Tool instance
31
+ """
32
+ self.window = window # window instance
33
+ self.tool = tool # tool instance
34
+ self.history = None # history
35
+ self.input = None # input
36
+ self.output = None # output
37
+ self.checkbox_all = None # all checkbox
38
+ self.checkbox_auto_clear = None # auto clear checkbox
39
+ self.checkbox_ipython = None # IPython checkbox
40
+ self.btn_send = None # send button
41
+ self.btn_clear = None # clear button
42
+ self.label_output = None # output label
43
+ self.label_history = None # history label
44
+
45
+ def setup(self, all: bool = True) -> QVBoxLayout:
46
+ """
47
+ Setup widget body
48
+
49
+ :param all: with all widgets
50
+ :return: QVBoxLayout
51
+ """
52
+ self.output = PythonOutput(self.window, self.tool)
53
+ self.output.setReadOnly(True)
54
+
55
+ if all:
56
+ self.history = PythonOutput(self.window, self.tool)
57
+ self.history.textChanged.connect(
58
+ lambda: self.tool.store_history(self)
59
+ )
60
+ self.history.setReadOnly(False)
61
+ self.history.excluded_copy_to = ["interpreter_edit"]
62
+
63
+ self.label_output = QLabel(trans("interpreter.edit_label.output"))
64
+ self.label_history = QLabel(trans("interpreter.edit_label.edit"))
65
+
66
+ if all:
67
+ self.checkbox_all = QCheckBox(trans("interpreter.all"))
68
+ self.checkbox_all.setChecked(True)
69
+ self.checkbox_all.clicked.connect(
70
+ lambda: self.tool.toggle_all(self)
71
+ )
72
+
73
+ self.checkbox_auto_clear = QCheckBox(trans("interpreter.auto_clear"))
74
+ self.checkbox_auto_clear.setChecked(False)
75
+ self.checkbox_auto_clear.clicked.connect(
76
+ lambda: self.tool.toggle_auto_clear(self)
77
+ )
78
+
79
+ self.checkbox_ipython = QCheckBox("IPython")
80
+ self.checkbox_ipython.setChecked(True)
81
+ self.checkbox_ipython.clicked.connect(
82
+ lambda: self.tool.toggle_ipython(self)
83
+ )
84
+
85
+ self.btn_clear = QPushButton(trans("interpreter.btn.clear"))
86
+ self.btn_clear.clicked.connect(
87
+ lambda: self.tool.clear(self)
88
+ )
89
+
90
+ self.btn_send = QPushButton(trans("interpreter.btn.send"))
91
+ self.btn_send.clicked.connect(
92
+ lambda: self.tool.send_input(self)
93
+ )
94
+
95
+ self.input = PythonInput(self.window, self.tool, self)
96
+ self.input.setPlaceholderText(trans("interpreter.input.placeholder"))
97
+ self.input.excluded_copy_to = ["interpreter_input"]
98
+
99
+ left_layout = QVBoxLayout()
100
+ left_layout.addWidget(self.label_output)
101
+ left_layout.addWidget(self.output)
102
+ left_layout.setContentsMargins(0, 0, 0, 0)
103
+ left_widget = QWidget()
104
+ left_widget.setLayout(left_layout)
105
+
106
+ self.window.ui.splitters['interpreter.columns'] = QSplitter(Qt.Horizontal)
107
+ self.window.ui.splitters['interpreter.columns'].addWidget(left_widget)
108
+
109
+ if all:
110
+ right_layout = QVBoxLayout()
111
+ right_layout.addWidget(self.label_history)
112
+ right_layout.addWidget(self.history)
113
+ right_layout.setContentsMargins(0, 0, 0, 0)
114
+ right_widget = QWidget()
115
+ right_widget.setLayout(right_layout)
116
+ right_widget.setMinimumWidth(300)
117
+ self.window.ui.splitters['interpreter.columns'].addWidget(right_widget)
118
+
119
+ bottom_layout = QHBoxLayout()
120
+ bottom_layout.addWidget(self.btn_clear)
121
+ bottom_layout.addWidget(self.checkbox_ipython)
122
+ bottom_layout.addWidget(self.checkbox_auto_clear)
123
+ bottom_layout.addStretch()
124
+ if all:
125
+ bottom_layout.addWidget(self.checkbox_all)
126
+ bottom_layout.addWidget(self.btn_send)
127
+
128
+ edit_layout = QVBoxLayout()
129
+ edit_layout.addWidget(self.window.ui.splitters['interpreter.columns'])
130
+ edit_layout.setContentsMargins(0, 0, 0, 0)
131
+ edit_widget = QWidget()
132
+ edit_widget.setLayout(edit_layout)
133
+
134
+ self.window.ui.splitters['interpreter'] = QSplitter(Qt.Vertical)
135
+ self.window.ui.splitters['interpreter'].addWidget(edit_widget)
136
+ self.window.ui.splitters['interpreter'].addWidget(self.input)
137
+ self.window.ui.splitters['interpreter'].setStretchFactor(0, 4)
138
+ self.window.ui.splitters['interpreter'].setStretchFactor(1, 1)
139
+
140
+ # connect signals
141
+ self.tool.signals.update.connect(self.set_output)
142
+ self.tool.signals.update_history.connect(self.set_history)
143
+ self.tool.signals.clear_history.connect(self.clear_history)
144
+ self.tool.signals.clear_output.connect(self.clear_output)
145
+ self.tool.signals.focus_input.connect(self.set_focus)
146
+ self.tool.signals.append_input.connect(self.append_to_input)
147
+ self.tool.signals.update_input.connect(self.set_input)
148
+ self.tool.signals.set_checkbox_all.connect(self.set_checkbox_all)
149
+ self.tool.signals.set_checkbox_auto_clear.connect(self.set_checkbox_auto_clear)
150
+ self.tool.signals.set_checkbox_ipython.connect(self.set_checkbox_ipython)
151
+ self.tool.signals.toggle_all_visible.connect(self.toggle_all_visible)
152
+
153
+ layout = QVBoxLayout()
154
+ layout.addWidget(self.window.ui.splitters['interpreter'])
155
+ layout.addLayout(bottom_layout)
156
+ return layout
157
+
158
+ @Slot(str, str)
159
+ def set_output(self, output: str, type="stdout"):
160
+ """
161
+ Set output content
162
+
163
+ :param output: Content
164
+ :param type: Output type
165
+ """
166
+ area = self.output
167
+ if type == "stdin":
168
+ data = ">> " + str(output)
169
+ else:
170
+ data = str(output)
171
+ cur = area.textCursor()
172
+ cur.movePosition(QTextCursor.End)
173
+ s = data + "\n"
174
+ while s:
175
+ head, sep, s = s.partition("\n")
176
+ cur.insertText(head)
177
+ if sep: # New line if LF
178
+ cur.insertText("\n")
179
+ area.setTextCursor(cur)
180
+
181
+ @Slot()
182
+ def set_history(self, data: str):
183
+ """
184
+ Set history
185
+
186
+ :param data: history data
187
+ """
188
+ if not self.history:
189
+ return
190
+ self.history.setPlainText(data)
191
+ cur = self.history.textCursor()
192
+ cur.movePosition(QTextCursor.End)
193
+ self.history.setTextCursor(cur)
194
+
195
+ @Slot()
196
+ def append_to_input(self, data: str):
197
+ """
198
+ Append data to input
199
+
200
+ :param data: Data
201
+ """
202
+ current = self.input.toPlainText()
203
+ if current:
204
+ current += "\n"
205
+ current += data
206
+ self.input.setPlainText(current)
207
+
208
+ @Slot(str)
209
+ def set_input(self, data: str):
210
+ """
211
+ Set input
212
+
213
+ :param data: Data
214
+ """
215
+ self.input.setPlainText(data)
216
+
217
+ @Slot()
218
+ def clear_history(self):
219
+ """Clear input"""
220
+ if not self.history:
221
+ return
222
+ self.history.clear()
223
+
224
+ @Slot()
225
+ def clear_output(self):
226
+ """Clear output"""
227
+ self.output.clear()
228
+
229
+ @Slot()
230
+ def set_focus(self):
231
+ """Set focus to input"""
232
+ self.input.setFocus()
233
+
234
+ @Slot(bool)
235
+ def set_checkbox_all(self, value: bool):
236
+ """
237
+ Set checkbox all
238
+
239
+ :param value: Value
240
+ """
241
+ if not self.checkbox_all:
242
+ return
243
+ self.checkbox_all.setChecked(value)
244
+
245
+ @Slot(bool)
246
+ def set_checkbox_auto_clear(self, value: bool):
247
+ """
248
+ Set checkbox auto clear
249
+
250
+ :param value: Value
251
+ """
252
+ self.checkbox_auto_clear.setChecked(value)
253
+
254
+ @Slot(bool)
255
+ def set_checkbox_ipython(self, value: bool):
256
+ """
257
+ Set checkbox IPython
258
+
259
+ :param value: Value
260
+ """
261
+ self.checkbox_ipython.setChecked(value)
262
+
263
+ @Slot(bool)
264
+ def toggle_all_visible(self, value: bool):
265
+ """
266
+ Toggle all visible
267
+
268
+ :param value: Value
269
+ """
270
+ if not self.checkbox_all:
271
+ return
272
+ if value:
273
+ self.checkbox_all.setVisible(True)
274
+ else:
275
+ self.checkbox_all.setVisible(False)
276
+
277
+
21
278
  class PythonInput(QTextEdit):
22
- def __init__(self, window=None):
279
+ def __init__(self, window=None, tool=None, widget=None):
23
280
  """
24
281
  Python interpreter input
25
282
 
@@ -27,6 +284,8 @@ class PythonInput(QTextEdit):
27
284
  """
28
285
  super(PythonInput, self).__init__(window)
29
286
  self.window = window
287
+ self.tool = tool
288
+ self.widget = widget
30
289
  self.setAcceptRichText(False)
31
290
  self.value = 12
32
291
  self.max_font_size = 42
@@ -35,7 +294,7 @@ class PythonInput(QTextEdit):
35
294
  self.default_stylesheet = ""
36
295
  self.setStyleSheet(self.default_stylesheet)
37
296
  self.textChanged.connect(
38
- lambda: self.window.tools.get("interpreter").update_input()
297
+ lambda: self.tool.update_input()
39
298
  )
40
299
  self.setFocus()
41
300
 
@@ -60,11 +319,11 @@ class PythonInput(QTextEdit):
60
319
  if mode == 2: # Shift + Enter
61
320
  modifiers = QApplication.keyboardModifiers()
62
321
  if modifiers == QtCore.Qt.ShiftModifier:
63
- self.window.tools.get("interpreter").send_input()
322
+ self.tool.send_input(self.widget)
64
323
  else: # Enter
65
324
  modifiers = QApplication.keyboardModifiers()
66
325
  if modifiers != QtCore.Qt.ShiftModifier:
67
- self.window.tools.get("interpreter").send_input()
326
+ self.tool.send_input(self.widget)
68
327
  self.setFocus()
69
328
 
70
329
  def wheelEvent(self, event):
@@ -89,7 +348,7 @@ class PythonInput(QTextEdit):
89
348
 
90
349
 
91
350
  class PythonOutput(BaseCodeEditor):
92
- def __init__(self, window=None):
351
+ def __init__(self, window=None, tool=None):
93
352
  """
94
353
  Python interpreter output
95
354
 
@@ -97,6 +356,7 @@ class PythonOutput(BaseCodeEditor):
97
356
  """
98
357
  super(PythonOutput, self).__init__(window)
99
358
  self.window = window
359
+ self.tool = tool
100
360
  self.setReadOnly(True)
101
361
  self.value = 12
102
362
  self.max_font_size = 42
@@ -108,4 +368,19 @@ class PythonOutput(BaseCodeEditor):
108
368
  def clear_content(self):
109
369
  """Clear content"""
110
370
  super(PythonOutput, self).clear_content()
111
- self.window.tools.get("interpreter").save_output()
371
+ self.tool.save_output()
372
+
373
+
374
+ class ToolSignals(QObject):
375
+ update = Signal(str, str) # content, type
376
+ update_history = Signal(str) # content
377
+ update_input = Signal(str) # content
378
+ append_input = Signal(str) # content
379
+ reload = Signal(str) # path
380
+ clear_history = Signal()
381
+ clear_output = Signal()
382
+ focus_input = Signal()
383
+ set_checkbox_all = Signal(bool)
384
+ set_checkbox_auto_clear = Signal(bool)
385
+ set_checkbox_ipython = Signal(bool)
386
+ toggle_all_visible = Signal(bool)
@@ -6,36 +6,45 @@
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.11 23:00:00 #
9
+ # Updated Date: 2024.12.12 04:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
13
13
 
14
- from PySide6.QtCore import QTimer, Slot, QUrl
14
+ from PySide6.QtCore import QTimer, Slot
15
15
  from PySide6.QtGui import QAction, QIcon
16
- from PySide6.QtWidgets import QFileDialog
16
+ from PySide6.QtWidgets import QFileDialog, QWidget
17
17
 
18
+ from pygpt_net.core.tabs.tab import Tab
18
19
  from pygpt_net.core.text.utils import output_clean_html, output_html2text
19
20
  from pygpt_net.tools.base import BaseTool
20
- from pygpt_net.tools.html_canvas.ui.dialogs import Canvas
21
21
  from pygpt_net.utils import trans
22
22
 
23
+ from .ui.dialogs import Tool
24
+ from .ui.widgets import ToolWidget, ToolSignals
25
+
23
26
 
24
27
  class HtmlCanvas(BaseTool):
25
28
  def __init__(self, *args, **kwargs):
26
29
  """
27
- HTML/JS canvas
30
+ HTML/JS Canvas
28
31
 
29
32
  :param window: Window instance
30
33
  """
31
34
  super(HtmlCanvas, self).__init__(*args, **kwargs)
32
35
  self.id = "html_canvas"
36
+ self.dialog_id = "html_canvas"
37
+ self.has_tab = True
38
+ self.tab_title = "menu.tools.html_canvas"
39
+ self.tab_icon = ":/icons/code.svg"
33
40
  self.opened = False
34
41
  self.is_edit = False
35
42
  self.auto_clear = True
36
43
  self.dialog = None
37
44
  self.is_edit = False
45
+ self.auto_opened = False
38
46
  self.file_output = ".canvas.html"
47
+ self.signals = ToolSignals()
39
48
 
40
49
  def setup(self):
41
50
  """Setup"""
@@ -50,16 +59,19 @@ class HtmlCanvas(BaseTool):
50
59
  """Update menu"""
51
60
  self.update_menu()
52
61
 
53
- def toggle_edit(self):
54
- """Toggle edit mode"""
62
+ def toggle_edit(self, widget: ToolWidget):
63
+ """
64
+ Toggle edit mode
65
+
66
+ :param widget: Canvas widget
67
+ """
55
68
  current = self.is_edit
56
69
  self.is_edit = not self.is_edit
57
- self.window.ui.nodes['html_canvas.edit'].setVisible(self.is_edit)
58
- self.window.ui.nodes['html_canvas.output'].setVisible(not self.is_edit)
70
+ widget.edit.setVisible(self.is_edit)
71
+ widget.output.setVisible(not self.is_edit)
59
72
  if current:
60
- self.set_output(self.window.ui.nodes['html_canvas.edit'].toPlainText())
73
+ self.set_output(widget.edit.toPlainText())
61
74
  self.save_output()
62
- self.window.ui.nodes['html_canvas.btn.edit'].setChecked(self.is_edit)
63
75
 
64
76
  def update_menu(self):
65
77
  """Update menu"""
@@ -78,18 +90,26 @@ class HtmlCanvas(BaseTool):
78
90
  """
79
91
  return os.path.join(self.window.core.config.get_user_dir("data"), self.file_output)
80
92
 
93
+ def get_dialog_id(self) -> str:
94
+ """
95
+ Get dialog ID
96
+
97
+ :return: Dialog ID
98
+ """
99
+ return self.dialog_id
100
+
81
101
  def set_output(self, output: str):
82
102
  """
83
103
  Set output HTML
84
104
 
85
105
  :param output: Output HTML code
86
106
  """
87
- path = os.path.join(self.window.core.config.get_user_dir("data"), self.file_output)
107
+ path = self.get_current_path()
88
108
  with open(path, "w", encoding="utf-8") as f:
89
109
  f.write(output)
90
110
  if os.path.exists(path):
91
- self.window.ui.nodes['html_canvas.output'].setUrl(QUrl().fromLocalFile(path))
92
- self.window.ui.nodes['html_canvas.edit'].setPlainText(output)
111
+ self.signals.reload.emit(path)
112
+ self.signals.update.emit(output)
93
113
 
94
114
  def reload_output(self):
95
115
  """Reload output data"""
@@ -102,11 +122,16 @@ class HtmlCanvas(BaseTool):
102
122
 
103
123
  :return: Output data
104
124
  """
105
- return self.window.ui.nodes['html_canvas.edit'].toPlainText()
125
+ path = self.get_current_path()
126
+ data = ""
127
+ if os.path.exists(path):
128
+ with open(path, "r", encoding="utf-8") as f:
129
+ data = f.read()
130
+ return data
106
131
 
107
132
  def load_output(self):
108
133
  """Load output data from file"""
109
- path = os.path.join(self.window.core.config.get_user_dir("data"), self.file_output)
134
+ path = self.get_current_path()
110
135
  data = ""
111
136
  if os.path.exists(path):
112
137
  with open(path, "r", encoding="utf-8") as f:
@@ -115,14 +140,14 @@ class HtmlCanvas(BaseTool):
115
140
 
116
141
  def save_output(self):
117
142
  """Save output data to file"""
118
- path = os.path.join(self.window.core.config.get_user_dir("data"), self.file_output)
143
+ path = self.get_current_path()
119
144
  data = self.get_output()
120
145
  with open(path, "w", encoding="utf-8") as f:
121
146
  f.write(data)
122
147
 
123
148
  def clear_output(self):
124
149
  """Clear output"""
125
- path = os.path.join(self.window.core.config.get_user_dir("data"), self.file_output)
150
+ path = self.get_current_path()
126
151
  if os.path.exists(path):
127
152
  os.remove(path)
128
153
  self.set_output("")
@@ -151,9 +176,17 @@ class HtmlCanvas(BaseTool):
151
176
  if not self.opened:
152
177
  self.opened = True
153
178
  self.load_output()
154
- self.window.ui.dialogs.open('html_canvas', width=800, height=600)
179
+ self.window.ui.dialogs.open(self.dialog_id, width=800, height=600)
155
180
  self.update()
156
181
 
182
+ def auto_open(self):
183
+ """Auto open canvas dialog"""
184
+ if self.window.controller.ui.tabs.is_current_tool(self.id):
185
+ return # do not open if already opened in tab
186
+ if not self.auto_opened:
187
+ self.auto_opened = True
188
+ self.open()
189
+
157
190
  def open_file(self):
158
191
  """Open file dialog"""
159
192
  last_dir = self.window.core.config.get_last_used_dir()
@@ -172,7 +205,7 @@ class HtmlCanvas(BaseTool):
172
205
  def close(self):
173
206
  """Close HTML canvas dialog"""
174
207
  self.opened = False
175
- self.window.ui.dialogs.close('html_canvas')
208
+ self.window.ui.dialogs.close(self.dialog_id)
176
209
  self.update()
177
210
 
178
211
  def toggle(self):
@@ -193,13 +226,21 @@ class HtmlCanvas(BaseTool):
193
226
  else:
194
227
  self.close()
195
228
 
229
+ def get_toolbar_icon(self) -> QWidget:
230
+ """
231
+ Get toolbar icon
232
+
233
+ :return: QWidget
234
+ """
235
+ return self.window.ui.nodes['icon.html_canvas']
236
+
196
237
  def toggle_icon(self, state: bool):
197
238
  """
198
239
  Toggle canvas icon
199
240
 
200
241
  :param state: State
201
242
  """
202
- self.window.ui.nodes['icon.html_canvas'].setVisible(state)
243
+ self.get_toolbar_icon().setVisible(state)
203
244
 
204
245
  def get_current_output(self) -> str:
205
246
  """
@@ -207,7 +248,7 @@ class HtmlCanvas(BaseTool):
207
248
 
208
249
  :return: Output data
209
250
  """
210
- return self.window.ui.nodes['html_canvas.output'].get_html_content()
251
+ return self.get_output()
211
252
 
212
253
  @Slot(str, str)
213
254
  def handle_save_as(self, text: str, type: str = 'txt'):
@@ -242,9 +283,23 @@ class HtmlCanvas(BaseTool):
242
283
  )
243
284
  return actions
244
285
 
286
+ def as_tab(self, tab: Tab) -> QWidget:
287
+ """
288
+ Spawn and return tab instance
289
+
290
+ :param tab: Parent Tab instance
291
+ :return: Tab widget instance
292
+ """
293
+ canvas = Tool(window=self.window, tool=self)
294
+ layout = canvas.widget.setup()
295
+ widget = QWidget()
296
+ widget.setLayout(layout)
297
+ self.load_output()
298
+ return widget
299
+
245
300
  def setup_dialogs(self):
246
301
  """Setup dialogs (static)"""
247
- self.dialog = Canvas(self.window)
302
+ self.dialog = Tool(window=self.window, tool=self)
248
303
  self.dialog.setup()
249
304
 
250
305
  def setup_theme(self):