pygpt-net 2.6.57__py3-none-any.whl → 2.6.58__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 (35) hide show
  1. pygpt_net/CHANGELOG.txt +4 -0
  2. pygpt_net/__init__.py +1 -1
  3. pygpt_net/app.py +30 -25
  4. pygpt_net/controller/debug/debug.py +3 -3
  5. pygpt_net/controller/dialogs/info.py +6 -2
  6. pygpt_net/controller/ui/tabs.py +17 -0
  7. pygpt_net/core/filesystem/url.py +5 -2
  8. pygpt_net/data/config/config.json +3 -2
  9. pygpt_net/data/config/models.json +2 -2
  10. pygpt_net/data/config/settings.json +41 -2
  11. pygpt_net/data/js/app/ui.js +1 -1
  12. pygpt_net/data/js/app.min.js +2 -2
  13. pygpt_net/data/locale/locale.de.ini +5 -1
  14. pygpt_net/data/locale/locale.en.ini +5 -1
  15. pygpt_net/data/locale/locale.es.ini +5 -1
  16. pygpt_net/data/locale/locale.fr.ini +5 -1
  17. pygpt_net/data/locale/locale.it.ini +5 -1
  18. pygpt_net/data/locale/locale.pl.ini +5 -1
  19. pygpt_net/data/locale/locale.uk.ini +5 -1
  20. pygpt_net/data/locale/locale.zh.ini +5 -1
  21. pygpt_net/js_rc.py +5 -5
  22. pygpt_net/plugin/base/plugin.py +3 -5
  23. pygpt_net/provider/core/config/patch.py +8 -1
  24. pygpt_net/tools/html_canvas/ui/widgets.py +19 -18
  25. pygpt_net/tools/web_browser/__init__.py +12 -0
  26. pygpt_net/tools/web_browser/tool.py +232 -0
  27. pygpt_net/tools/web_browser/ui/__init__.py +0 -0
  28. pygpt_net/tools/web_browser/ui/dialogs.py +123 -0
  29. pygpt_net/tools/web_browser/ui/widgets.py +351 -0
  30. pygpt_net/ui/widget/textarea/html.py +172 -24
  31. {pygpt_net-2.6.57.dist-info → pygpt_net-2.6.58.dist-info}/METADATA +18 -2
  32. {pygpt_net-2.6.57.dist-info → pygpt_net-2.6.58.dist-info}/RECORD +35 -30
  33. {pygpt_net-2.6.57.dist-info → pygpt_net-2.6.58.dist-info}/LICENSE +0 -0
  34. {pygpt_net-2.6.57.dist-info → pygpt_net-2.6.58.dist-info}/WHEEL +0 -0
  35. {pygpt_net-2.6.57.dist-info → pygpt_net-2.6.58.dist-info}/entry_points.txt +0 -0
@@ -833,6 +833,7 @@ menu.tools.interpreter = Інтерпретатор коду Python
833
833
  menu.tools.media.player = Медіаплеєр
834
834
  menu.tools.text.editor = Редактор текстів
835
835
  menu.tools.translator = Перекладач
836
+ menu.tools.web_browser = Веб-браузер (Chromium)
836
837
  menu.tray.notepad = Відкрити Блокнот...
837
838
  menu.tray.scheduled = Заплановані завдання
838
839
  menu.tray.screenshot = Запитати зі скріншотом...
@@ -1107,8 +1108,8 @@ settings.api_key.perplexity.desc = Обов'язковий для Perplexity API
1107
1108
  settings.api_key.xai = КЛЮЧ API xAI
1108
1109
  settings.api_key.xai.desc = Обов'язковий для xAI API та моделей Grok.
1109
1110
  settings.api_proxy = Використовувати проксі
1110
- settings.api_proxy.enabled = Проксі-адреса
1111
1111
  settings.api_proxy.desc = Опціонально, проксі для API SDK, наприклад, http://proxy.example.com або socks5://user:pass@host:port
1112
+ settings.api_proxy.enabled = Проксі-адреса
1112
1113
  settings.api_proxy.enabled.desc = Увімкніть цю опцію, щоб використовувати проксі для підключень до API
1113
1114
  settings.api_use_responses = Використовувати API відповідей у режимі чату
1114
1115
  settings.api_use_responses.desc = Використовувати API відповідей замість API ChatCompletions у режимі чату
@@ -1182,6 +1183,8 @@ settings.ctx.search_content = Шукати також у вмісті розмо
1182
1183
  settings.ctx.search.desc = Увімкнути пошук також у вмісті елементів контексту
1183
1184
  settings.ctx.sources = Показати джерела індексу Llama
1184
1185
  settings.ctx.sources.desc = Якщо включено, використані джерела будуть відображатися в відповіді (якщо доступно, не працюватиме в потоковому чаті)
1186
+ settings.ctx.urls.internal = Відкривайте URL-адреси у вбудованому браузері
1187
+ settings.ctx.urls.internal.desc = Увімкніть цю опцію, щоб відкривати всі URL-адреси у вбудованому браузері (Chromium) замість зовнішнього браузера.
1185
1188
  settings.ctx.use_extra = Використовувати додатковий контекст виводу
1186
1189
  settings.ctx.use_extra.desc = Якщо увімкнено, звичайний текстовий вивід (якщо доступний) з результатів команд буде відображений поруч з JSON виводом.
1187
1190
  settings.debug.show_menu = Показати меню налагодження
@@ -1546,6 +1549,7 @@ tool.indexer.tab.web.loader = Завантажувач даних
1546
1549
  tool.indexer.tab.web.source = Джерело даних
1547
1550
  tool.indexer.tab.web.tip = Виберіть завантажувач даних та визначте параметри завантажувача для вбудовування зовнішніх даних з вебу.
1548
1551
  tool.indexer.title = Індексатор
1552
+ tool.web_browser.security.footer = ПОВІДОМЛЕННЯ ПРО БЕЗПЕКУ: Для вашого захисту уникайте використання вбудованого браузера для чутливих або критичних завдань. Він призначений лише для базового використання.
1549
1553
  translator.btn.left = Перекласти >>
1550
1554
  translator.btn.right = << Перекласти
1551
1555
  translator.clear.confirm = Очистити вихідні дані перекладача (обидві колонки)?
@@ -833,6 +833,7 @@ menu.tools.interpreter = Python 代码解释器
833
833
  menu.tools.media.player = 媒体播放器
834
834
  menu.tools.text.editor = 文本编辑器
835
835
  menu.tools.translator = 翻译
836
+ menu.tools.web_browser = 网络浏览器 (Chromium)
836
837
  menu.tray.notepad = 打開記事本...
837
838
  menu.tray.scheduled = 已排程任務
838
839
  menu.tray.screenshot = 使用截圖提問...
@@ -1107,8 +1108,8 @@ settings.api_key.perplexity.desc = Perplexity API 所需。
1107
1108
  settings.api_key.xai = xAI API 密钥
1108
1109
  settings.api_key.xai.desc = xAI API 和 Grok 模型所需。
1109
1110
  settings.api_proxy = 代理地址
1110
- settings.api_proxy.enabled = 使用代理
1111
1111
  settings.api_proxy.desc = 可选,用于 API SDK 的代理,例如 http://proxy.example.com 或 socks5://user:pass@host:port
1112
+ settings.api_proxy.enabled = 使用代理
1112
1113
  settings.api_proxy.enabled.desc = 启用此选项以使用代理进行 API 连接
1113
1114
  settings.api_use_responses = 在聊天模式中使用 Responses API
1114
1115
  settings.api_use_responses.desc = 在聊天模式中使用 Responses API 而不是 ChatCompletions API
@@ -1182,6 +1183,8 @@ settings.ctx.search_content = 也在對話內容中搜索,而不僅僅是標
1182
1183
  settings.ctx.search.desc = 啟用在上下文項目內容中進行搜索的功能
1183
1184
  settings.ctx.sources = 显示Llama索引源
1184
1185
  settings.ctx.sources.desc = 如果启用,使用的源将在回应中显示(如果可用,不适用于流式聊天)
1186
+ settings.ctx.urls.internal = 在内置浏览器中打开网址
1187
+ settings.ctx.urls.internal.desc = 启用此选项以在内置浏览器 (Chromium) 中打开所有网址,而不是外部浏览器。
1185
1188
  settings.ctx.use_extra = 使用额外的上下文输出
1186
1189
  settings.ctx.use_extra.desc = 如果启用,将在命令结果的 JSON 输出旁边显示纯文本输出(如果有)。
1187
1190
  settings.debug.show_menu = 显示调试菜单
@@ -1546,6 +1549,7 @@ tool.indexer.tab.web.loader = 数据加载器
1546
1549
  tool.indexer.tab.web.source = 数据来源
1547
1550
  tool.indexer.tab.web.tip = 选择数据加载器并定义加载器参数,以便嵌入来自网络的外部数据。
1548
1551
  tool.indexer.title = 索引器
1552
+ tool.web_browser.security.footer = 安全提示:为保护您的安全,请避免使用内置浏览器进行敏感或关键的任务。 它仅用于基本用途。
1549
1553
  translator.btn.left = 翻译 >>
1550
1554
  translator.btn.right = << 翻译
1551
1555
  translator.clear.confirm = 清除翻译输出(两列)?
pygpt_net/js_rc.py CHANGED
@@ -70566,8 +70566,8 @@ eight: 0; border\
70566
70566
  sparent; backgro\
70567
70567
  und: transparent\
70568
70568
  ; }',\x0a\x09\x09\x09'.msg-b\
70569
- ox.msg-user:hove\
70570
- r .msg .msg-copy\
70569
+ ox.msg-user .msg\
70570
+ :hover .msg-copy\
70571
70571
  -btn, .msg-box.m\
70572
70572
  sg-user .msg:foc\
70573
70573
  us-within .msg-c\
@@ -113507,8 +113507,8 @@ ros||{},d(e,r)}}\
113507
113507
  /\
113508
113508
  * app.min.js \xe2\x80\x94\
113509
113509
  generated on 20\
113510
- 25-09-22 04:44:2\
113511
- 6 by bin/minify_\
113510
+ 25-09-22 09:05:0\
113511
+ 8 by bin/minify_\
113512
113512
  js.py using rjsm\
113513
113513
  in */\x0a\x0a/* data/j\
113514
113514
  s/app/async.js *\
@@ -114086,7 +114086,7 @@ VE_CODES_MAX',12\
114086
114086
  E_AFTER_LINES:Ut\
114087
114087
  ils.g('STREAM_PL\
114088
114088
  AIN_ACTIVATE_AFT\
114089
- ER_LINES',30),};\
114089
+ ER_LINES',80),};\
114090
114090
  this.MATH={IDLE_\
114091
114091
  TIMEOUT_MS:Utils\
114092
114092
  .g('MATH_IDLE_TI\
@@ -6,14 +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.08.15 23:00:00 #
9
+ # Updated Date: 2025.09.22 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
13
13
  from typing import Optional, Any, Dict, List
14
14
 
15
- from PySide6.QtCore import QObject, Slot, QUrl
16
- from PySide6.QtGui import QDesktopServices
15
+ from PySide6.QtCore import QObject, Slot
17
16
 
18
17
  from pygpt_net.core.bridge.context import BridgeContext
19
18
  from pygpt_net.core.events import Event, KernelEvent
@@ -554,5 +553,4 @@ class BasePlugin(QObject):
554
553
 
555
554
  :param url: URL to open
556
555
  """
557
- if url:
558
- QDesktopServices.openUrl(QUrl(url))
556
+ self.window.controller.dialogs.info.open_url(url)
@@ -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.09.17 05:00:00 #
9
+ # Updated Date: 2025.09.22 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
@@ -144,6 +144,13 @@ class Patch:
144
144
  data["api_proxy.enabled"] = True
145
145
  updated = True
146
146
 
147
+ # < 2.6.58
148
+ if old < parse_version("2.6.58"):
149
+ print("Migrating config from < 2.6.58...")
150
+ if "ctx.urls.internal" not in data:
151
+ data["ctx.urls.internal"] = False
152
+ updated = True
153
+
147
154
  # update file
148
155
  migrated = False
149
156
  if updated:
@@ -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.09.16 02:00:00 #
9
+ # Updated Date: 2025.09.22 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import Qt, Slot, QUrl, QObject, Signal, QSize
@@ -41,7 +41,7 @@ class ToolWidget:
41
41
  self.edit = None # canvas edit
42
42
  self.btn_edit = None # edit checkbox
43
43
 
44
- # --- Navigation bar state (added) ---
44
+ # --- Navigation bar state ---
45
45
  # This toolbar is shown only when opening via URL (open_url),
46
46
  # and hidden when using set_output/load_output.
47
47
  self.nav_bar = None
@@ -50,7 +50,7 @@ class ToolWidget:
50
50
  self.btn_back = None
51
51
  self.btn_next = None
52
52
  self.btn_reload = None
53
- self.btn_go = None # "Go" button (text)
53
+ self.btn_go = None
54
54
 
55
55
  def on_open(self):
56
56
  """On open"""
@@ -88,7 +88,7 @@ class ToolWidget:
88
88
  lambda: self.tool.save_output()
89
89
  )
90
90
 
91
- # ---- Navigation bar (added) ----
91
+ # ---- Navigation bar ----
92
92
  # Visible only when navigating URLs via open_url or address bar.
93
93
  self.nav_bar = QWidget()
94
94
  self.nav_layout = QHBoxLayout(self.nav_bar)
@@ -101,14 +101,14 @@ class ToolWidget:
101
101
  nav_height = max(32, min(44, icon_size_px + 16)) # compact, never half-screen
102
102
  self.nav_bar.setFixedHeight(nav_height)
103
103
 
104
- # Buttons (use QPushButton with icons from resources)
104
+ # Buttons
105
105
  self.btn_back = QPushButton()
106
106
  self.btn_back.setToolTip("Back")
107
107
  self.btn_back.setIcon(QIcon(":/icons/back.svg"))
108
108
  self.btn_back.setIconSize(QSize(icon_size_px, icon_size_px))
109
109
  self.btn_back.setFixedHeight(nav_height - 8)
110
110
  self.btn_back.setEnabled(False)
111
- self.btn_back.setAutoDefault(False) # prevent Enter from triggering this (added)
111
+ self.btn_back.setAutoDefault(False) # prevent Enter from triggering this
112
112
  try:
113
113
  self.btn_back.setDefault(False)
114
114
  except Exception:
@@ -116,11 +116,11 @@ class ToolWidget:
116
116
 
117
117
  self.btn_next = QPushButton()
118
118
  self.btn_next.setToolTip("Next")
119
- self.btn_next.setIcon(QIcon(":/icons/redo.svg")) # use redo.svg as "next"
119
+ self.btn_next.setIcon(QIcon(":/icons/forward.svg"))
120
120
  self.btn_next.setIconSize(QSize(icon_size_px, icon_size_px))
121
121
  self.btn_next.setFixedHeight(nav_height - 8)
122
122
  self.btn_next.setEnabled(False)
123
- self.btn_next.setAutoDefault(False) # (added)
123
+ self.btn_next.setAutoDefault(False)
124
124
  try:
125
125
  self.btn_next.setDefault(False)
126
126
  except Exception:
@@ -131,17 +131,19 @@ class ToolWidget:
131
131
  self.btn_reload.setIcon(QIcon(":/icons/reload.svg"))
132
132
  self.btn_reload.setIconSize(QSize(icon_size_px, icon_size_px))
133
133
  self.btn_reload.setFixedHeight(nav_height - 8)
134
- self.btn_reload.setAutoDefault(False) # (added)
134
+ self.btn_reload.setAutoDefault(False)
135
135
  try:
136
136
  self.btn_reload.setDefault(False)
137
137
  except Exception:
138
138
  pass
139
139
 
140
- # "Go" button is text-only as requested
141
- self.btn_go = QPushButton("GO")
140
+ # "Go" button
141
+ self.btn_go = QPushButton()
142
142
  self.btn_go.setToolTip("Open URL")
143
+ self.btn_go.setIcon(QIcon(":/icons/redo.svg"))
144
+ self.btn_go.setIconSize(QSize(icon_size_px, icon_size_px))
143
145
  self.btn_go.setFixedHeight(nav_height - 8)
144
- self.btn_go.setAutoDefault(False) # avoid stealing Enter (added)
146
+ self.btn_go.setAutoDefault(False) # avoid stealing Enter
145
147
  try:
146
148
  self.btn_go.setDefault(False)
147
149
  except Exception:
@@ -185,7 +187,7 @@ class ToolWidget:
185
187
 
186
188
  output_layout = QVBoxLayout()
187
189
  # put navigation bar above the web output
188
- output_layout.addWidget(self.nav_bar) # added
190
+ output_layout.addWidget(self.nav_bar)
189
191
  output_layout.addWidget(self.output)
190
192
  output_layout.addWidget(self.edit)
191
193
  output_layout.setContentsMargins(0, 0, 0, 0)
@@ -256,9 +258,9 @@ class ToolWidget:
256
258
  # Hide navigation bar when loading from local file/path
257
259
  self._show_navbar(False)
258
260
 
259
- # ----------------------
260
- # Navigation helpers (added)
261
- # ----------------------
261
+ # ------------------
262
+ # Navigation helpers
263
+ # ------------------
262
264
  def _show_navbar(self, show: bool):
263
265
  """Show/hide the navigation bar."""
264
266
  if self.nav_bar:
@@ -287,7 +289,6 @@ class ToolWidget:
287
289
  text = self.address_bar.text().strip()
288
290
  if not text:
289
291
  return
290
- # Use fromUserInput to accept entries like 'example.com'
291
292
  url = QUrl.fromUserInput(text)
292
293
  if url.isValid():
293
294
  self._show_navbar(True)
@@ -371,7 +372,7 @@ class CanvasEdit(BaseCodeEditor):
371
372
  return super().eventFilter(source, event)
372
373
 
373
374
 
374
- # --- Address bar input widget (added) ---
375
+ # --- Address bar input widget ---
375
376
  class AddressLineEdit(QLineEdit):
376
377
  """
377
378
  Custom QLineEdit that ensures Enter triggers opening the typed URL
@@ -0,0 +1,12 @@
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: 2025.09.22 19:00:00 #
10
+ # ================================================== #
11
+
12
+ from .tool import *
@@ -0,0 +1,232 @@
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: 2025.09.22 19:00:00 #
10
+ # ================================================== #
11
+
12
+ from typing import Dict
13
+
14
+ from PySide6.QtCore import QTimer, Slot
15
+ from PySide6.QtGui import QAction, QIcon
16
+ from PySide6.QtWidgets import QWidget
17
+
18
+ from pygpt_net.core.tabs.tab import Tab
19
+ from pygpt_net.core.text.utils import output_clean_html, output_html2text
20
+ from pygpt_net.tools.base import BaseTool, TabWidget
21
+ from pygpt_net.utils import trans
22
+
23
+ from .ui.dialogs import Tool
24
+ from .ui.widgets import ToolSignals
25
+
26
+
27
+ class WebBrowser(BaseTool):
28
+ def __init__(self, *args, **kwargs):
29
+ """
30
+ Web Browser tool
31
+
32
+ :param window: Window instance
33
+ """
34
+ super(WebBrowser, self).__init__(*args, **kwargs)
35
+ self.id = "web_browser"
36
+ self.dialog_id = "web_browser"
37
+ self.has_tab = True
38
+ self.tab_title = "menu.tools.web_browser"
39
+ self.tab_icon = ":/icons/web_on.svg"
40
+ self.opened = False
41
+ self.is_edit = False
42
+ self.auto_clear = True
43
+ self.dialog = None
44
+ self.is_edit = False
45
+ self.auto_opened = False
46
+ self.signals = ToolSignals()
47
+
48
+ def setup(self):
49
+ """Setup"""
50
+ self.update()
51
+
52
+ def on_reload(self):
53
+ """On app profile reload"""
54
+ self.setup()
55
+
56
+ def update(self):
57
+ """Update menu"""
58
+ self.update_menu()
59
+
60
+ def update_menu(self):
61
+ """Update menu"""
62
+ """
63
+ if self.opened:
64
+ self.window.ui.menu['tools.web_browser'].setChecked(True)
65
+ else:
66
+ self.window.ui.menu['tools.web_browser'].setChecked(False)
67
+ """
68
+
69
+ def get_dialog_id(self) -> str:
70
+ """
71
+ Get dialog ID
72
+
73
+ :return: Dialog ID
74
+ """
75
+ return self.dialog_id
76
+
77
+ def set_url(self, url: str):
78
+ """
79
+ Set output URL
80
+
81
+ :param url: URL to load
82
+ """
83
+ self.signals.url.emit(url)
84
+
85
+ def open(self, load: bool = True):
86
+ """
87
+ Open HTML canvas dialog
88
+
89
+ :param load: Load output data
90
+ """
91
+ if not self.opened:
92
+ self.opened = True
93
+ self.auto_opened = False
94
+ self.window.ui.dialogs.open(self.dialog_id, width=800, height=600)
95
+ self.dialog.widget.on_open()
96
+ self.update()
97
+
98
+ def auto_open(self, load: bool = True):
99
+ """
100
+ Auto open canvas dialog or tab
101
+
102
+ :param load: Load output data
103
+ """
104
+ if self.window.controller.ui.tabs.is_current_tool(self.id):
105
+ tool_col = self.window.controller.ui.tabs.get_tool_column(self.id)
106
+ current_col = self.window.controller.ui.tabs.column_idx
107
+ if tool_col == 1 and tool_col != current_col:
108
+ self.window.controller.ui.tabs.enable_split_screen(True) # enable split screen
109
+ return # do not open if already opened in tab
110
+ elif self.window.controller.ui.tabs.is_tool(self.id):
111
+ tab = self.window.controller.ui.tabs.get_first_tab_by_tool(self.id)
112
+ if tab:
113
+ tool_col = tab.column_idx
114
+ current_col = self.window.controller.ui.tabs.column_idx
115
+ self.window.controller.ui.tabs.switch_tab_by_idx(tab.idx, tab.column_idx)
116
+ if tool_col == 1 and tool_col != current_col:
117
+ self.window.controller.ui.tabs.enable_split_screen(True) # enable split screen
118
+ return
119
+ if not self.auto_opened:
120
+ self.auto_opened = True
121
+ self.open(load=load)
122
+
123
+ def close(self):
124
+ """Close HTML canvas dialog"""
125
+ self.opened = False
126
+ self.signals.closed.emit()
127
+ self.window.ui.dialogs.close(self.dialog_id)
128
+ self.update()
129
+
130
+ def toggle(self):
131
+ """Toggle HTML canvas dialog open/close"""
132
+ if self.opened:
133
+ self.close()
134
+ else:
135
+ self.open()
136
+
137
+ @Slot(str, str)
138
+ def handle_save_as(self, text: str, type: str = 'txt'):
139
+ """
140
+ Handle save as signal
141
+
142
+ :param text: Data to save
143
+ :param type: File type
144
+ """
145
+ if type == 'html':
146
+ text = output_clean_html(text)
147
+ else:
148
+ text = output_html2text(text)
149
+ # fix: QTimer required here to prevent crash if signal emitted from WebEngine window
150
+ QTimer.singleShot(0, lambda: self.window.controller.chat.common.save_text(text, type))
151
+
152
+ def show_hide(self, show: bool = True):
153
+ """
154
+ Show/hide HTML canvas window
155
+
156
+ :param show: show/hide
157
+ """
158
+ if show:
159
+ self.open()
160
+ else:
161
+ self.close()
162
+
163
+ def get_toolbar_icon(self) -> QWidget:
164
+ """
165
+ Get toolbar icon
166
+
167
+ :return: QWidget
168
+ """
169
+ return self.window.ui.nodes['icon.web_browser']
170
+
171
+ def toggle_icon(self, state: bool):
172
+ """
173
+ Toggle canvas icon
174
+
175
+ :param state: State
176
+ """
177
+ self.get_toolbar_icon().setVisible(state)
178
+
179
+ def setup_menu(self) -> Dict[str, QAction]:
180
+ """
181
+ Setup main menu
182
+
183
+ :return dict with menu actions
184
+ """
185
+ actions = {}
186
+ actions["web_browser"] = QAction(
187
+ QIcon(":/icons/web_on.svg"),
188
+ trans("menu.tools.web_browser"),
189
+ self.window,
190
+ checkable=False,
191
+ )
192
+ actions["web_browser"].triggered.connect(
193
+ lambda: self.toggle()
194
+ )
195
+ return actions
196
+
197
+ def as_tab(self, tab: Tab) -> QWidget:
198
+ """
199
+ Spawn and return tab instance
200
+
201
+ :param tab: Parent Tab instance
202
+ :return: Tab widget instance
203
+ """
204
+
205
+ tool = Tool(window=self.window, tool=self) # dialog
206
+ tool_widget = tool.as_tab() # ToolWidget
207
+ widget = TabWidget()
208
+ widget.from_tool(tool_widget)
209
+ widget.setup()
210
+ tool.set_tab(tab)
211
+ return widget
212
+
213
+ def setup_dialogs(self):
214
+ """Setup dialogs (static)"""
215
+ self.dialog = Tool(window=self.window, tool=self)
216
+ self.dialog.setup()
217
+
218
+ def setup_theme(self):
219
+ """Setup theme"""
220
+ pass
221
+
222
+ def get_lang_mappings(self) -> Dict[str, Dict]:
223
+ """
224
+ Get language mappings
225
+
226
+ :return: dict with language mappings
227
+ """
228
+ return {
229
+ 'menu.text': {
230
+ 'tools.web_browser': 'menu.tools.web_browser',
231
+ }
232
+ }
File without changes
@@ -0,0 +1,123 @@
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: 2025.09.22 19:00:00 #
10
+ # ================================================== #
11
+
12
+ import re
13
+
14
+ from PySide6.QtCore import Qt
15
+ from PySide6.QtGui import QAction, QIcon
16
+ from PySide6.QtWidgets import QMenuBar, QVBoxLayout
17
+
18
+ from pygpt_net.core.tabs.tab import Tab
19
+ from pygpt_net.ui.widget.dialog.base import BaseDialog
20
+ from pygpt_net.utils import trans
21
+
22
+ from .widgets import ToolWidget
23
+
24
+ class Tool:
25
+ def __init__(self, window=None, tool=None):
26
+ """
27
+ HTML/JS canvas dialog
28
+
29
+ :param window: Window instance
30
+ :param tool: Tool instance
31
+ """
32
+ self.window = window
33
+ self.tool = tool # tool instance
34
+ self.widget = ToolWidget(window, tool)
35
+ self.layout = None
36
+ self.menu_bar = None
37
+ self.menu = {}
38
+ self.actions = {} # menu actions
39
+
40
+ def as_tab(self) -> ToolWidget:
41
+ """
42
+ Return tool as tab
43
+
44
+ :return: ToolWidget
45
+ """
46
+ return self.widget
47
+
48
+ def set_tab(self, tab: Tab):
49
+ """
50
+ Set tab
51
+
52
+ :param tab: Tab
53
+ """
54
+ self.widget.set_tab(tab)
55
+
56
+ def setup(self):
57
+ """Setup canvas dialog"""
58
+ self.layout = self.widget.setup()
59
+
60
+ id = self.tool.get_dialog_id()
61
+ dialog = ToolDialog(window=self.window, tool=self.tool)
62
+ dialog.setLayout(self.layout)
63
+ dialog.setWindowTitle(trans("menu.tools.web_browser"))
64
+ dialog.resize(800, 500)
65
+ self.window.ui.dialog[id] = dialog
66
+
67
+ def get_widget(self) -> ToolWidget:
68
+ """
69
+ Get widget
70
+
71
+ :return: ToolWidget
72
+ """
73
+ return self.widget
74
+
75
+ def get_tab(self) -> QVBoxLayout:
76
+ """
77
+ Get layout
78
+
79
+ :return: QVBoxLayout
80
+ """
81
+ return self.layout
82
+
83
+
84
+ class ToolDialog(BaseDialog):
85
+ def __init__(self, window=None, id="html_canvas", tool=None):
86
+ """
87
+ HTML canvas dialog
88
+
89
+ :param window: main window
90
+ :param id: logger id
91
+ """
92
+ super(ToolDialog, self).__init__(window, id)
93
+ self.window = window
94
+ self.tool = tool
95
+
96
+ def closeEvent(self, event):
97
+ """
98
+ Close event
99
+
100
+ :param event: close event
101
+ """
102
+ self.cleanup()
103
+ super(ToolDialog, self).closeEvent(event)
104
+
105
+ def keyPressEvent(self, event):
106
+ """
107
+ Key press event
108
+
109
+ :param event: key press event
110
+ """
111
+ if event.key() == Qt.Key_Escape:
112
+ self.cleanup()
113
+ self.close() # close dialog when the Esc key is pressed.
114
+ else:
115
+ super(ToolDialog, self).keyPressEvent(event)
116
+
117
+ def cleanup(self):
118
+ """Cleanup on close"""
119
+ if self.window is None or self.tool is None:
120
+ return
121
+ self.tool.opened = False
122
+ self.tool.close()
123
+ self.tool.update()