pygpt-net 2.6.56__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 (221) hide show
  1. pygpt_net/CHANGELOG.txt +10 -0
  2. pygpt_net/__init__.py +1 -1
  3. pygpt_net/app.py +30 -25
  4. pygpt_net/config.py +44 -0
  5. pygpt_net/controller/chat/remote_tools.py +5 -3
  6. pygpt_net/controller/debug/debug.py +3 -3
  7. pygpt_net/controller/dialogs/info.py +6 -2
  8. pygpt_net/controller/ui/mode.py +5 -3
  9. pygpt_net/controller/ui/tabs.py +17 -0
  10. pygpt_net/core/filesystem/url.py +5 -2
  11. pygpt_net/core/models/models.py +2 -1
  12. pygpt_net/core/plugins/plugins.py +60 -0
  13. pygpt_net/data/config/config.json +4 -2
  14. pygpt_net/data/config/models.json +2 -2
  15. pygpt_net/data/config/settings.json +55 -2
  16. pygpt_net/data/js/app/ui.js +1 -1
  17. pygpt_net/data/js/app.min.js +2 -2
  18. pygpt_net/data/locale/locale.de.ini +7 -1
  19. pygpt_net/data/locale/locale.en.ini +12 -7
  20. pygpt_net/data/locale/locale.es.ini +6 -0
  21. pygpt_net/data/locale/locale.fr.ini +6 -0
  22. pygpt_net/data/locale/locale.it.ini +6 -0
  23. pygpt_net/data/locale/locale.pl.ini +7 -1
  24. pygpt_net/data/locale/locale.uk.ini +7 -1
  25. pygpt_net/data/locale/locale.zh.ini +6 -0
  26. pygpt_net/data/locale/plugin.agent.de.ini +0 -0
  27. pygpt_net/data/locale/plugin.agent.en.ini +0 -0
  28. pygpt_net/data/locale/plugin.agent.es.ini +0 -0
  29. pygpt_net/data/locale/plugin.agent.fr.ini +0 -0
  30. pygpt_net/data/locale/plugin.agent.it.ini +0 -0
  31. pygpt_net/data/locale/plugin.agent.pl.ini +0 -0
  32. pygpt_net/data/locale/plugin.agent.uk.ini +0 -0
  33. pygpt_net/data/locale/plugin.agent.zh.ini +0 -0
  34. pygpt_net/data/locale/plugin.audio_input.de.ini +0 -0
  35. pygpt_net/data/locale/plugin.audio_input.en.ini +0 -0
  36. pygpt_net/data/locale/plugin.audio_input.es.ini +0 -0
  37. pygpt_net/data/locale/plugin.audio_input.fr.ini +0 -0
  38. pygpt_net/data/locale/plugin.audio_input.it.ini +0 -0
  39. pygpt_net/data/locale/plugin.audio_input.pl.ini +0 -0
  40. pygpt_net/data/locale/plugin.audio_input.uk.ini +0 -0
  41. pygpt_net/data/locale/plugin.audio_input.zh.ini +0 -0
  42. pygpt_net/data/locale/plugin.audio_output.de.ini +0 -0
  43. pygpt_net/data/locale/plugin.audio_output.en.ini +0 -0
  44. pygpt_net/data/locale/plugin.audio_output.es.ini +0 -0
  45. pygpt_net/data/locale/plugin.audio_output.fr.ini +0 -0
  46. pygpt_net/data/locale/plugin.audio_output.it.ini +0 -0
  47. pygpt_net/data/locale/plugin.audio_output.pl.ini +0 -0
  48. pygpt_net/data/locale/plugin.audio_output.uk.ini +0 -0
  49. pygpt_net/data/locale/plugin.audio_output.zh.ini +0 -0
  50. pygpt_net/data/locale/plugin.cmd_api.de.ini +0 -0
  51. pygpt_net/data/locale/plugin.cmd_api.en.ini +0 -0
  52. pygpt_net/data/locale/plugin.cmd_api.es.ini +0 -0
  53. pygpt_net/data/locale/plugin.cmd_api.fr.ini +0 -0
  54. pygpt_net/data/locale/plugin.cmd_api.it.ini +0 -0
  55. pygpt_net/data/locale/plugin.cmd_api.pl.ini +0 -0
  56. pygpt_net/data/locale/plugin.cmd_api.uk.ini +0 -0
  57. pygpt_net/data/locale/plugin.cmd_api.zh.ini +0 -0
  58. pygpt_net/data/locale/plugin.cmd_code_interpreter.de.ini +0 -0
  59. pygpt_net/data/locale/plugin.cmd_code_interpreter.en.ini +0 -0
  60. pygpt_net/data/locale/plugin.cmd_code_interpreter.es.ini +0 -0
  61. pygpt_net/data/locale/plugin.cmd_code_interpreter.fr.ini +0 -0
  62. pygpt_net/data/locale/plugin.cmd_code_interpreter.it.ini +0 -0
  63. pygpt_net/data/locale/plugin.cmd_code_interpreter.pl.ini +0 -0
  64. pygpt_net/data/locale/plugin.cmd_code_interpreter.uk.ini +0 -0
  65. pygpt_net/data/locale/plugin.cmd_code_interpreter.zh.ini +0 -0
  66. pygpt_net/data/locale/plugin.cmd_custom.de.ini +0 -0
  67. pygpt_net/data/locale/plugin.cmd_custom.en.ini +0 -0
  68. pygpt_net/data/locale/plugin.cmd_custom.es.ini +0 -0
  69. pygpt_net/data/locale/plugin.cmd_custom.fr.ini +0 -0
  70. pygpt_net/data/locale/plugin.cmd_custom.it.ini +0 -0
  71. pygpt_net/data/locale/plugin.cmd_custom.pl.ini +0 -0
  72. pygpt_net/data/locale/plugin.cmd_custom.uk.ini +0 -0
  73. pygpt_net/data/locale/plugin.cmd_custom.zh.ini +0 -0
  74. pygpt_net/data/locale/plugin.cmd_files.de.ini +0 -0
  75. pygpt_net/data/locale/plugin.cmd_files.en.ini +0 -0
  76. pygpt_net/data/locale/plugin.cmd_files.es.ini +0 -0
  77. pygpt_net/data/locale/plugin.cmd_files.fr.ini +0 -0
  78. pygpt_net/data/locale/plugin.cmd_files.it.ini +0 -0
  79. pygpt_net/data/locale/plugin.cmd_files.pl.ini +0 -0
  80. pygpt_net/data/locale/plugin.cmd_files.uk.ini +0 -0
  81. pygpt_net/data/locale/plugin.cmd_files.zh.ini +0 -0
  82. pygpt_net/data/locale/plugin.cmd_history.de.ini +0 -0
  83. pygpt_net/data/locale/plugin.cmd_history.en.ini +0 -0
  84. pygpt_net/data/locale/plugin.cmd_history.es.ini +0 -0
  85. pygpt_net/data/locale/plugin.cmd_history.fr.ini +0 -0
  86. pygpt_net/data/locale/plugin.cmd_history.it.ini +0 -0
  87. pygpt_net/data/locale/plugin.cmd_history.pl.ini +0 -0
  88. pygpt_net/data/locale/plugin.cmd_history.uk.ini +0 -0
  89. pygpt_net/data/locale/plugin.cmd_history.zh.ini +0 -0
  90. pygpt_net/data/locale/plugin.cmd_mouse_control.de.ini +0 -0
  91. pygpt_net/data/locale/plugin.cmd_mouse_control.en.ini +0 -0
  92. pygpt_net/data/locale/plugin.cmd_mouse_control.es.ini +0 -0
  93. pygpt_net/data/locale/plugin.cmd_mouse_control.fr.ini +0 -0
  94. pygpt_net/data/locale/plugin.cmd_mouse_control.it.ini +0 -0
  95. pygpt_net/data/locale/plugin.cmd_mouse_control.pl.ini +0 -0
  96. pygpt_net/data/locale/plugin.cmd_mouse_control.uk.ini +0 -0
  97. pygpt_net/data/locale/plugin.cmd_mouse_control.zh.ini +0 -0
  98. pygpt_net/data/locale/plugin.cmd_serial.de.ini +0 -0
  99. pygpt_net/data/locale/plugin.cmd_serial.en.ini +0 -0
  100. pygpt_net/data/locale/plugin.cmd_serial.es.ini +0 -0
  101. pygpt_net/data/locale/plugin.cmd_serial.fr.ini +0 -0
  102. pygpt_net/data/locale/plugin.cmd_serial.it.ini +0 -0
  103. pygpt_net/data/locale/plugin.cmd_serial.pl.ini +0 -0
  104. pygpt_net/data/locale/plugin.cmd_serial.uk.ini +0 -0
  105. pygpt_net/data/locale/plugin.cmd_serial.zh.ini +0 -0
  106. pygpt_net/data/locale/plugin.cmd_system.de.ini +0 -0
  107. pygpt_net/data/locale/plugin.cmd_system.en.ini +0 -0
  108. pygpt_net/data/locale/plugin.cmd_system.es.ini +0 -0
  109. pygpt_net/data/locale/plugin.cmd_system.fr.ini +0 -0
  110. pygpt_net/data/locale/plugin.cmd_system.it.ini +0 -0
  111. pygpt_net/data/locale/plugin.cmd_system.pl.ini +0 -0
  112. pygpt_net/data/locale/plugin.cmd_system.uk.ini +0 -0
  113. pygpt_net/data/locale/plugin.cmd_system.zh.ini +0 -0
  114. pygpt_net/data/locale/plugin.cmd_web.de.ini +0 -0
  115. pygpt_net/data/locale/plugin.cmd_web.en.ini +0 -0
  116. pygpt_net/data/locale/plugin.cmd_web.es.ini +0 -0
  117. pygpt_net/data/locale/plugin.cmd_web.fr.ini +0 -0
  118. pygpt_net/data/locale/plugin.cmd_web.it.ini +0 -0
  119. pygpt_net/data/locale/plugin.cmd_web.pl.ini +0 -0
  120. pygpt_net/data/locale/plugin.cmd_web.uk.ini +0 -0
  121. pygpt_net/data/locale/plugin.cmd_web.zh.ini +0 -0
  122. pygpt_net/data/locale/plugin.crontab.de.ini +0 -0
  123. pygpt_net/data/locale/plugin.crontab.en.ini +0 -0
  124. pygpt_net/data/locale/plugin.crontab.es.ini +0 -0
  125. pygpt_net/data/locale/plugin.crontab.fr.ini +0 -0
  126. pygpt_net/data/locale/plugin.crontab.it.ini +0 -0
  127. pygpt_net/data/locale/plugin.crontab.pl.ini +0 -0
  128. pygpt_net/data/locale/plugin.crontab.uk.ini +0 -0
  129. pygpt_net/data/locale/plugin.crontab.zh.ini +0 -0
  130. pygpt_net/data/locale/plugin.experts.de.ini +0 -0
  131. pygpt_net/data/locale/plugin.experts.en.ini +0 -0
  132. pygpt_net/data/locale/plugin.experts.es.ini +0 -0
  133. pygpt_net/data/locale/plugin.experts.fr.ini +0 -0
  134. pygpt_net/data/locale/plugin.experts.it.ini +0 -0
  135. pygpt_net/data/locale/plugin.experts.pl.ini +0 -0
  136. pygpt_net/data/locale/plugin.experts.uk.ini +0 -0
  137. pygpt_net/data/locale/plugin.experts.zh.ini +0 -0
  138. pygpt_net/data/locale/plugin.extra_prompt.de.ini +0 -0
  139. pygpt_net/data/locale/plugin.extra_prompt.en.ini +0 -0
  140. pygpt_net/data/locale/plugin.extra_prompt.es.ini +0 -0
  141. pygpt_net/data/locale/plugin.extra_prompt.fr.ini +0 -0
  142. pygpt_net/data/locale/plugin.extra_prompt.it.ini +0 -0
  143. pygpt_net/data/locale/plugin.extra_prompt.pl.ini +0 -0
  144. pygpt_net/data/locale/plugin.extra_prompt.uk.ini +0 -0
  145. pygpt_net/data/locale/plugin.extra_prompt.zh.ini +0 -0
  146. pygpt_net/data/locale/plugin.idx_llama_index.de.ini +0 -0
  147. pygpt_net/data/locale/plugin.idx_llama_index.en.ini +0 -0
  148. pygpt_net/data/locale/plugin.idx_llama_index.es.ini +0 -0
  149. pygpt_net/data/locale/plugin.idx_llama_index.fr.ini +0 -0
  150. pygpt_net/data/locale/plugin.idx_llama_index.it.ini +0 -0
  151. pygpt_net/data/locale/plugin.idx_llama_index.pl.ini +0 -0
  152. pygpt_net/data/locale/plugin.idx_llama_index.uk.ini +0 -0
  153. pygpt_net/data/locale/plugin.idx_llama_index.zh.ini +0 -0
  154. pygpt_net/data/locale/plugin.mailer.en.ini +0 -0
  155. pygpt_net/data/locale/plugin.mcp.en.ini +0 -0
  156. pygpt_net/data/locale/plugin.openai_dalle.de.ini +0 -0
  157. pygpt_net/data/locale/plugin.openai_dalle.en.ini +0 -0
  158. pygpt_net/data/locale/plugin.openai_dalle.es.ini +0 -0
  159. pygpt_net/data/locale/plugin.openai_dalle.fr.ini +0 -0
  160. pygpt_net/data/locale/plugin.openai_dalle.it.ini +0 -0
  161. pygpt_net/data/locale/plugin.openai_dalle.pl.ini +0 -0
  162. pygpt_net/data/locale/plugin.openai_dalle.uk.ini +0 -0
  163. pygpt_net/data/locale/plugin.openai_dalle.zh.ini +0 -0
  164. pygpt_net/data/locale/plugin.openai_vision.de.ini +0 -0
  165. pygpt_net/data/locale/plugin.openai_vision.en.ini +0 -0
  166. pygpt_net/data/locale/plugin.openai_vision.es.ini +0 -0
  167. pygpt_net/data/locale/plugin.openai_vision.fr.ini +0 -0
  168. pygpt_net/data/locale/plugin.openai_vision.it.ini +0 -0
  169. pygpt_net/data/locale/plugin.openai_vision.pl.ini +0 -0
  170. pygpt_net/data/locale/plugin.openai_vision.uk.ini +0 -0
  171. pygpt_net/data/locale/plugin.openai_vision.zh.ini +0 -0
  172. pygpt_net/data/locale/plugin.osm.en.ini +24 -24
  173. pygpt_net/data/locale/plugin.real_time.de.ini +0 -0
  174. pygpt_net/data/locale/plugin.real_time.en.ini +0 -0
  175. pygpt_net/data/locale/plugin.real_time.es.ini +0 -0
  176. pygpt_net/data/locale/plugin.real_time.fr.ini +0 -0
  177. pygpt_net/data/locale/plugin.real_time.it.ini +0 -0
  178. pygpt_net/data/locale/plugin.real_time.pl.ini +0 -0
  179. pygpt_net/data/locale/plugin.real_time.uk.ini +0 -0
  180. pygpt_net/data/locale/plugin.real_time.zh.ini +0 -0
  181. pygpt_net/data/locale/plugin.voice_control.de.ini +0 -0
  182. pygpt_net/data/locale/plugin.voice_control.en.ini +0 -0
  183. pygpt_net/data/locale/plugin.voice_control.es.ini +0 -0
  184. pygpt_net/data/locale/plugin.voice_control.fr.ini +0 -0
  185. pygpt_net/data/locale/plugin.voice_control.it.ini +0 -0
  186. pygpt_net/data/locale/plugin.voice_control.pl.ini +0 -0
  187. pygpt_net/data/locale/plugin.voice_control.uk.ini +0 -0
  188. pygpt_net/data/locale/plugin.voice_control.zh.ini +0 -0
  189. pygpt_net/data/locale/plugin.wolfram.en.ini +9 -9
  190. pygpt_net/js_rc.py +5 -5
  191. pygpt_net/plugin/base/plugin.py +3 -5
  192. pygpt_net/plugin/cmd_web/config.py +17 -17
  193. pygpt_net/plugin/cmd_web/worker.py +325 -171
  194. pygpt_net/provider/api/x_ai/__init__.py +2 -0
  195. pygpt_net/provider/core/config/patch.py +23 -1
  196. pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +1 -0
  197. pygpt_net/provider/llms/anthropic.py +4 -0
  198. pygpt_net/provider/llms/base.py +2 -0
  199. pygpt_net/provider/llms/deepseek_api.py +2 -0
  200. pygpt_net/provider/llms/google.py +2 -0
  201. pygpt_net/provider/llms/hugging_face_api.py +4 -0
  202. pygpt_net/provider/llms/hugging_face_router.py +2 -0
  203. pygpt_net/provider/llms/mistral.py +4 -0
  204. pygpt_net/provider/llms/perplexity.py +2 -0
  205. pygpt_net/provider/llms/x_ai.py +2 -0
  206. pygpt_net/tools/html_canvas/ui/widgets.py +19 -18
  207. pygpt_net/tools/web_browser/__init__.py +12 -0
  208. pygpt_net/tools/web_browser/tool.py +232 -0
  209. pygpt_net/tools/web_browser/ui/__init__.py +0 -0
  210. pygpt_net/tools/web_browser/ui/dialogs.py +123 -0
  211. pygpt_net/tools/web_browser/ui/widgets.py +351 -0
  212. pygpt_net/ui/layout/chat/output.py +5 -5
  213. pygpt_net/ui/widget/dialog/base.py +4 -1
  214. pygpt_net/ui/widget/textarea/html.py +173 -24
  215. pygpt_net/ui/widget/textarea/input.py +19 -3
  216. pygpt_net/ui/widget/textarea/web.py +2 -1
  217. {pygpt_net-2.6.56.dist-info → pygpt_net-2.6.58.dist-info}/METADATA +26 -2
  218. {pygpt_net-2.6.56.dist-info → pygpt_net-2.6.58.dist-info}/RECORD +59 -54
  219. {pygpt_net-2.6.56.dist-info → pygpt_net-2.6.58.dist-info}/LICENSE +0 -0
  220. {pygpt_net-2.6.56.dist-info → pygpt_net-2.6.58.dist-info}/WHEEL +0 -0
  221. {pygpt_net-2.6.56.dist-info → pygpt_net-2.6.58.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt CHANGED
@@ -1,3 +1,13 @@
1
+ 2.6.58 (2025-09-22)
2
+
3
+ - Added: internal Web Browser tool.
4
+
5
+ 2.6.57 (2025-09-22)
6
+
7
+ - Changed: The web search icon has been moved to the input area.
8
+ - Improved: The local web search plugin has been enhanced to retrieve multiple URLs at once.
9
+ - Added: Use proxy switch in Settings.
10
+
1
11
  2.6.56 (2025-09-22)
2
12
 
3
13
  - Optimized: Memory usage and performance in streaming and rendering large contexts.
pygpt_net/__init__.py CHANGED
@@ -13,7 +13,7 @@ __author__ = "Marcin Szczygliński"
13
13
  __copyright__ = "Copyright 2025, Marcin Szczygliński"
14
14
  __credits__ = ["Marcin Szczygliński"]
15
15
  __license__ = "MIT"
16
- __version__ = "2.6.56"
16
+ __version__ = "2.6.58"
17
17
  __build__ = "2025-09-22"
18
18
  __maintainer__ = "Marcin Szczygliński"
19
19
  __github__ = "https://github.com/szczyglis-dev/py-gpt"
pygpt_net/app.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.22 09:00:00 #
9
+ # Updated Date: 2025.09.22 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -203,13 +203,14 @@ from pygpt_net.tools.media_player import MediaPlayer as MediaPlayerTool
203
203
  from pygpt_net.tools.text_editor import TextEditor as TextEditorTool
204
204
  from pygpt_net.tools.html_canvas import HtmlCanvas as HtmlCanvasTool
205
205
  from pygpt_net.tools.translator import Translator as TranslatorTool
206
+ from pygpt_net.tools.web_browser import WebBrowser as WebBrowserTool
206
207
  # from pygpt_net.tools.agent_builder import AgentBuilder as AgentBuilderTool
207
208
 
208
209
  def run(**kwargs):
209
210
  """
210
- PyGPT launcher.
211
+ PyGPT Launcher
211
212
 
212
- :param kwargs: keyword arguments for launcher
213
+ :param kwargs: Keyword arguments for the launcher.
213
214
 
214
215
  PyGPT can be extended with:
215
216
 
@@ -223,46 +224,48 @@ def run(**kwargs):
223
224
  - Custom tools
224
225
  - Custom agents
225
226
 
226
- - You can pass custom plugin instances, LLM wrappers, vector store providers and more to the launcher.
227
- - This is useful if you want to extend PyGPT with your own plugins, vector storage, LLMs, or other data providers.
227
+ You can provide custom plugin instances, LLM wrappers, vector store providers, and more to the launcher.
228
+ This is useful for extending PyGPT with your own plugins, vector storage, LLMs, or other data providers.
228
229
 
229
- First, create a custom launcher file, for example, "custom_launcher.py," and register your extensions in it.
230
+ --- HOW TO REGISTER CUSTOM EXTENSIONS ---
230
231
 
231
- To register a custom plugin - create the custom launcher, e.g. "custom_launcher.py" and:
232
+ 1. First, create a custom launcher file, such as "custom_launcher.py," and register your extensions in it.
232
233
 
233
- - Pass a list with the plugin instances as the 'plugins' keyword argument.
234
+ To register a custom plugin, create the custom launcher (e.g., "custom_launcher.py") and:
235
+
236
+ - Pass a list containing the plugin instances as the `plugins` keyword argument.
234
237
 
235
238
  To register a custom LLM wrapper:
236
239
 
237
- - Pass a list with the LLM wrapper instances as the 'llms' keyword argument.
240
+ - Pass a list containing the LLM wrapper instances as the `llms` keyword argument.
238
241
 
239
242
  To register a custom vector store provider:
240
243
 
241
- - Pass a list with the vector store provider instances as the 'vector_stores' keyword argument.
244
+ - Pass a list containing the vector store provider instances as the `vector_stores` keyword argument.
242
245
 
243
246
  To register a custom data loader:
244
247
 
245
- - Pass a list with the data loader instances as the 'loaders' keyword argument.
248
+ - Pass a list containing the data loader instances as the `loaders` keyword argument.
246
249
 
247
250
  To register a custom audio input provider:
248
251
 
249
- - Pass a list with the audio input provider instances as the 'audio_input' keyword argument.
252
+ - Pass a list containing the audio input provider instances as the `audio_input` keyword argument.
250
253
 
251
254
  To register a custom audio output provider:
252
255
 
253
- - Pass a list with the audio output provider instances as the 'audio_output' keyword argument.
256
+ - Pass a list containing the audio output provider instances as the `audio_output` keyword argument.
254
257
 
255
258
  To register a custom web provider:
256
259
 
257
- - Pass a list with the web provider instances as the 'web' keyword argument.
260
+ - Pass a list containing the web provider instances as the `web` keyword argument.
258
261
 
259
262
  To register a custom agent:
260
263
 
261
- - Pass a list with the agent instances as the 'agents' keyword argument.
264
+ - Pass a list containing the agent instances as the `agents` keyword argument.
262
265
 
263
266
  To register a custom tool:
264
267
 
265
- - Pass a list with the tool instances as the 'tools' keyword argument.
268
+ - Pass a list containing the tool instances as the `tools` keyword argument.
266
269
 
267
270
  Example:
268
271
  --------
@@ -271,15 +274,16 @@ def run(**kwargs):
271
274
  # custom_launcher.py
272
275
 
273
276
  from pygpt_net.app import run
274
- from plugins import CustomPlugin, OtherCustomPlugin
275
- from llms import CustomLLM
276
- from vector_stores import CustomVectorStore
277
- from loaders import CustomLoader
278
- from audio_input import CustomAudioInput
279
- from audio_output import CustomAudioOutput
280
- from web import CustomWebSearch
281
- from tools import CustomTool
282
- from agents import CustomAgent
277
+
278
+ from .my_plugins import CustomPlugin, OtherCustomPlugin
279
+ from .my_llms import CustomLLM
280
+ from .my_vector_stores import CustomVectorStore
281
+ from .my_loaders import CustomLoader
282
+ from .my_audio_input import CustomAudioInput
283
+ from .my_audio_output import CustomAudioOutput
284
+ from .my_web import CustomWebSearch
285
+ from .my_tools import CustomTool
286
+ from .my_agents import CustomAgent
283
287
 
284
288
  plugins = [
285
289
  CustomPlugin(),
@@ -511,6 +515,7 @@ def run(**kwargs):
511
515
  launcher.add_tool(CodeInterpreterTool())
512
516
  launcher.add_tool(HtmlCanvasTool())
513
517
  launcher.add_tool(TranslatorTool())
518
+ launcher.add_tool(WebBrowserTool())
514
519
  # launcher.add_tool(AgentBuilderTool())
515
520
 
516
521
  # register custom tools
pygpt_net/config.py CHANGED
@@ -208,6 +208,50 @@ class Config:
208
208
  workdir = "/data"
209
209
  return workdir
210
210
 
211
+ def remove_plugin_config(self, plugin: str, key: str = None) -> bool:
212
+ """
213
+ Remove plugin config or specific key
214
+
215
+ :param plugin: plugin name
216
+ :param key: key name (optional)
217
+ :return: True if removed
218
+ """
219
+ try:
220
+ if plugin not in self.data['plugins']:
221
+ return False
222
+ if key is None:
223
+ del self.data['plugins'][plugin]
224
+ self.window.core.plugins.remove_plugin_param_from_presets(plugin)
225
+ return True
226
+ if plugin in self.data['plugins'] \
227
+ and key in self.data['plugins'][plugin]:
228
+ del self.data['plugins'][plugin][key]
229
+ self.window.core.plugins.remove_plugin_param_from_presets(plugin, key)
230
+ return True
231
+ except Exception as e:
232
+ print(f"Error removing plugin config: {e}")
233
+ return False
234
+
235
+ def update_plugin_config(self, plugin: str, key: str, value: any) -> bool:
236
+ """
237
+ Update plugin config key with value
238
+
239
+ :param plugin: plugin name
240
+ :param key: key name
241
+ :param value: value
242
+ :return: True if updated
243
+ """
244
+ try:
245
+ if 'plugins' not in self.data:
246
+ self.data['plugins'] = {}
247
+ if plugin not in self.data['plugins']:
248
+ self.data['plugins'][plugin] = {}
249
+ self.data['plugins'][plugin][key] = value
250
+ return True
251
+ except Exception as e:
252
+ print(f"Error updating plugin config: {e}")
253
+ return False
254
+
211
255
  def get_app_path(self) -> str:
212
256
  """
213
257
  Return app data path
@@ -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 20:00:00 #
9
+ # Updated Date: 2025.09.22 12:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import Union
@@ -87,9 +87,11 @@ class RemoteTools:
87
87
  """
88
88
  state = self.enabled_global["web_search"]
89
89
  if state:
90
- self.window.ui.nodes['icon.remote_tool.web'].set_icon(":/icons/web_on.svg")
90
+ self.window.ui.nodes['input'].set_icon_state("web", True)
91
+ # self.window.ui.nodes['icon.remote_tool.web'].set_icon(":/icons/web_on.svg")
91
92
  else:
92
- self.window.ui.nodes['icon.remote_tool.web'].set_icon(":/icons/web_off.svg")
93
+ self.window.ui.nodes['input'].set_icon_state("web", False)
94
+ # self.window.ui.nodes['icon.remote_tool.web'].set_icon(":/icons/web_off.svg")
93
95
 
94
96
  def toggle(self, tool_name: str):
95
97
  """
@@ -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.14 20:00:00 #
9
+ # Updated Date: 2025.09.22 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from datetime import datetime
@@ -74,8 +74,8 @@ class Debug(QObject):
74
74
 
75
75
  :param url: debug URL
76
76
  """
77
- self.window.tools.get("html_canvas").set_url(url)
78
- self.window.tools.get("html_canvas").auto_open(load=False)
77
+ self.window.tools.get("web_browser").set_url(url)
78
+ self.window.tools.get("web_browser").auto_open(load=False)
79
79
 
80
80
  def open_dev_tools(self) -> None:
81
81
  """
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.03 14:00:00 #
9
+ # Updated Date: 2025.09.22 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import QUrl
@@ -69,7 +69,11 @@ class Info:
69
69
  :param url: URL to open
70
70
  """
71
71
  if url:
72
- QDesktopServices.openUrl(QUrl(url))
72
+ if self.window.core.config.get("ctx.urls.internal", False):
73
+ self.window.tools.get("web_browser").set_url(url)
74
+ self.window.tools.get("web_browser").auto_open(load=False)
75
+ else:
76
+ QDesktopServices.openUrl(QUrl(url))
73
77
 
74
78
  def goto_website(self):
75
79
  """Open project website"""
@@ -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 20:00:00 #
9
+ # Updated Date: 2025.09.22 12:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from pygpt_net.core.types import (
@@ -197,9 +197,11 @@ class Mode:
197
197
 
198
198
  # remote tools icon visibility
199
199
  if not is_image and not is_completion:
200
- ui_nodes['icon.remote_tool.web'].setVisible(True)
200
+ self.window.ui.nodes['input'].set_icon_visible("web", True)
201
+ # ui_nodes['icon.remote_tool.web'].setVisible(True)
201
202
  else:
202
- ui_nodes['icon.remote_tool.web'].setVisible(False)
203
+ self.window.ui.nodes['input'].set_icon_visible("web", False)
204
+ # ui_nodes['icon.remote_tool.web'].setVisible(False)
203
205
 
204
206
  ui_tabs['input'].setTabVisible(2, is_assistant)
205
207
  ui_tabs['input'].setTabVisible(3, (not is_assistant) and (not is_image))
@@ -688,6 +688,23 @@ class Tabs:
688
688
  self.window.core.tabs.update_title(idx, title, tooltip)
689
689
  self.debug()
690
690
 
691
+ def update_title_by_tab(self, tab: Tab, title: str):
692
+ """
693
+ Update tab title by Tab instance
694
+
695
+ :param tab: Tab instance
696
+ :param title: new title
697
+ """
698
+ if tab is None:
699
+ return
700
+ tabs = self.window.ui.layout.get_tabs_by_idx(tab.column_idx)
701
+ tooltip = title
702
+ tabs.setTabToolTip(tab.idx, tooltip)
703
+ if len(title) > self.TAB_CHAT_MAX_CHARS:
704
+ title = title[:self.TAB_CHAT_MAX_CHARS] + '...'
705
+ tabs.setTabText(tab.idx, title)
706
+ self.debug()
707
+
691
708
  def update_title_current(self, title: str):
692
709
  """
693
710
  Update current tab title
@@ -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.12.09 03:00:00 #
9
+ # Updated Date: 2025.09.22 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import QUrl
@@ -85,7 +85,10 @@ class Url:
85
85
  elif url.scheme() == 'extra-code-copy': # copy code block
86
86
  id = url.toString().split(':')[1]
87
87
  self.window.controller.ctx.extra.copy_code_block(int(id))
88
-
89
88
  else:
90
89
  # external link
90
+ if url.scheme().startswith('http'):
91
+ self.window.controller.dialogs.info.open_url(url.toString())
92
+ return
93
+
91
94
  QDesktopServices.openUrl(url)
@@ -475,7 +475,8 @@ class Models:
475
475
 
476
476
  if cfg.has('api_proxy'):
477
477
  proxy = cfg.get('api_proxy')
478
- if proxy:
478
+ if proxy and cfg.get('api_proxy.enabled', False):
479
+ args["api_proxy"] = proxy
479
480
  transport = SyncProxyTransport.from_url(proxy)
480
481
  args["http_client"] = DefaultHttpxClient(transport=transport)
481
482
 
@@ -357,6 +357,66 @@ class Plugins:
357
357
  """Load presets"""
358
358
  self.presets = self.provider.load()
359
359
 
360
+ def remove_plugin_param_from_presets(self, plugin_id: str, param: str = None) -> bool:
361
+ """
362
+ Remove plugin param from all presets
363
+
364
+ :param plugin_id: plugin id
365
+ :param param: param key
366
+ :return: True if updated
367
+ """
368
+ updated = False
369
+ if self.presets is None:
370
+ self.load_presets()
371
+
372
+ if self.presets is None:
373
+ return False
374
+
375
+ if param is None:
376
+ # remove all params for plugin
377
+ for _preset_id, preset in self.presets.items():
378
+ preset_config = preset["config"]
379
+ if plugin_id in preset_config:
380
+ preset_config.pop(plugin_id)
381
+ updated = True
382
+ if updated:
383
+ self.save_presets()
384
+ return updated
385
+
386
+ for _preset_id, preset in self.presets.items():
387
+ preset_config = preset["config"]
388
+ if plugin_id in preset_config and param in preset_config[plugin_id]:
389
+ preset_config[plugin_id].pop(param)
390
+ updated = True
391
+ if updated:
392
+ self.save_presets()
393
+ return updated
394
+
395
+ def update_param_in_presets(self, plugin_id: str, param: str, value: Any) -> bool:
396
+ """
397
+ Update plugin param in all presets
398
+
399
+ :param plugin_id: plugin id
400
+ :param param: param key
401
+ :param value: param value
402
+ :return: True if updated
403
+ """
404
+ updated = False
405
+ if self.presets is None:
406
+ self.load_presets()
407
+
408
+ if self.presets is None:
409
+ return False
410
+
411
+ for _preset_id, preset in self.presets.items():
412
+ preset_config = preset["config"]
413
+ if plugin_id in preset_config and param in preset_config[plugin_id]:
414
+ preset_config[plugin_id][param] = value
415
+ updated = True
416
+ if updated:
417
+ self.save_presets()
418
+ return updated
419
+
360
420
  def get_presets(self) -> Dict[str, Any]:
361
421
  """
362
422
  Return all presets
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "__meta__": {
3
- "version": "2.6.56",
4
- "app.version": "2.6.56",
3
+ "version": "2.6.58",
4
+ "app.version": "2.6.58",
5
5
  "updated_at": "2025-09-22T00:00:00"
6
6
  },
7
7
  "access.audio.event.speech": false,
@@ -96,6 +96,7 @@
96
96
  "api_native_google.use_vertex": false,
97
97
  "api_native_xai": true,
98
98
  "api_proxy": "",
99
+ "api_proxy.enabled": false,
99
100
  "api_use_responses": true,
100
101
  "api_use_responses_llama": true,
101
102
  "app.env": [
@@ -163,6 +164,7 @@
163
164
  "ctx.search_content": true,
164
165
  "ctx.search.string": "",
165
166
  "ctx.sources": true,
167
+ "ctx.urls.internal": false,
166
168
  "ctx.use_extra": true,
167
169
  "current_model": {
168
170
  "assistant": "gpt-4o",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "__meta__": {
3
- "version": "2.6.56",
4
- "app.version": "2.6.56",
3
+ "version": "2.6.58",
4
+ "app.version": "2.6.58",
5
5
  "updated_at": "2025-09-22T00:00:00"
6
6
  },
7
7
  "items": {
@@ -14,7 +14,7 @@
14
14
  "bold": true
15
15
  },
16
16
  "urls": {
17
- "OpenAI API Keys": "https://platform.openai.com/account/api-keys"
17
+ "API Keys": "https://platform.openai.com/account/api-keys"
18
18
  },
19
19
  "secret": true,
20
20
  "persist": true,
@@ -98,6 +98,9 @@
98
98
  "extra": {
99
99
  "bold": true
100
100
  },
101
+ "urls": {
102
+ "API Keys": "https://aistudio.google.com/app/apikey"
103
+ },
101
104
  "secret": true,
102
105
  "persist": true,
103
106
  "advanced": false,
@@ -207,6 +210,9 @@
207
210
  "extra": {
208
211
  "bold": true
209
212
  },
213
+ "urls": {
214
+ "API Keys": "https://platform.claude.com/settings/keys"
215
+ },
210
216
  "secret": true,
211
217
  "persist": true,
212
218
  "advanced": false,
@@ -256,6 +262,9 @@
256
262
  "extra": {
257
263
  "bold": true
258
264
  },
265
+ "urls": {
266
+ "API Keys": "https://huggingface.co/settings/tokens"
267
+ },
259
268
  "secret": true,
260
269
  "persist": true,
261
270
  "advanced": false,
@@ -290,6 +299,9 @@
290
299
  "extra": {
291
300
  "bold": true
292
301
  },
302
+ "urls": {
303
+ "API Keys": "https://platform.deepseek.com/api_keys"
304
+ },
293
305
  "secret": true,
294
306
  "persist": true,
295
307
  "advanced": false,
@@ -324,6 +336,9 @@
324
336
  "extra": {
325
337
  "bold": true
326
338
  },
339
+ "urls": {
340
+ "API Keys": "https://console.x.ai"
341
+ },
327
342
  "secret": true,
328
343
  "persist": true,
329
344
  "advanced": false,
@@ -405,6 +420,9 @@
405
420
  "extra": {
406
421
  "bold": true
407
422
  },
423
+ "urls": {
424
+ "API Keys": "https://www.perplexity.ai/account/api/keys"
425
+ },
408
426
  "secret": true,
409
427
  "persist": true,
410
428
  "advanced": false,
@@ -439,6 +457,9 @@
439
457
  "extra": {
440
458
  "bold": true
441
459
  },
460
+ "urls": {
461
+ "API Keys": "https://admin.mistral.ai/organization/api-keys"
462
+ },
442
463
  "secret": true,
443
464
  "persist": true,
444
465
  "advanced": false,
@@ -473,6 +494,9 @@
473
494
  "extra": {
474
495
  "bold": true
475
496
  },
497
+ "urls": {
498
+ "API Keys": "https://dashboard.voyageai.com/organization/api-keys"
499
+ },
476
500
  "secret": true,
477
501
  "persist": true,
478
502
  "advanced": false,
@@ -492,6 +516,9 @@
492
516
  "extra": {
493
517
  "bold": true
494
518
  },
519
+ "urls": {
520
+ "API Keys": "https://openrouter.ai/settings/keys"
521
+ },
495
522
  "secret": true,
496
523
  "persist": true,
497
524
  "advanced": false,
@@ -579,6 +606,20 @@
579
606
  "step": 1,
580
607
  "advanced": false
581
608
  },
609
+ "api_proxy.enabled": {
610
+ "section": "general",
611
+ "type": "bool",
612
+ "slider": false,
613
+ "label": "settings.api_proxy.enabled",
614
+ "description": "settings.api_proxy.enabled.desc",
615
+ "value": true,
616
+ "min": null,
617
+ "max": null,
618
+ "multiplier": null,
619
+ "step": null,
620
+ "secret": false,
621
+ "advanced": false
622
+ },
582
623
  "api_proxy": {
583
624
  "section": "general",
584
625
  "type": "text",
@@ -1125,7 +1166,19 @@
1125
1166
  "step": null,
1126
1167
  "advanced": false
1127
1168
  },
1128
-
1169
+ "ctx.urls.internal": {
1170
+ "section": "ctx",
1171
+ "type": "bool",
1172
+ "slider": false,
1173
+ "label": "settings.ctx.urls.internal",
1174
+ "description": "settings.ctx.urls.internal.desc",
1175
+ "value": true,
1176
+ "min": null,
1177
+ "max": null,
1178
+ "multiplier": null,
1179
+ "step": null,
1180
+ "secret": false
1181
+ },
1129
1182
  "ctx.convert_lists": {
1130
1183
  "section": "ctx",
1131
1184
  "type": "bool",
@@ -46,7 +46,7 @@ class UIManager {
46
46
  '.msg-box.msg-user .msg .msg-copy-btn { position: absolute; top: 2px; right: 0px; z-index: 3;',
47
47
  ' opacity: 0; pointer-events: none; transition: opacity .15s ease, transform .15s ease, background-color .15s ease, border-color .15s ease;',
48
48
  ' border-radius: 6px; padding: 4px; line-height: 0; border: 1px solid transparent; background: transparent; }',
49
- '.msg-box.msg-user:hover .msg .msg-copy-btn, .msg-box.msg-user .msg:focus-within .msg-copy-btn { opacity: 1; pointer-events: auto; }',
49
+ '.msg-box.msg-user .msg:hover .msg-copy-btn, .msg-box.msg-user .msg:focus-within .msg-copy-btn { opacity: 1; pointer-events: auto; }',
50
50
  '.msg-box.msg-user .msg .msg-copy-btn:hover { transform: scale(1.06); background: var(--copy-btn-bg-hover, rgba(0,0,0,.86)); border-color: var(--copy-btn-border, rgba(0,0,0,.08)); }',
51
51
  '.msg-box.msg-user .msg .msg-copy-btn.copied { background: var(--copy-btn-bg-copied, rgba(150,150,150,.12)); border-color: var(--copy-btn-border-copied, rgba(150,150,150,.35)); animation: msg-copy-pop .25s ease; }',
52
52
  '.msg-box.msg-user .msg .msg-copy-btn img { display: block; width: 18px; height: 18px; }',
@@ -1,4 +1,4 @@
1
- /* app.min.js — generated on 2025-09-22 09:05:08 by bin/minify_js.py using rjsmin */
1
+ /* app.min.js — generated on 2025-09-22 22:18:13 by bin/minify_js.py using rjsmin */
2
2
 
3
3
  /* data/js/app/async.js */
4
4
  class AsyncRunner{constructor(cfg,raf){this.cfg=cfg||{};this.raf=raf||null;const A=this.cfg.ASYNC||{};this.SLICE_MS=Utils.g('ASYNC_SLICE_MS',A.SLICE_MS??12);this.SLICE_HIDDEN_MS=Utils.g('ASYNC_SLICE_HIDDEN_MS',A.SLICE_HIDDEN_MS??Math.min(this.SLICE_MS,6));this.MIN_YIELD_MS=Utils.g('ASYNC_MIN_YIELD_MS',A.MIN_YIELD_MS??0);this._opGen=new Map();}
@@ -803,7 +803,7 @@ toggle(id){const el=document.getElementById('msg-bot-'+id);if(!el)return;const o
803
803
  /* data/js/app/ui.js */
804
804
  class UIManager{updateCSS(styles){let style=document.getElementById('app-style');if(!style){style=document.createElement('style');style.id='app-style';document.head.appendChild(style);}
805
805
  style.textContent=styles;}
806
- ensureStickyHeaderStyle(){let style=document.getElementById('code-sticky-style');if(style)return;style=document.createElement('style');style.id='code-sticky-style';style.textContent=['.code-wrapper { position: relative; }','.code-wrapper .code-header-wrapper { position: sticky; top: var(--code-header-sticky-top, -2px); z-index: 2; box-shadow: 0 1px 0 rgba(0,0,0,.06); }','.code-wrapper pre { overflow: visible; margin-top: 0; }','.code-wrapper pre code { display: block; white-space: pre; max-height: 100dvh; overflow: auto;',' overscroll-behavior: contain; -webkit-overflow-scrolling: touch; overflow-anchor: none; scrollbar-gutter: stable both-edges; scroll-behavior: auto; }','#_loader_.hidden { display: none !important; visibility: hidden !important; }','#_loader_.visible { display: block; visibility: visible; }','.msg-box.msg-user .msg { position: relative; }','.msg-box.msg-user .msg > .uc-content { display: block; overflow: visible; }','.msg-box.msg-user .msg > .uc-content.uc-collapsed { max-height: 1000px; overflow: hidden; }','.msg-box.msg-user .msg > .uc-toggle { display: none; margin-top: 8px; text-align: center; cursor: pointer; user-select: none; }','.msg-box.msg-user .msg > .uc-toggle.visible { display: block; }','.msg-box.msg-user .msg > .uc-toggle img { width: var(--uc-toggle-icon-size, 26px); height: var(--uc-toggle-icon-size, 26px); opacity: .8; }','.msg-box.msg-user .msg > .uc-toggle:hover img { opacity: 1; }','.msg-box.msg-user .msg .msg-copy-btn { position: absolute; top: 2px; right: 0px; z-index: 3;',' opacity: 0; pointer-events: none; transition: opacity .15s ease, transform .15s ease, background-color .15s ease, border-color .15s ease;',' border-radius: 6px; padding: 4px; line-height: 0; border: 1px solid transparent; background: transparent; }','.msg-box.msg-user:hover .msg .msg-copy-btn, .msg-box.msg-user .msg:focus-within .msg-copy-btn { opacity: 1; pointer-events: auto; }','.msg-box.msg-user .msg .msg-copy-btn:hover { transform: scale(1.06); background: var(--copy-btn-bg-hover, rgba(0,0,0,.86)); border-color: var(--copy-btn-border, rgba(0,0,0,.08)); }','.msg-box.msg-user .msg .msg-copy-btn.copied { background: var(--copy-btn-bg-copied, rgba(150,150,150,.12)); border-color: var(--copy-btn-border-copied, rgba(150,150,150,.35)); animation: msg-copy-pop .25s ease; }','.msg-box.msg-user .msg .msg-copy-btn img { display: block; width: 18px; height: 18px; }','.code-wrapper .code-header-action.code-header-copy,','.code-wrapper .code-header-action.code-header-collapse { display: inline-flex; align-items: center; border-radius: 6px; padding: 2px; line-height: 0; border: 1px solid transparent; transition: transform .15s ease, background-color .15s ease, border-color .15s ease; }','.code-wrapper .code-header-action.code-header-copy:hover,','.code-wrapper .code-header-action.code-header-collapse:hover { transform: scale(1.06); background: var(--copy-btn-bg-hover, rgba(0,0,0,.76)); border-color: var(--copy-btn-border, rgba(0,0,0,.08)); }','.code-wrapper .code-header-action.copied { background: var(--copy-btn-bg-copied, rgba(150,150,150,.12)); border-color: var(--copy-btn-border-copied, rgba(150,150,150,.35)); animation: msg-copy-pop .25s ease; }','@keyframes msg-copy-pop { 0%{ transform: scale(1); } 60%{ transform: scale(1.1); } 100%{ transform: scale(1); } }'].join('\n');document.head.appendChild(style);}
806
+ ensureStickyHeaderStyle(){let style=document.getElementById('code-sticky-style');if(style)return;style=document.createElement('style');style.id='code-sticky-style';style.textContent=['.code-wrapper { position: relative; }','.code-wrapper .code-header-wrapper { position: sticky; top: var(--code-header-sticky-top, -2px); z-index: 2; box-shadow: 0 1px 0 rgba(0,0,0,.06); }','.code-wrapper pre { overflow: visible; margin-top: 0; }','.code-wrapper pre code { display: block; white-space: pre; max-height: 100dvh; overflow: auto;',' overscroll-behavior: contain; -webkit-overflow-scrolling: touch; overflow-anchor: none; scrollbar-gutter: stable both-edges; scroll-behavior: auto; }','#_loader_.hidden { display: none !important; visibility: hidden !important; }','#_loader_.visible { display: block; visibility: visible; }','.msg-box.msg-user .msg { position: relative; }','.msg-box.msg-user .msg > .uc-content { display: block; overflow: visible; }','.msg-box.msg-user .msg > .uc-content.uc-collapsed { max-height: 1000px; overflow: hidden; }','.msg-box.msg-user .msg > .uc-toggle { display: none; margin-top: 8px; text-align: center; cursor: pointer; user-select: none; }','.msg-box.msg-user .msg > .uc-toggle.visible { display: block; }','.msg-box.msg-user .msg > .uc-toggle img { width: var(--uc-toggle-icon-size, 26px); height: var(--uc-toggle-icon-size, 26px); opacity: .8; }','.msg-box.msg-user .msg > .uc-toggle:hover img { opacity: 1; }','.msg-box.msg-user .msg .msg-copy-btn { position: absolute; top: 2px; right: 0px; z-index: 3;',' opacity: 0; pointer-events: none; transition: opacity .15s ease, transform .15s ease, background-color .15s ease, border-color .15s ease;',' border-radius: 6px; padding: 4px; line-height: 0; border: 1px solid transparent; background: transparent; }','.msg-box.msg-user .msg:hover .msg-copy-btn, .msg-box.msg-user .msg:focus-within .msg-copy-btn { opacity: 1; pointer-events: auto; }','.msg-box.msg-user .msg .msg-copy-btn:hover { transform: scale(1.06); background: var(--copy-btn-bg-hover, rgba(0,0,0,.86)); border-color: var(--copy-btn-border, rgba(0,0,0,.08)); }','.msg-box.msg-user .msg .msg-copy-btn.copied { background: var(--copy-btn-bg-copied, rgba(150,150,150,.12)); border-color: var(--copy-btn-border-copied, rgba(150,150,150,.35)); animation: msg-copy-pop .25s ease; }','.msg-box.msg-user .msg .msg-copy-btn img { display: block; width: 18px; height: 18px; }','.code-wrapper .code-header-action.code-header-copy,','.code-wrapper .code-header-action.code-header-collapse { display: inline-flex; align-items: center; border-radius: 6px; padding: 2px; line-height: 0; border: 1px solid transparent; transition: transform .15s ease, background-color .15s ease, border-color .15s ease; }','.code-wrapper .code-header-action.code-header-copy:hover,','.code-wrapper .code-header-action.code-header-collapse:hover { transform: scale(1.06); background: var(--copy-btn-bg-hover, rgba(0,0,0,.76)); border-color: var(--copy-btn-border, rgba(0,0,0,.08)); }','.code-wrapper .code-header-action.copied { background: var(--copy-btn-bg-copied, rgba(150,150,150,.12)); border-color: var(--copy-btn-border-copied, rgba(150,150,150,.35)); animation: msg-copy-pop .25s ease; }','@keyframes msg-copy-pop { 0%{ transform: scale(1); } 60%{ transform: scale(1.1); } 100%{ transform: scale(1); } }'].join('\n');document.head.appendChild(style);}
807
807
  enableEditIcons(){document.body&&document.body.classList.add('display-edit-icons');}
808
808
  disableEditIcons(){document.body&&document.body.classList.remove('display-edit-icons');}
809
809
  enableTimestamp(){document.body&&document.body.classList.add('display-timestamp');}