pygpt-net 2.6.55__py3-none-any.whl → 2.6.57__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.
- pygpt_net/CHANGELOG.txt +12 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +26 -22
- pygpt_net/config.py +44 -0
- pygpt_net/controller/audio/audio.py +0 -0
- pygpt_net/controller/calendar/calendar.py +0 -0
- pygpt_net/controller/calendar/note.py +0 -0
- pygpt_net/controller/chat/chat.py +0 -0
- pygpt_net/controller/chat/handler/openai_stream.py +2 -1
- pygpt_net/controller/chat/handler/worker.py +0 -0
- pygpt_net/controller/chat/remote_tools.py +5 -3
- pygpt_net/controller/chat/render.py +0 -0
- pygpt_net/controller/chat/text.py +0 -0
- pygpt_net/controller/ctx/common.py +0 -0
- pygpt_net/controller/debug/debug.py +26 -2
- pygpt_net/controller/debug/fixtures.py +1 -1
- pygpt_net/controller/dialogs/confirm.py +15 -1
- pygpt_net/controller/dialogs/debug.py +2 -0
- pygpt_net/controller/lang/mapping.py +0 -0
- pygpt_net/controller/launcher/launcher.py +0 -0
- pygpt_net/controller/mode/mode.py +0 -0
- pygpt_net/controller/presets/presets.py +0 -0
- pygpt_net/controller/realtime/realtime.py +0 -0
- pygpt_net/controller/theme/theme.py +0 -0
- pygpt_net/controller/ui/mode.py +5 -3
- pygpt_net/controller/ui/tabs.py +0 -0
- pygpt_net/core/agents/agents.py +3 -1
- pygpt_net/core/agents/custom.py +150 -0
- pygpt_net/core/agents/provider.py +0 -0
- pygpt_net/core/builder/__init__.py +12 -0
- pygpt_net/core/builder/graph.py +478 -0
- pygpt_net/core/calendar/calendar.py +0 -0
- pygpt_net/core/ctx/ctx.py +0 -0
- pygpt_net/core/ctx/output.py +0 -0
- pygpt_net/core/debug/agent.py +0 -0
- pygpt_net/core/debug/agent_builder.py +29 -0
- pygpt_net/core/debug/console/console.py +0 -0
- pygpt_net/core/debug/db.py +0 -0
- pygpt_net/core/debug/debug.py +0 -0
- pygpt_net/core/debug/events.py +0 -0
- pygpt_net/core/debug/indexes.py +0 -0
- pygpt_net/core/debug/kernel.py +0 -0
- pygpt_net/core/debug/tabs.py +0 -0
- pygpt_net/core/filesystem/filesystem.py +0 -0
- pygpt_net/core/fixtures/__init__ +0 -0
- pygpt_net/core/fixtures/stream/__init__.py +0 -0
- pygpt_net/core/fixtures/stream/generator.py +0 -0
- pygpt_net/core/models/models.py +2 -1
- pygpt_net/core/plugins/plugins.py +60 -0
- pygpt_net/core/render/plain/pid.py +0 -0
- pygpt_net/core/render/plain/renderer.py +26 -4
- pygpt_net/core/render/web/body.py +46 -4
- pygpt_net/core/render/web/debug.py +0 -0
- pygpt_net/core/render/web/helpers.py +0 -0
- pygpt_net/core/render/web/pid.py +0 -0
- pygpt_net/core/render/web/renderer.py +15 -20
- pygpt_net/core/tabs/tab.py +0 -0
- pygpt_net/core/tabs/tabs.py +0 -0
- pygpt_net/core/text/utils.py +0 -0
- pygpt_net/css.qrc +0 -0
- pygpt_net/css_rc.py +0 -0
- pygpt_net/data/config/config.json +8 -7
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/config/settings.json +14 -0
- pygpt_net/data/css/web-blocks.css +9 -0
- pygpt_net/data/css/web-blocks.dark.css +6 -0
- pygpt_net/data/css/web-blocks.darkest.css +6 -0
- pygpt_net/data/css/web-chatgpt.css +14 -6
- pygpt_net/data/css/web-chatgpt.dark.css +6 -0
- pygpt_net/data/css/web-chatgpt.darkest.css +6 -0
- pygpt_net/data/css/web-chatgpt.light.css +6 -0
- pygpt_net/data/css/web-chatgpt_wide.css +14 -6
- pygpt_net/data/css/web-chatgpt_wide.dark.css +6 -0
- pygpt_net/data/css/web-chatgpt_wide.darkest.css +6 -0
- pygpt_net/data/css/web-chatgpt_wide.light.css +6 -0
- pygpt_net/data/fixtures/fake_stream.txt +14 -1
- pygpt_net/data/icons/case.svg +0 -0
- pygpt_net/data/icons/chat1.svg +0 -0
- pygpt_net/data/icons/chat2.svg +0 -0
- pygpt_net/data/icons/chat3.svg +0 -0
- pygpt_net/data/icons/chat4.svg +0 -0
- pygpt_net/data/icons/fit.svg +0 -0
- pygpt_net/data/icons/note1.svg +0 -0
- pygpt_net/data/icons/note2.svg +0 -0
- pygpt_net/data/icons/note3.svg +0 -0
- pygpt_net/data/icons/stt.svg +0 -0
- pygpt_net/data/icons/translate.svg +0 -0
- pygpt_net/data/icons/tts.svg +0 -0
- pygpt_net/data/icons/url.svg +0 -0
- pygpt_net/data/icons/vision.svg +0 -0
- pygpt_net/data/icons/web_off.svg +0 -0
- pygpt_net/data/icons/web_on.svg +0 -0
- pygpt_net/data/js/app/async.js +166 -0
- pygpt_net/data/js/app/bridge.js +88 -0
- pygpt_net/data/js/app/common.js +212 -0
- pygpt_net/data/js/app/config.js +223 -0
- pygpt_net/data/js/app/custom.js +961 -0
- pygpt_net/data/js/app/data.js +84 -0
- pygpt_net/data/js/app/dom.js +322 -0
- pygpt_net/data/js/app/events.js +400 -0
- pygpt_net/data/js/app/highlight.js +542 -0
- pygpt_net/data/js/app/logger.js +305 -0
- pygpt_net/data/js/app/markdown.js +1137 -0
- pygpt_net/data/js/app/math.js +167 -0
- pygpt_net/data/js/app/nodes.js +395 -0
- pygpt_net/data/js/app/queue.js +260 -0
- pygpt_net/data/js/app/raf.js +250 -0
- pygpt_net/data/js/app/runtime.js +582 -0
- pygpt_net/data/js/app/scroll.js +433 -0
- pygpt_net/data/js/app/stream.js +2708 -0
- pygpt_net/data/js/app/template.js +287 -0
- pygpt_net/data/js/app/tool.js +87 -0
- pygpt_net/data/js/app/ui.js +86 -0
- pygpt_net/data/js/app/user.js +380 -0
- pygpt_net/data/js/app/utils.js +64 -0
- pygpt_net/data/js/app.min.js +880 -0
- pygpt_net/data/js/markdown-it/markdown-it-katex.min.js +1 -1
- pygpt_net/data/js/markdown-it/markdown-it.min.js +0 -0
- pygpt_net/data/locale/locale.de.ini +3 -1
- pygpt_net/data/locale/locale.en.ini +8 -0
- pygpt_net/data/locale/locale.es.ini +2 -0
- pygpt_net/data/locale/locale.fr.ini +2 -0
- pygpt_net/data/locale/locale.it.ini +2 -0
- pygpt_net/data/locale/locale.pl.ini +3 -1
- pygpt_net/data/locale/locale.uk.ini +3 -1
- pygpt_net/data/locale/locale.zh.ini +2 -0
- pygpt_net/data/locale/plugin.osm.en.ini +24 -24
- pygpt_net/data/locale/plugin.wolfram.en.ini +9 -9
- pygpt_net/fonts.qrc +0 -0
- pygpt_net/fonts_rc.py +0 -0
- pygpt_net/icons.qrc +0 -0
- pygpt_net/icons_rc.py +0 -0
- pygpt_net/item/agent.py +62 -0
- pygpt_net/item/builder_layout.py +62 -0
- pygpt_net/js.qrc +24 -1
- pygpt_net/js_rc.py +51394 -33687
- pygpt_net/plugin/base/worker.py +0 -0
- pygpt_net/plugin/cmd_web/config.py +17 -17
- pygpt_net/plugin/cmd_web/worker.py +325 -171
- pygpt_net/plugin/mcp/__init__.py +0 -0
- pygpt_net/plugin/mcp/config.py +0 -0
- pygpt_net/plugin/mcp/plugin.py +0 -0
- pygpt_net/plugin/mcp/worker.py +0 -0
- pygpt_net/plugin/osm/__init__.py +0 -0
- pygpt_net/plugin/osm/config.py +0 -0
- pygpt_net/plugin/osm/plugin.py +0 -0
- pygpt_net/plugin/osm/worker.py +0 -0
- pygpt_net/plugin/wolfram/__init__.py +0 -0
- pygpt_net/plugin/wolfram/config.py +0 -0
- pygpt_net/plugin/wolfram/plugin.py +0 -0
- pygpt_net/plugin/wolfram/worker.py +0 -0
- pygpt_net/provider/api/anthropic/tools.py +0 -0
- pygpt_net/provider/api/google/__init__.py +0 -0
- pygpt_net/provider/api/google/video.py +0 -0
- pygpt_net/provider/api/openai/agents/experts.py +0 -0
- pygpt_net/provider/api/openai/agents/remote_tools.py +0 -0
- pygpt_net/provider/api/openai/remote_tools.py +0 -0
- pygpt_net/provider/api/openai/responses.py +0 -0
- pygpt_net/provider/api/x_ai/__init__.py +2 -0
- pygpt_net/provider/api/x_ai/remote.py +0 -0
- pygpt_net/provider/core/agent/__init__.py +10 -0
- pygpt_net/provider/core/agent/base.py +51 -0
- pygpt_net/provider/core/agent/json_file.py +200 -0
- pygpt_net/provider/core/config/patch.py +33 -0
- pygpt_net/provider/core/config/patches/__init__.py +0 -0
- pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +1 -0
- pygpt_net/provider/core/ctx/db_sqlite/storage.py +0 -0
- pygpt_net/provider/core/model/patches/__init__.py +0 -0
- pygpt_net/provider/core/model/patches/patch_before_2_6_42.py +0 -0
- pygpt_net/provider/core/preset/patch.py +0 -0
- pygpt_net/provider/core/preset/patches/__init__.py +0 -0
- pygpt_net/provider/core/preset/patches/patch_before_2_6_42.py +0 -0
- pygpt_net/provider/llms/anthropic.py +4 -0
- pygpt_net/provider/llms/base.py +2 -0
- pygpt_net/provider/llms/deepseek_api.py +2 -0
- pygpt_net/provider/llms/google.py +2 -0
- pygpt_net/provider/llms/hugging_face_api.py +4 -0
- pygpt_net/provider/llms/hugging_face_embedding.py +0 -0
- pygpt_net/provider/llms/hugging_face_router.py +2 -0
- pygpt_net/provider/llms/local.py +0 -0
- pygpt_net/provider/llms/mistral.py +4 -0
- pygpt_net/provider/llms/open_router.py +0 -0
- pygpt_net/provider/llms/perplexity.py +2 -0
- pygpt_net/provider/llms/utils.py +0 -0
- pygpt_net/provider/llms/voyage.py +0 -0
- pygpt_net/provider/llms/x_ai.py +2 -0
- pygpt_net/tools/agent_builder/__init__.py +12 -0
- pygpt_net/tools/agent_builder/tool.py +292 -0
- pygpt_net/tools/agent_builder/ui/__init__.py +0 -0
- pygpt_net/tools/agent_builder/ui/dialogs.py +152 -0
- pygpt_net/tools/agent_builder/ui/list.py +228 -0
- pygpt_net/tools/code_interpreter/ui/html.py +0 -0
- pygpt_net/tools/code_interpreter/ui/widgets.py +0 -0
- pygpt_net/tools/html_canvas/tool.py +23 -6
- pygpt_net/tools/html_canvas/ui/widgets.py +224 -2
- pygpt_net/ui/layout/chat/chat.py +0 -0
- pygpt_net/ui/layout/chat/output.py +5 -5
- pygpt_net/ui/main.py +10 -9
- pygpt_net/ui/menu/debug.py +39 -1
- pygpt_net/ui/widget/builder/__init__.py +12 -0
- pygpt_net/ui/widget/builder/editor.py +2001 -0
- pygpt_net/ui/widget/dialog/base.py +4 -1
- pygpt_net/ui/widget/draw/painter.py +0 -0
- pygpt_net/ui/widget/element/labels.py +9 -4
- pygpt_net/ui/widget/lists/db.py +0 -0
- pygpt_net/ui/widget/lists/debug.py +0 -0
- pygpt_net/ui/widget/tabs/body.py +0 -0
- pygpt_net/ui/widget/textarea/html.py +1 -0
- pygpt_net/ui/widget/textarea/input.py +28 -10
- pygpt_net/ui/widget/textarea/output.py +21 -1
- pygpt_net/ui/widget/textarea/web.py +31 -3
- pygpt_net/utils.py +40 -0
- {pygpt_net-2.6.55.dist-info → pygpt_net-2.6.57.dist-info}/METADATA +16 -2
- {pygpt_net-2.6.55.dist-info → pygpt_net-2.6.57.dist-info}/RECORD +116 -77
- pygpt_net/data/js/app.js +0 -5869
- {pygpt_net-2.6.55.dist-info → pygpt_net-2.6.57.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.55.dist-info → pygpt_net-2.6.57.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.55.dist-info → pygpt_net-2.6.57.dist-info}/entry_points.txt +0 -0
|
@@ -112,6 +112,14 @@ class HtmlCanvas(BaseTool):
|
|
|
112
112
|
self.signals.reload.emit(path)
|
|
113
113
|
self.signals.update.emit(output)
|
|
114
114
|
|
|
115
|
+
def set_url(self, url: str):
|
|
116
|
+
"""
|
|
117
|
+
Set output URL
|
|
118
|
+
|
|
119
|
+
:param url: URL to load
|
|
120
|
+
"""
|
|
121
|
+
self.signals.url.emit(url)
|
|
122
|
+
|
|
115
123
|
def reload_output(self):
|
|
116
124
|
"""Reload output data"""
|
|
117
125
|
self.load_output()
|
|
@@ -172,18 +180,27 @@ class HtmlCanvas(BaseTool):
|
|
|
172
180
|
"""Clear input and output"""
|
|
173
181
|
self.clear_output()
|
|
174
182
|
|
|
175
|
-
def open(self):
|
|
176
|
-
"""
|
|
183
|
+
def open(self, load: bool = True):
|
|
184
|
+
"""
|
|
185
|
+
Open HTML canvas dialog
|
|
186
|
+
|
|
187
|
+
:param load: Load output data
|
|
188
|
+
"""
|
|
177
189
|
if not self.opened:
|
|
178
190
|
self.opened = True
|
|
179
191
|
self.auto_opened = False
|
|
180
|
-
|
|
192
|
+
if load:
|
|
193
|
+
self.load_output()
|
|
181
194
|
self.window.ui.dialogs.open(self.dialog_id, width=800, height=600)
|
|
182
195
|
self.dialog.widget.on_open()
|
|
183
196
|
self.update()
|
|
184
197
|
|
|
185
|
-
def auto_open(self):
|
|
186
|
-
"""
|
|
198
|
+
def auto_open(self, load: bool = True):
|
|
199
|
+
"""
|
|
200
|
+
Auto open canvas dialog or tab
|
|
201
|
+
|
|
202
|
+
:param load: Load output data
|
|
203
|
+
"""
|
|
187
204
|
if self.window.controller.ui.tabs.is_current_tool(self.id):
|
|
188
205
|
tool_col = self.window.controller.ui.tabs.get_tool_column(self.id)
|
|
189
206
|
current_col = self.window.controller.ui.tabs.column_idx
|
|
@@ -201,7 +218,7 @@ class HtmlCanvas(BaseTool):
|
|
|
201
218
|
return
|
|
202
219
|
if not self.auto_opened:
|
|
203
220
|
self.auto_opened = True
|
|
204
|
-
self.open()
|
|
221
|
+
self.open(load=load)
|
|
205
222
|
|
|
206
223
|
def open_file(self):
|
|
207
224
|
"""Open file dialog"""
|
|
@@ -9,8 +9,17 @@
|
|
|
9
9
|
# Updated Date: 2025.09.16 02:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
|
-
from PySide6.QtCore import Qt, Slot, QUrl, QObject, Signal
|
|
13
|
-
from PySide6.
|
|
12
|
+
from PySide6.QtCore import Qt, Slot, QUrl, QObject, Signal, QSize
|
|
13
|
+
from PySide6.QtGui import QIcon
|
|
14
|
+
from PySide6.QtWidgets import (
|
|
15
|
+
QVBoxLayout,
|
|
16
|
+
QCheckBox,
|
|
17
|
+
QHBoxLayout,
|
|
18
|
+
QLineEdit,
|
|
19
|
+
QPushButton,
|
|
20
|
+
QWidget,
|
|
21
|
+
QSizePolicy,
|
|
22
|
+
)
|
|
14
23
|
|
|
15
24
|
from pygpt_net.ui.widget.element.labels import HelpLabel
|
|
16
25
|
from pygpt_net.ui.widget.textarea.editor import BaseCodeEditor
|
|
@@ -32,6 +41,17 @@ class ToolWidget:
|
|
|
32
41
|
self.edit = None # canvas edit
|
|
33
42
|
self.btn_edit = None # edit checkbox
|
|
34
43
|
|
|
44
|
+
# --- Navigation bar state (added) ---
|
|
45
|
+
# This toolbar is shown only when opening via URL (open_url),
|
|
46
|
+
# and hidden when using set_output/load_output.
|
|
47
|
+
self.nav_bar = None
|
|
48
|
+
self.nav_layout = None
|
|
49
|
+
self.address_bar = None
|
|
50
|
+
self.btn_back = None
|
|
51
|
+
self.btn_next = None
|
|
52
|
+
self.btn_reload = None
|
|
53
|
+
self.btn_go = None # "Go" button (text)
|
|
54
|
+
|
|
35
55
|
def on_open(self):
|
|
36
56
|
"""On open"""
|
|
37
57
|
pass
|
|
@@ -41,6 +61,7 @@ class ToolWidget:
|
|
|
41
61
|
if self.tool:
|
|
42
62
|
self.tool.signals.update.disconnect(self.set_output)
|
|
43
63
|
self.tool.signals.reload.disconnect(self.load_output)
|
|
64
|
+
self.tool.signals.url.disconnect(self.open_url) # keep connections clean
|
|
44
65
|
if self.output:
|
|
45
66
|
self.output.on_delete()
|
|
46
67
|
|
|
@@ -67,6 +88,86 @@ class ToolWidget:
|
|
|
67
88
|
lambda: self.tool.save_output()
|
|
68
89
|
)
|
|
69
90
|
|
|
91
|
+
# ---- Navigation bar (added) ----
|
|
92
|
+
# Visible only when navigating URLs via open_url or address bar.
|
|
93
|
+
self.nav_bar = QWidget()
|
|
94
|
+
self.nav_layout = QHBoxLayout(self.nav_bar)
|
|
95
|
+
self.nav_layout.setContentsMargins(6, 4, 6, 4) # small margins for compact look
|
|
96
|
+
self.nav_layout.setSpacing(6)
|
|
97
|
+
self.nav_bar.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
|
|
98
|
+
|
|
99
|
+
# Icon size and fixed navbar height (to prevent taking too much space)
|
|
100
|
+
icon_size_px = 20 # base icon size for QPushButtons
|
|
101
|
+
nav_height = max(32, min(44, icon_size_px + 16)) # compact, never half-screen
|
|
102
|
+
self.nav_bar.setFixedHeight(nav_height)
|
|
103
|
+
|
|
104
|
+
# Buttons (use QPushButton with icons from resources)
|
|
105
|
+
self.btn_back = QPushButton()
|
|
106
|
+
self.btn_back.setToolTip("Back")
|
|
107
|
+
self.btn_back.setIcon(QIcon(":/icons/back.svg"))
|
|
108
|
+
self.btn_back.setIconSize(QSize(icon_size_px, icon_size_px))
|
|
109
|
+
self.btn_back.setFixedHeight(nav_height - 8)
|
|
110
|
+
self.btn_back.setEnabled(False)
|
|
111
|
+
self.btn_back.setAutoDefault(False) # prevent Enter from triggering this (added)
|
|
112
|
+
try:
|
|
113
|
+
self.btn_back.setDefault(False)
|
|
114
|
+
except Exception:
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
self.btn_next = QPushButton()
|
|
118
|
+
self.btn_next.setToolTip("Next")
|
|
119
|
+
self.btn_next.setIcon(QIcon(":/icons/redo.svg")) # use redo.svg as "next"
|
|
120
|
+
self.btn_next.setIconSize(QSize(icon_size_px, icon_size_px))
|
|
121
|
+
self.btn_next.setFixedHeight(nav_height - 8)
|
|
122
|
+
self.btn_next.setEnabled(False)
|
|
123
|
+
self.btn_next.setAutoDefault(False) # (added)
|
|
124
|
+
try:
|
|
125
|
+
self.btn_next.setDefault(False)
|
|
126
|
+
except Exception:
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
self.btn_reload = QPushButton()
|
|
130
|
+
self.btn_reload.setToolTip("Reload")
|
|
131
|
+
self.btn_reload.setIcon(QIcon(":/icons/reload.svg"))
|
|
132
|
+
self.btn_reload.setIconSize(QSize(icon_size_px, icon_size_px))
|
|
133
|
+
self.btn_reload.setFixedHeight(nav_height - 8)
|
|
134
|
+
self.btn_reload.setAutoDefault(False) # (added)
|
|
135
|
+
try:
|
|
136
|
+
self.btn_reload.setDefault(False)
|
|
137
|
+
except Exception:
|
|
138
|
+
pass
|
|
139
|
+
|
|
140
|
+
# "Go" button is text-only as requested
|
|
141
|
+
self.btn_go = QPushButton("GO")
|
|
142
|
+
self.btn_go.setToolTip("Open URL")
|
|
143
|
+
self.btn_go.setFixedHeight(nav_height - 8)
|
|
144
|
+
self.btn_go.setAutoDefault(False) # avoid stealing Enter (added)
|
|
145
|
+
try:
|
|
146
|
+
self.btn_go.setDefault(False)
|
|
147
|
+
except Exception:
|
|
148
|
+
pass
|
|
149
|
+
|
|
150
|
+
# Address bar (custom line edit to capture Enter and avoid default button reload)
|
|
151
|
+
self.address_bar = AddressLineEdit(on_return_callback=self._on_address_enter)
|
|
152
|
+
self.address_bar.setPlaceholderText("Enter URL and press Enter")
|
|
153
|
+
self.address_bar.setFixedHeight(nav_height - 8)
|
|
154
|
+
# Still keep the Qt signal for completeness (won't fire because we intercept the key)
|
|
155
|
+
self.address_bar.returnPressed.connect(self._on_address_enter)
|
|
156
|
+
|
|
157
|
+
# Hook up button actions
|
|
158
|
+
self.btn_back.clicked.connect(lambda: self._navigate('back'))
|
|
159
|
+
self.btn_next.clicked.connect(lambda: self._navigate('forward'))
|
|
160
|
+
self.btn_reload.clicked.connect(lambda: self._navigate('reload'))
|
|
161
|
+
self.btn_go.clicked.connect(self._on_address_enter) # "Go" triggers address open
|
|
162
|
+
|
|
163
|
+
# Build nav layout
|
|
164
|
+
self.nav_layout.addWidget(self.btn_back)
|
|
165
|
+
self.nav_layout.addWidget(self.btn_next)
|
|
166
|
+
self.nav_layout.addWidget(self.btn_reload)
|
|
167
|
+
self.nav_layout.addWidget(self.address_bar, 1)
|
|
168
|
+
self.nav_layout.addWidget(self.btn_go)
|
|
169
|
+
self.nav_bar.setVisible(False) # hidden by default
|
|
170
|
+
|
|
70
171
|
# edit checkbox
|
|
71
172
|
self.btn_edit = QCheckBox(trans("html_canvas.btn.edit"))
|
|
72
173
|
self.btn_edit.stateChanged.connect(
|
|
@@ -83,6 +184,8 @@ class ToolWidget:
|
|
|
83
184
|
bottom_layout.addWidget(path_label)
|
|
84
185
|
|
|
85
186
|
output_layout = QVBoxLayout()
|
|
187
|
+
# put navigation bar above the web output
|
|
188
|
+
output_layout.addWidget(self.nav_bar) # added
|
|
86
189
|
output_layout.addWidget(self.output)
|
|
87
190
|
output_layout.addWidget(self.edit)
|
|
88
191
|
output_layout.setContentsMargins(0, 0, 0, 0)
|
|
@@ -93,8 +196,24 @@ class ToolWidget:
|
|
|
93
196
|
self.output.signals.audio_read.connect(
|
|
94
197
|
self.window.controller.chat.render.handle_audio_read)
|
|
95
198
|
|
|
199
|
+
# Keep nav bar in sync with the underlying view if signals are available
|
|
200
|
+
# (urlChanged/loadStarted/loadFinished are part of QWebEngineView)
|
|
201
|
+
if hasattr(self.output, 'urlChanged'):
|
|
202
|
+
self.output.urlChanged.connect(self._on_url_changed)
|
|
203
|
+
if hasattr(self.output, 'loadStarted'):
|
|
204
|
+
try:
|
|
205
|
+
self.output.loadStarted.connect(lambda: self._update_nav_controls())
|
|
206
|
+
except TypeError:
|
|
207
|
+
pass
|
|
208
|
+
if hasattr(self.output, 'loadFinished'):
|
|
209
|
+
try:
|
|
210
|
+
self.output.loadFinished.connect(lambda ok: self._update_nav_controls())
|
|
211
|
+
except TypeError:
|
|
212
|
+
pass
|
|
213
|
+
|
|
96
214
|
self.tool.signals.update.connect(self.set_output)
|
|
97
215
|
self.tool.signals.reload.connect(self.load_output)
|
|
216
|
+
self.tool.signals.url.connect(self.open_url)
|
|
98
217
|
|
|
99
218
|
layout = QVBoxLayout()
|
|
100
219
|
layout.addLayout(output_layout)
|
|
@@ -109,6 +228,22 @@ class ToolWidget:
|
|
|
109
228
|
:param content: Content
|
|
110
229
|
"""
|
|
111
230
|
self.edit.setPlainText(content)
|
|
231
|
+
# Hide navigation bar when content is set via set_output (not a URL)
|
|
232
|
+
self._show_navbar(False)
|
|
233
|
+
|
|
234
|
+
@Slot(str)
|
|
235
|
+
def open_url(self, url: str):
|
|
236
|
+
"""
|
|
237
|
+
Open URL in output
|
|
238
|
+
|
|
239
|
+
:param url: URL
|
|
240
|
+
"""
|
|
241
|
+
print("Opening URL:", url)
|
|
242
|
+
self._show_navbar(True) # Show navigation bar for URL navigation
|
|
243
|
+
if self.address_bar:
|
|
244
|
+
self.address_bar.setText(url)
|
|
245
|
+
self.output.setUrl(QUrl(url))
|
|
246
|
+
self._update_nav_controls()
|
|
112
247
|
|
|
113
248
|
@Slot(str)
|
|
114
249
|
def load_output(self, path: str):
|
|
@@ -118,6 +253,71 @@ class ToolWidget:
|
|
|
118
253
|
:param path: Content
|
|
119
254
|
"""
|
|
120
255
|
self.output.setUrl(QUrl().fromLocalFile(path))
|
|
256
|
+
# Hide navigation bar when loading from local file/path
|
|
257
|
+
self._show_navbar(False)
|
|
258
|
+
|
|
259
|
+
# ----------------------
|
|
260
|
+
# Navigation helpers (added)
|
|
261
|
+
# ----------------------
|
|
262
|
+
def _show_navbar(self, show: bool):
|
|
263
|
+
"""Show/hide the navigation bar."""
|
|
264
|
+
if self.nav_bar:
|
|
265
|
+
self.nav_bar.setVisible(show)
|
|
266
|
+
# Keep controls state up-to-date
|
|
267
|
+
self._update_nav_controls()
|
|
268
|
+
|
|
269
|
+
def _navigate(self, action: str):
|
|
270
|
+
"""Perform navigation action on the output view."""
|
|
271
|
+
if not self.output:
|
|
272
|
+
return
|
|
273
|
+
try:
|
|
274
|
+
if action == 'back' and hasattr(self.output, 'back'):
|
|
275
|
+
self.output.back()
|
|
276
|
+
elif action == 'forward' and hasattr(self.output, 'forward'):
|
|
277
|
+
self.output.forward()
|
|
278
|
+
elif action == 'reload' and hasattr(self.output, 'reload'):
|
|
279
|
+
self.output.reload()
|
|
280
|
+
finally:
|
|
281
|
+
self._update_nav_controls()
|
|
282
|
+
|
|
283
|
+
def _on_address_enter(self):
|
|
284
|
+
"""Handle Enter pressed in the address bar or clicking GO."""
|
|
285
|
+
if not self.output or not self.address_bar:
|
|
286
|
+
return
|
|
287
|
+
text = self.address_bar.text().strip()
|
|
288
|
+
if not text:
|
|
289
|
+
return
|
|
290
|
+
# Use fromUserInput to accept entries like 'example.com'
|
|
291
|
+
url = QUrl.fromUserInput(text)
|
|
292
|
+
if url.isValid():
|
|
293
|
+
self._show_navbar(True)
|
|
294
|
+
self.output.setUrl(url)
|
|
295
|
+
self._update_nav_controls()
|
|
296
|
+
|
|
297
|
+
def _on_url_changed(self, url: QUrl):
|
|
298
|
+
"""Keep address bar in sync with the view URL."""
|
|
299
|
+
if self.address_bar:
|
|
300
|
+
self.address_bar.setText(url.toString())
|
|
301
|
+
self._update_nav_controls()
|
|
302
|
+
|
|
303
|
+
def _update_nav_controls(self):
|
|
304
|
+
"""Enable/disable back/forward buttons based on history."""
|
|
305
|
+
if not self.output:
|
|
306
|
+
return
|
|
307
|
+
# Reload is generally available when navbar is visible
|
|
308
|
+
if self.btn_reload:
|
|
309
|
+
self.btn_reload.setEnabled(True)
|
|
310
|
+
# Back/forward depend on history if available
|
|
311
|
+
if self.btn_back and self.btn_next:
|
|
312
|
+
try:
|
|
313
|
+
hist = self.output.history()
|
|
314
|
+
self.btn_back.setEnabled(bool(hist and hist.canGoBack()))
|
|
315
|
+
self.btn_next.setEnabled(bool(hist and hist.canGoForward()))
|
|
316
|
+
except Exception:
|
|
317
|
+
# If history is not available, keep safe defaults
|
|
318
|
+
self.btn_back.setEnabled(False)
|
|
319
|
+
self.btn_next.setEnabled(False)
|
|
320
|
+
|
|
121
321
|
|
|
122
322
|
class CanvasOutput(HtmlOutput):
|
|
123
323
|
def __init__(self, window=None):
|
|
@@ -129,6 +329,7 @@ class CanvasOutput(HtmlOutput):
|
|
|
129
329
|
super(CanvasOutput, self).__init__(window)
|
|
130
330
|
self.window = window
|
|
131
331
|
|
|
332
|
+
|
|
132
333
|
class CanvasEdit(BaseCodeEditor):
|
|
133
334
|
def __init__(self, window=None):
|
|
134
335
|
"""
|
|
@@ -170,6 +371,27 @@ class CanvasEdit(BaseCodeEditor):
|
|
|
170
371
|
return super().eventFilter(source, event)
|
|
171
372
|
|
|
172
373
|
|
|
374
|
+
# --- Address bar input widget (added) ---
|
|
375
|
+
class AddressLineEdit(QLineEdit):
|
|
376
|
+
"""
|
|
377
|
+
Custom QLineEdit that ensures Enter triggers opening the typed URL
|
|
378
|
+
and prevents default buttons (e.g., Reload) from consuming the key.
|
|
379
|
+
"""
|
|
380
|
+
def __init__(self, on_return_callback=None, *args, **kwargs):
|
|
381
|
+
super().__init__(*args, **kwargs)
|
|
382
|
+
self._on_return_callback = on_return_callback
|
|
383
|
+
|
|
384
|
+
def keyPressEvent(self, event):
|
|
385
|
+
if event.key() in (Qt.Key_Return, Qt.Key_Enter):
|
|
386
|
+
# Call the assigned handler and stop propagation so default buttons won't trigger.
|
|
387
|
+
if callable(self._on_return_callback):
|
|
388
|
+
self._on_return_callback()
|
|
389
|
+
event.accept()
|
|
390
|
+
return
|
|
391
|
+
super().keyPressEvent(event)
|
|
392
|
+
|
|
393
|
+
|
|
173
394
|
class ToolSignals(QObject):
|
|
174
395
|
update = Signal(str) # data
|
|
175
396
|
reload = Signal(str) # path
|
|
397
|
+
url = Signal(str) # url
|
pygpt_net/ui/layout/chat/chat.py
CHANGED
|
File without changes
|
|
@@ -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.
|
|
9
|
+
# Updated Date: 2025.09.22 12:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtCore import Qt
|
|
@@ -86,9 +86,9 @@ class Output:
|
|
|
86
86
|
nodes['icon.indexer'].setToolTip("Indexer")
|
|
87
87
|
nodes['icon.indexer'].clicked.connect(lambda: tools.get("indexer").toggle())
|
|
88
88
|
|
|
89
|
-
nodes['icon.remote_tool.web'] = IconLabel(":/icons/web_on.svg", window=self.window)
|
|
90
|
-
nodes['icon.remote_tool.web'].setToolTip(trans("icon.remote_tool.web"))
|
|
91
|
-
nodes['icon.remote_tool.web'].clicked.connect(lambda: ctrl.chat.remote_tools.toggle('web_search'))
|
|
89
|
+
# nodes['icon.remote_tool.web'] = IconLabel(":/icons/web_on.svg", window=self.window)
|
|
90
|
+
# nodes['icon.remote_tool.web'].setToolTip(trans("icon.remote_tool.web"))
|
|
91
|
+
# nodes['icon.remote_tool.web'].clicked.connect(lambda: ctrl.chat.remote_tools.toggle('web_search'))
|
|
92
92
|
|
|
93
93
|
min_policy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
|
94
94
|
|
|
@@ -139,7 +139,7 @@ class Output:
|
|
|
139
139
|
nodes['anim.loading'].hide()
|
|
140
140
|
|
|
141
141
|
right_bar_layout = QHBoxLayout()
|
|
142
|
-
right_bar_layout.addWidget(nodes['icon.remote_tool.web'])
|
|
142
|
+
# right_bar_layout.addWidget(nodes['icon.remote_tool.web'])
|
|
143
143
|
right_bar_layout.addWidget(nodes['icon.video.capture'])
|
|
144
144
|
right_bar_layout.addWidget(nodes['icon.audio.input'])
|
|
145
145
|
right_bar_layout.addWidget(nodes['icon.audio.output'])
|
pygpt_net/ui/main.py
CHANGED
|
@@ -22,7 +22,7 @@ from pygpt_net.controller import Controller
|
|
|
22
22
|
from pygpt_net.tools import Tools
|
|
23
23
|
from pygpt_net.ui import UI
|
|
24
24
|
from pygpt_net.ui.widget.textarea.web import ChatWebOutput
|
|
25
|
-
from pygpt_net.utils import get_app_meta, freeze_updates
|
|
25
|
+
from pygpt_net.utils import get_app_meta, freeze_updates, set_env, has_env, get_env
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
class MainWindow(QMainWindow, QtStyleTools):
|
|
@@ -112,20 +112,21 @@ class MainWindow(QMainWindow, QtStyleTools):
|
|
|
112
112
|
render_debug = True
|
|
113
113
|
|
|
114
114
|
# disable/enable logging web engine context
|
|
115
|
+
CHROMIUM_FLAGS = "QTWEBENGINE_CHROMIUM_FLAGS"
|
|
116
|
+
|
|
115
117
|
if not render_debug:
|
|
116
118
|
QLoggingCategory.setFilterRules("*.info=false")
|
|
117
119
|
else:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
else:
|
|
121
|
-
os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = "--enable-logging --log-level=0"
|
|
120
|
+
set_env(CHROMIUM_FLAGS, "--enable-logging", True)
|
|
121
|
+
set_env(CHROMIUM_FLAGS, "--log-level=0", True)
|
|
122
122
|
|
|
123
123
|
# OpenGL disable
|
|
124
124
|
if self.core.config.get("render.open_gl") is False:
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
125
|
+
set_env(CHROMIUM_FLAGS, "--disable-gpu", True)
|
|
126
|
+
|
|
127
|
+
# log chromium flags
|
|
128
|
+
if (has_env(CHROMIUM_FLAGS) and get_env("PYGPT_APP_ENV") == "dev") or render_debug:
|
|
129
|
+
print("Running with Chromium flags:", get_env(CHROMIUM_FLAGS))
|
|
129
130
|
|
|
130
131
|
def add_plugin(self, plugin):
|
|
131
132
|
"""
|
pygpt_net/ui/menu/debug.py
CHANGED
|
@@ -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.
|
|
9
|
+
# Updated Date: 2025.09.19 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtGui import QAction
|
|
@@ -39,6 +39,7 @@ class Debug:
|
|
|
39
39
|
'attachments',
|
|
40
40
|
'assistants',
|
|
41
41
|
'agent',
|
|
42
|
+
# 'agent_builder',
|
|
42
43
|
'events',
|
|
43
44
|
'indexes',
|
|
44
45
|
'ui',
|
|
@@ -61,6 +62,7 @@ class Debug:
|
|
|
61
62
|
'attachments',
|
|
62
63
|
'assistants',
|
|
63
64
|
'agent',
|
|
65
|
+
# 'agent_builder',
|
|
64
66
|
'events',
|
|
65
67
|
'indexes',
|
|
66
68
|
'ui',
|
|
@@ -98,6 +100,7 @@ class Debug:
|
|
|
98
100
|
menu.addActions(
|
|
99
101
|
[
|
|
100
102
|
m['debug.agent'],
|
|
103
|
+
# m['debug.agent_builder'],
|
|
101
104
|
m['debug.assistants'],
|
|
102
105
|
m['debug.attachments'],
|
|
103
106
|
m['debug.config'],
|
|
@@ -113,4 +116,39 @@ class Debug:
|
|
|
113
116
|
]
|
|
114
117
|
)
|
|
115
118
|
|
|
119
|
+
# add separator and sub-menu with Chrome debug
|
|
120
|
+
menu.addSeparator()
|
|
121
|
+
|
|
122
|
+
# Sub-menu "Chrome"
|
|
123
|
+
m['menu.debug.chrome'] = menu.addMenu("Chromium")
|
|
124
|
+
chrome_urls = {
|
|
125
|
+
'sandbox': ('Sandbox', 'chrome://sandbox'),
|
|
126
|
+
'gpu': ('GPU', 'chrome://gpu'),
|
|
127
|
+
'version': ('Version', 'chrome://version'),
|
|
128
|
+
'flags': ('Flags', 'chrome://flags'),
|
|
129
|
+
'components': ('Components', 'chrome://components'),
|
|
130
|
+
'policy': ('Policy', 'chrome://policy'),
|
|
131
|
+
'dns': ('DNS', 'chrome://dns'),
|
|
132
|
+
'net_export': ('Net Export', 'chrome://net-export'),
|
|
133
|
+
'media_internals': ('Media Internals', 'chrome://media-internals'),
|
|
134
|
+
'webrtc_internals': ('WebRTC Internals', 'chrome://webrtc-internals'),
|
|
135
|
+
'discards': ('Discards (Tab Discarding)', 'chrome://discards'),
|
|
136
|
+
'crashes': ('Crashes', 'chrome://crashes'),
|
|
137
|
+
'histograms': ('Histograms', 'chrome://histograms'),
|
|
138
|
+
'process_internals': ('Process Internals', 'chrome://process-internals'),
|
|
139
|
+
'tracing': ('Tracing', 'chrome://tracing'),
|
|
140
|
+
'chrome_urls': ('All Chrome URLs', 'chrome://chrome-urls'),
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
for key, (label, url) in chrome_urls.items():
|
|
144
|
+
action_key = f"debug.chrome.{key}"
|
|
145
|
+
m[action_key] = QAction(label, win)
|
|
146
|
+
m[action_key].triggered.connect(lambda _=False, u=url: dbg.open_chrome_debug(u))
|
|
147
|
+
m['menu.debug.chrome'].addAction(m[action_key])
|
|
148
|
+
|
|
149
|
+
# add open devtools action
|
|
150
|
+
m['debug.chrome.devtools'] = QAction("Open DevTools", win)
|
|
151
|
+
m['debug.chrome.devtools'].triggered.connect(dbg.open_dev_tools)
|
|
152
|
+
menu.addAction(m['debug.chrome.devtools'])
|
|
153
|
+
|
|
116
154
|
m['debug.render'].setChecked(bool(win.core.config.get('debug.render')))
|
|
@@ -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.19 00:00:00 #
|
|
10
|
+
# ================================================== #
|
|
11
|
+
|
|
12
|
+
from .editor import NodeEditor
|