pygpt-net 2.6.3__py3-none-any.whl → 2.6.4__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 +5 -0
- pygpt_net/__init__.py +1 -1
- pygpt_net/config.py +55 -65
- pygpt_net/controller/chat/chat.py +38 -35
- pygpt_net/controller/chat/render.py +144 -217
- pygpt_net/controller/chat/stream.py +51 -25
- pygpt_net/controller/config/config.py +39 -42
- pygpt_net/controller/config/field/checkbox.py +16 -12
- pygpt_net/controller/config/field/checkbox_list.py +36 -31
- pygpt_net/controller/config/field/cmd.py +51 -57
- pygpt_net/controller/config/field/combo.py +33 -16
- pygpt_net/controller/config/field/dictionary.py +48 -55
- pygpt_net/controller/config/field/input.py +50 -32
- pygpt_net/controller/config/field/slider.py +40 -45
- pygpt_net/controller/config/field/textarea.py +20 -6
- pygpt_net/controller/config/placeholder.py +110 -231
- pygpt_net/controller/lang/mapping.py +57 -95
- pygpt_net/controller/lang/plugins.py +64 -55
- pygpt_net/controller/lang/settings.py +39 -38
- pygpt_net/controller/layout/layout.py +11 -2
- pygpt_net/controller/plugins/plugins.py +19 -1
- pygpt_net/controller/ui/mode.py +107 -125
- pygpt_net/core/bridge/bridge.py +5 -5
- pygpt_net/core/command/command.py +149 -219
- pygpt_net/core/ctx/ctx.py +94 -146
- pygpt_net/core/debug/debug.py +48 -58
- pygpt_net/core/models/models.py +74 -112
- pygpt_net/core/modes/modes.py +13 -21
- pygpt_net/core/plugins/plugins.py +154 -177
- pygpt_net/core/presets/presets.py +103 -176
- pygpt_net/core/render/web/body.py +2 -3
- pygpt_net/core/render/web/renderer.py +109 -180
- pygpt_net/core/text/utils.py +28 -44
- pygpt_net/core/tokens/tokens.py +104 -203
- pygpt_net/data/config/config.json +2 -2
- pygpt_net/data/config/models.json +2 -2
- pygpt_net/item/ctx.py +141 -139
- pygpt_net/plugin/agent/plugin.py +2 -1
- pygpt_net/plugin/audio_output/plugin.py +5 -2
- pygpt_net/plugin/base/plugin.py +77 -93
- pygpt_net/plugin/bitbucket/plugin.py +3 -2
- pygpt_net/plugin/cmd_code_interpreter/plugin.py +3 -2
- pygpt_net/plugin/cmd_custom/plugin.py +3 -2
- pygpt_net/plugin/cmd_files/plugin.py +3 -2
- pygpt_net/plugin/cmd_history/plugin.py +3 -2
- pygpt_net/plugin/cmd_mouse_control/plugin.py +5 -2
- pygpt_net/plugin/cmd_serial/plugin.py +3 -2
- pygpt_net/plugin/cmd_system/plugin.py +3 -6
- pygpt_net/plugin/cmd_web/plugin.py +3 -2
- pygpt_net/plugin/experts/plugin.py +2 -2
- pygpt_net/plugin/facebook/plugin.py +3 -4
- pygpt_net/plugin/github/plugin.py +4 -2
- pygpt_net/plugin/google/plugin.py +3 -3
- pygpt_net/plugin/idx_llama_index/plugin.py +3 -2
- pygpt_net/plugin/mailer/plugin.py +3 -5
- pygpt_net/plugin/openai_vision/plugin.py +3 -2
- pygpt_net/plugin/real_time/plugin.py +52 -60
- pygpt_net/plugin/slack/plugin.py +3 -4
- pygpt_net/plugin/telegram/plugin.py +3 -4
- pygpt_net/plugin/twitter/plugin.py +3 -4
- pygpt_net/ui/widget/textarea/web.py +18 -14
- {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.4.dist-info}/METADATA +7 -2
- {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.4.dist-info}/RECORD +66 -66
- {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.4.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.4.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.4.dist-info}/entry_points.txt +0 -0
|
@@ -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.
|
|
9
|
+
# Updated Date: 2025.08.15 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import copy
|
|
@@ -35,6 +35,22 @@ from pygpt_net.provider.core.preset.json_file import JsonFileProvider
|
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
class Presets:
|
|
38
|
+
_MODE_TO_ATTR = {
|
|
39
|
+
MODE_CHAT: "chat",
|
|
40
|
+
MODE_COMPLETION: "completion",
|
|
41
|
+
MODE_IMAGE: "img",
|
|
42
|
+
MODE_VISION: "vision",
|
|
43
|
+
MODE_LANGCHAIN: "langchain",
|
|
44
|
+
MODE_ASSISTANT: "assistant",
|
|
45
|
+
MODE_LLAMA_INDEX: "llama_index",
|
|
46
|
+
MODE_AGENT: "agent",
|
|
47
|
+
MODE_AGENT_LLAMA: "agent_llama",
|
|
48
|
+
MODE_AGENT_OPENAI: "agent_openai",
|
|
49
|
+
MODE_EXPERT: "expert",
|
|
50
|
+
MODE_RESEARCH: "research",
|
|
51
|
+
MODE_COMPUTER: "computer",
|
|
52
|
+
MODE_AUDIO: "audio",
|
|
53
|
+
}
|
|
38
54
|
|
|
39
55
|
def __init__(self, window=None):
|
|
40
56
|
"""
|
|
@@ -44,7 +60,7 @@ class Presets:
|
|
|
44
60
|
"""
|
|
45
61
|
self.window = window
|
|
46
62
|
self.provider = JsonFileProvider(window)
|
|
47
|
-
self.items = {}
|
|
63
|
+
self.items: Dict[str, PresetItem] = {}
|
|
48
64
|
|
|
49
65
|
def install(self):
|
|
50
66
|
"""Install provider data"""
|
|
@@ -70,114 +86,34 @@ class Presets:
|
|
|
70
86
|
|
|
71
87
|
def append_current(self):
|
|
72
88
|
"""Append current presets"""
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
id_computer = 'current.computer'
|
|
102
|
-
|
|
103
|
-
# set default initial prompt for chat mode
|
|
104
|
-
curr_chat.prompt = self.window.core.prompt.get('default')
|
|
105
|
-
|
|
106
|
-
# get data from presets if exists
|
|
107
|
-
if id_chat in self.items:
|
|
108
|
-
curr_chat = self.items[id_chat]
|
|
109
|
-
if id_completion in self.items:
|
|
110
|
-
curr_completion = self.items[id_completion]
|
|
111
|
-
if id_img in self.items:
|
|
112
|
-
curr_img = self.items[id_img]
|
|
113
|
-
if id_vision in self.items:
|
|
114
|
-
curr_vision = self.items[id_vision]
|
|
115
|
-
if id_langchain in self.items:
|
|
116
|
-
curr_langchain = self.items[id_langchain]
|
|
117
|
-
if id_assistant in self.items:
|
|
118
|
-
curr_assistant = self.items[id_assistant]
|
|
119
|
-
if id_llama in self.items:
|
|
120
|
-
curr_llama = self.items[id_llama]
|
|
121
|
-
if id_agent in self.items:
|
|
122
|
-
curr_agent = self.items[id_agent]
|
|
123
|
-
if id_agent_llama in self.items:
|
|
124
|
-
curr_agent_llama = self.items[id_agent_llama]
|
|
125
|
-
if id_agent_openai in self.items:
|
|
126
|
-
curr_agent_openai = self.items[id_agent_openai]
|
|
127
|
-
if id_expert in self.items:
|
|
128
|
-
curr_expert = self.items[id_expert]
|
|
129
|
-
if id_audio in self.items:
|
|
130
|
-
curr_audio = self.items[id_audio]
|
|
131
|
-
if id_computer in self.items:
|
|
132
|
-
curr_computer = self.items[id_computer]
|
|
133
|
-
|
|
134
|
-
# allow usage in specific mode
|
|
135
|
-
curr_chat.chat = True
|
|
136
|
-
curr_completion.completion = True
|
|
137
|
-
curr_img.img = True
|
|
138
|
-
curr_vision.vision = True
|
|
139
|
-
curr_langchain.langchain = True
|
|
140
|
-
curr_assistant.assistant = True
|
|
141
|
-
curr_llama.llama_index = True
|
|
142
|
-
curr_agent.agent = True
|
|
143
|
-
curr_agent_llama.agent_llama = True
|
|
144
|
-
curr_agent_openai.agent_openai = True
|
|
145
|
-
curr_expert.expert = True
|
|
146
|
-
curr_audio.audio = True
|
|
147
|
-
curr_computer.computer = True
|
|
148
|
-
|
|
149
|
-
# always apply default name
|
|
150
|
-
curr_chat.name = '*'
|
|
151
|
-
curr_completion.name = '*'
|
|
152
|
-
curr_img.name = '*'
|
|
153
|
-
curr_vision.name = '*'
|
|
154
|
-
curr_langchain.name = '*'
|
|
155
|
-
curr_assistant.name = '*'
|
|
156
|
-
curr_llama.name = '*'
|
|
157
|
-
curr_agent.name = '*'
|
|
158
|
-
curr_agent_llama.name = '*'
|
|
159
|
-
curr_agent_openai.name = '*'
|
|
160
|
-
curr_expert.name = '*'
|
|
161
|
-
curr_audio.name = '*'
|
|
162
|
-
curr_computer.name = '*'
|
|
163
|
-
|
|
164
|
-
# append at first position
|
|
165
|
-
self.items = {
|
|
166
|
-
id_chat: curr_chat,
|
|
167
|
-
id_completion: curr_completion,
|
|
168
|
-
id_img: curr_img,
|
|
169
|
-
id_vision: curr_vision,
|
|
170
|
-
id_langchain: curr_langchain,
|
|
171
|
-
id_assistant: curr_assistant,
|
|
172
|
-
id_llama: curr_llama,
|
|
173
|
-
id_agent: curr_agent,
|
|
174
|
-
id_agent_llama: curr_agent_llama,
|
|
175
|
-
id_agent_openai: curr_agent_openai,
|
|
176
|
-
id_expert: curr_expert,
|
|
177
|
-
id_audio: curr_audio,
|
|
178
|
-
id_computer: curr_computer,
|
|
179
|
-
**self.items
|
|
180
|
-
}
|
|
89
|
+
items = self.items
|
|
90
|
+
ids = [
|
|
91
|
+
("current.chat", "chat"),
|
|
92
|
+
("current.completion", "completion"),
|
|
93
|
+
("current.img", "img"),
|
|
94
|
+
("current.vision", "vision"),
|
|
95
|
+
("current.langchain", "langchain"),
|
|
96
|
+
("current.assistant", "assistant"),
|
|
97
|
+
("current.llama_index", "llama_index"),
|
|
98
|
+
("current.agent", "agent"),
|
|
99
|
+
("current.agent_llama", "agent_llama"),
|
|
100
|
+
("current.agent_openai", "agent_openai"),
|
|
101
|
+
("current.expert", "expert"),
|
|
102
|
+
("current.audio", "audio"),
|
|
103
|
+
("current.computer", "computer"),
|
|
104
|
+
]
|
|
105
|
+
front = {}
|
|
106
|
+
for pid, attr in ids:
|
|
107
|
+
if pid in items:
|
|
108
|
+
obj = items[pid]
|
|
109
|
+
else:
|
|
110
|
+
obj = self.build()
|
|
111
|
+
if pid == "current.chat":
|
|
112
|
+
obj.prompt = self.window.core.prompt.get("default")
|
|
113
|
+
setattr(obj, attr, True)
|
|
114
|
+
obj.name = "*"
|
|
115
|
+
front[pid] = obj
|
|
116
|
+
self.items = {**front, **items}
|
|
181
117
|
|
|
182
118
|
def exists(self, id: str) -> bool:
|
|
183
119
|
"""
|
|
@@ -186,9 +122,7 @@ class Presets:
|
|
|
186
122
|
:param id: preset id
|
|
187
123
|
:return: True if exists
|
|
188
124
|
"""
|
|
189
|
-
|
|
190
|
-
return True
|
|
191
|
-
return False
|
|
125
|
+
return id in self.items
|
|
192
126
|
|
|
193
127
|
def exists_uuid(self, uuid: str) -> bool:
|
|
194
128
|
"""
|
|
@@ -197,10 +131,7 @@ class Presets:
|
|
|
197
131
|
:param uuid: preset uuid
|
|
198
132
|
:return: True if exists
|
|
199
133
|
"""
|
|
200
|
-
for
|
|
201
|
-
if self.items[id].uuid == uuid:
|
|
202
|
-
return True
|
|
203
|
-
return False
|
|
134
|
+
return any(item.uuid == uuid for item in self.items.values())
|
|
204
135
|
|
|
205
136
|
def enable(self, id: str):
|
|
206
137
|
"""
|
|
@@ -266,10 +197,11 @@ class Presets:
|
|
|
266
197
|
:param id : preset id
|
|
267
198
|
:return: True if exists
|
|
268
199
|
"""
|
|
269
|
-
|
|
270
|
-
if
|
|
271
|
-
return
|
|
272
|
-
|
|
200
|
+
item = self.items.get(id)
|
|
201
|
+
if not item:
|
|
202
|
+
return False
|
|
203
|
+
attr = self._MODE_TO_ATTR.get(mode)
|
|
204
|
+
return bool(attr and getattr(item, attr, False))
|
|
273
205
|
|
|
274
206
|
def get_by_idx(self, idx: int, mode: str) -> str:
|
|
275
207
|
"""
|
|
@@ -279,8 +211,16 @@ class Presets:
|
|
|
279
211
|
:param mode: mode
|
|
280
212
|
:return: preset id
|
|
281
213
|
"""
|
|
282
|
-
|
|
283
|
-
|
|
214
|
+
attr = self._MODE_TO_ATTR.get(mode)
|
|
215
|
+
if not attr:
|
|
216
|
+
raise IndexError(idx)
|
|
217
|
+
i = 0
|
|
218
|
+
for key, item in self.items.items():
|
|
219
|
+
if getattr(item, attr, False):
|
|
220
|
+
if i == idx:
|
|
221
|
+
return key
|
|
222
|
+
i += 1
|
|
223
|
+
raise IndexError(idx)
|
|
284
224
|
|
|
285
225
|
def get_by_id(self, mode: str, id: str) -> Optional[PresetItem]:
|
|
286
226
|
"""
|
|
@@ -290,10 +230,13 @@ class Presets:
|
|
|
290
230
|
:param id: preset id
|
|
291
231
|
:return: preset item
|
|
292
232
|
"""
|
|
293
|
-
|
|
294
|
-
if
|
|
295
|
-
return
|
|
296
|
-
|
|
233
|
+
item = self.items.get(id)
|
|
234
|
+
if not item:
|
|
235
|
+
return None
|
|
236
|
+
attr = self._MODE_TO_ATTR.get(mode)
|
|
237
|
+
if not attr:
|
|
238
|
+
return None
|
|
239
|
+
return item if getattr(item, attr, False) else None
|
|
297
240
|
|
|
298
241
|
def get(self, id: str) -> Optional[PresetItem]:
|
|
299
242
|
"""
|
|
@@ -302,9 +245,7 @@ class Presets:
|
|
|
302
245
|
:param id: preset ID
|
|
303
246
|
:return: preset item
|
|
304
247
|
"""
|
|
305
|
-
|
|
306
|
-
return self.items[id]
|
|
307
|
-
return None
|
|
248
|
+
return self.items.get(id)
|
|
308
249
|
|
|
309
250
|
def get_by_uuid(self, uuid: str) -> Optional[PresetItem]:
|
|
310
251
|
"""
|
|
@@ -313,10 +254,7 @@ class Presets:
|
|
|
313
254
|
:param uuid: preset UUID
|
|
314
255
|
:return: preset item
|
|
315
256
|
"""
|
|
316
|
-
for
|
|
317
|
-
if self.items[id].uuid == uuid:
|
|
318
|
-
return self.items[id]
|
|
319
|
-
return None
|
|
257
|
+
return next((item for item in self.items.values() if item.uuid == uuid), None)
|
|
320
258
|
|
|
321
259
|
def get_by_mode(self, mode: str) -> Dict[str, PresetItem]:
|
|
322
260
|
"""
|
|
@@ -325,24 +263,10 @@ class Presets:
|
|
|
325
263
|
:param mode: mode name
|
|
326
264
|
:return: presets dict for mode
|
|
327
265
|
"""
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
or (mode == MODE_IMAGE and self.items[id].img) \
|
|
333
|
-
or (mode == MODE_VISION and self.items[id].vision) \
|
|
334
|
-
or (mode == MODE_LANGCHAIN and self.items[id].langchain) \
|
|
335
|
-
or (mode == MODE_ASSISTANT and self.items[id].assistant) \
|
|
336
|
-
or (mode == MODE_LLAMA_INDEX and self.items[id].llama_index) \
|
|
337
|
-
or (mode == MODE_AGENT and self.items[id].agent) \
|
|
338
|
-
or (mode == MODE_AGENT_LLAMA and self.items[id].agent_llama) \
|
|
339
|
-
or (mode == MODE_AGENT_OPENAI and self.items[id].agent_openai) \
|
|
340
|
-
or (mode == MODE_EXPERT and self.items[id].expert) \
|
|
341
|
-
or (mode == MODE_RESEARCH and self.items[id].research) \
|
|
342
|
-
or (mode == MODE_COMPUTER and self.items[id].computer) \
|
|
343
|
-
or (mode == MODE_AUDIO and self.items[id].audio):
|
|
344
|
-
presets[id] = self.items[id]
|
|
345
|
-
return presets
|
|
266
|
+
attr = self._MODE_TO_ATTR.get(mode)
|
|
267
|
+
if not attr:
|
|
268
|
+
return {}
|
|
269
|
+
return {id: item for id, item in self.items.items() if getattr(item, attr, False)}
|
|
346
270
|
|
|
347
271
|
def get_idx_by_id(self, mode: str, id: str) -> int:
|
|
348
272
|
"""
|
|
@@ -352,12 +276,15 @@ class Presets:
|
|
|
352
276
|
:param id: preset id
|
|
353
277
|
:return: preset idx
|
|
354
278
|
"""
|
|
355
|
-
|
|
279
|
+
attr = self._MODE_TO_ATTR.get(mode)
|
|
280
|
+
if not attr:
|
|
281
|
+
return 0
|
|
356
282
|
i = 0
|
|
357
|
-
for key in
|
|
358
|
-
if
|
|
359
|
-
|
|
360
|
-
|
|
283
|
+
for key, item in self.items.items():
|
|
284
|
+
if getattr(item, attr, False):
|
|
285
|
+
if key == id:
|
|
286
|
+
return i
|
|
287
|
+
i += 1
|
|
361
288
|
return 0
|
|
362
289
|
|
|
363
290
|
def get_default(self, mode: str) -> Optional[str]:
|
|
@@ -367,10 +294,13 @@ class Presets:
|
|
|
367
294
|
:param mode: mode name
|
|
368
295
|
:return: default prompt name
|
|
369
296
|
"""
|
|
370
|
-
|
|
371
|
-
if
|
|
297
|
+
attr = self._MODE_TO_ATTR.get(mode)
|
|
298
|
+
if not attr:
|
|
372
299
|
return None
|
|
373
|
-
|
|
300
|
+
for key, item in self.items.items():
|
|
301
|
+
if getattr(item, attr, False):
|
|
302
|
+
return key
|
|
303
|
+
return None
|
|
374
304
|
|
|
375
305
|
def get_duplicate_name(self, id: str) -> Tuple[str, str]:
|
|
376
306
|
"""
|
|
@@ -400,7 +330,7 @@ class Presets:
|
|
|
400
330
|
self.items[id] = copy.deepcopy(self.items[prev_id])
|
|
401
331
|
self.items[id].name = name
|
|
402
332
|
self.items[id].filename = id
|
|
403
|
-
self.items[id].uuid = str(uuid.uuid4())
|
|
333
|
+
self.items[id].uuid = str(uuid.uuid4())
|
|
404
334
|
self.sort_by_name()
|
|
405
335
|
return id
|
|
406
336
|
|
|
@@ -420,7 +350,6 @@ class Presets:
|
|
|
420
350
|
|
|
421
351
|
if remove_file:
|
|
422
352
|
self.provider.remove(id)
|
|
423
|
-
# self.load_presets()
|
|
424
353
|
|
|
425
354
|
def sort_by_name(self):
|
|
426
355
|
"""Sort presets by name"""
|
|
@@ -479,10 +408,8 @@ class Presets:
|
|
|
479
408
|
def load(self):
|
|
480
409
|
"""Load presets templates"""
|
|
481
410
|
self.items = self.provider.load()
|
|
482
|
-
self.patch_empty()
|
|
483
|
-
self.patch_duplicated()
|
|
484
|
-
|
|
485
|
-
# sort presets
|
|
411
|
+
self.patch_empty()
|
|
412
|
+
self.patch_duplicated()
|
|
486
413
|
self.sort_by_name()
|
|
487
414
|
self.append_current()
|
|
488
415
|
|
|
@@ -538,12 +465,12 @@ class Presets:
|
|
|
538
465
|
def patch_empty(self):
|
|
539
466
|
"""Patch UUIDs for all presets"""
|
|
540
467
|
patched = False
|
|
541
|
-
for
|
|
542
|
-
if
|
|
543
|
-
|
|
468
|
+
for key, item in self.items.items():
|
|
469
|
+
if item.uuid is None:
|
|
470
|
+
item.uuid = str(uuid.uuid4())
|
|
544
471
|
patched = True
|
|
545
|
-
if
|
|
546
|
-
|
|
472
|
+
if item.filename is None or item.filename == "":
|
|
473
|
+
item.filename = key
|
|
547
474
|
patched = True
|
|
548
475
|
if patched:
|
|
549
476
|
self.save_all()
|
|
@@ -551,11 +478,11 @@ class Presets:
|
|
|
551
478
|
def patch_duplicated(self):
|
|
552
479
|
"""Patch UUIDs for all presets"""
|
|
553
480
|
patched = False
|
|
554
|
-
uuids =
|
|
555
|
-
for
|
|
556
|
-
if
|
|
557
|
-
|
|
481
|
+
uuids = set()
|
|
482
|
+
for item in self.items.values():
|
|
483
|
+
if item.uuid in uuids:
|
|
484
|
+
item.uuid = str(uuid.uuid4())
|
|
558
485
|
patched = True
|
|
559
|
-
uuids.
|
|
486
|
+
uuids.add(item.uuid)
|
|
560
487
|
if patched:
|
|
561
488
|
self.save_all()
|
|
@@ -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.15
|
|
9
|
+
# Updated Date: 2025.08.15 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import os
|
|
@@ -200,7 +200,7 @@ class Body:
|
|
|
200
200
|
}
|
|
201
201
|
function scrollToBottom(live = false) {
|
|
202
202
|
const el = document.scrollingElement || document.documentElement;
|
|
203
|
-
const marginPx =
|
|
203
|
+
const marginPx = 450;
|
|
204
204
|
let behavior = 'instant';
|
|
205
205
|
if (live == true) {
|
|
206
206
|
behavior = 'instant';
|
|
@@ -211,7 +211,6 @@ class Body:
|
|
|
211
211
|
el.scrollTo({ top: el.scrollHeight, behavior });
|
|
212
212
|
}
|
|
213
213
|
prevScroll = el.scrollHeight;
|
|
214
|
-
getScrollPosition();
|
|
215
214
|
}
|
|
216
215
|
function sanitize(content) {
|
|
217
216
|
return content.replace(/&lt;/g, '<').replace(/&gt;/g, '>');
|