pygpt-net 2.6.54__py3-none-any.whl → 2.6.56__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 +11 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +26 -22
- 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 +0 -0
- 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 +0 -0
- 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 +2 -1
- 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 +0 -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 +7 -7
- pygpt_net/data/config/models.json +3 -3
- 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 +0 -0
- pygpt_net/data/locale/locale.en.ini +7 -0
- pygpt_net/data/locale/locale.es.ini +0 -0
- pygpt_net/data/locale/locale.fr.ini +0 -0
- pygpt_net/data/locale/locale.it.ini +0 -0
- pygpt_net/data/locale/locale.pl.ini +0 -0
- pygpt_net/data/locale/locale.uk.ini +0 -0
- pygpt_net/data/locale/locale.zh.ini +0 -0
- pygpt_net/data/locale/plugin.agent.de.ini +0 -0
- pygpt_net/data/locale/plugin.agent.en.ini +0 -0
- pygpt_net/data/locale/plugin.agent.es.ini +0 -0
- pygpt_net/data/locale/plugin.agent.fr.ini +0 -0
- pygpt_net/data/locale/plugin.agent.it.ini +0 -0
- pygpt_net/data/locale/plugin.agent.pl.ini +0 -0
- pygpt_net/data/locale/plugin.agent.uk.ini +0 -0
- pygpt_net/data/locale/plugin.agent.zh.ini +0 -0
- pygpt_net/data/locale/plugin.audio_input.de.ini +0 -0
- pygpt_net/data/locale/plugin.audio_input.en.ini +0 -0
- pygpt_net/data/locale/plugin.audio_input.es.ini +0 -0
- pygpt_net/data/locale/plugin.audio_input.fr.ini +0 -0
- pygpt_net/data/locale/plugin.audio_input.it.ini +0 -0
- pygpt_net/data/locale/plugin.audio_input.pl.ini +0 -0
- pygpt_net/data/locale/plugin.audio_input.uk.ini +0 -0
- pygpt_net/data/locale/plugin.audio_input.zh.ini +0 -0
- pygpt_net/data/locale/plugin.audio_output.de.ini +0 -0
- pygpt_net/data/locale/plugin.audio_output.en.ini +0 -0
- pygpt_net/data/locale/plugin.audio_output.es.ini +0 -0
- pygpt_net/data/locale/plugin.audio_output.fr.ini +0 -0
- pygpt_net/data/locale/plugin.audio_output.it.ini +0 -0
- pygpt_net/data/locale/plugin.audio_output.pl.ini +0 -0
- pygpt_net/data/locale/plugin.audio_output.uk.ini +0 -0
- pygpt_net/data/locale/plugin.audio_output.zh.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_api.de.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_api.en.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_api.es.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_api.fr.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_api.it.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_api.pl.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_api.uk.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_api.zh.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_code_interpreter.de.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_code_interpreter.en.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_code_interpreter.es.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_code_interpreter.fr.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_code_interpreter.it.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_code_interpreter.pl.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_code_interpreter.uk.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_code_interpreter.zh.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_custom.de.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_custom.en.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_custom.es.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_custom.fr.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_custom.it.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_custom.pl.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_custom.uk.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_custom.zh.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_files.de.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_files.en.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_files.es.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_files.fr.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_files.it.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_files.pl.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_files.uk.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_files.zh.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_history.de.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_history.en.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_history.es.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_history.fr.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_history.it.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_history.pl.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_history.uk.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_history.zh.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_mouse_control.de.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_mouse_control.en.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_mouse_control.es.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_mouse_control.fr.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_mouse_control.it.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_mouse_control.pl.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_mouse_control.uk.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_mouse_control.zh.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_serial.de.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_serial.en.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_serial.es.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_serial.fr.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_serial.it.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_serial.pl.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_serial.uk.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_serial.zh.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_system.de.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_system.en.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_system.es.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_system.fr.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_system.it.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_system.pl.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_system.uk.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_system.zh.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_web.de.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_web.en.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_web.es.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_web.fr.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_web.it.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_web.pl.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_web.uk.ini +0 -0
- pygpt_net/data/locale/plugin.cmd_web.zh.ini +0 -0
- pygpt_net/data/locale/plugin.crontab.de.ini +0 -0
- pygpt_net/data/locale/plugin.crontab.en.ini +0 -0
- pygpt_net/data/locale/plugin.crontab.es.ini +0 -0
- pygpt_net/data/locale/plugin.crontab.fr.ini +0 -0
- pygpt_net/data/locale/plugin.crontab.it.ini +0 -0
- pygpt_net/data/locale/plugin.crontab.pl.ini +0 -0
- pygpt_net/data/locale/plugin.crontab.uk.ini +0 -0
- pygpt_net/data/locale/plugin.crontab.zh.ini +0 -0
- pygpt_net/data/locale/plugin.experts.de.ini +0 -0
- pygpt_net/data/locale/plugin.experts.en.ini +0 -0
- pygpt_net/data/locale/plugin.experts.es.ini +0 -0
- pygpt_net/data/locale/plugin.experts.fr.ini +0 -0
- pygpt_net/data/locale/plugin.experts.it.ini +0 -0
- pygpt_net/data/locale/plugin.experts.pl.ini +0 -0
- pygpt_net/data/locale/plugin.experts.uk.ini +0 -0
- pygpt_net/data/locale/plugin.experts.zh.ini +0 -0
- pygpt_net/data/locale/plugin.extra_prompt.de.ini +0 -0
- pygpt_net/data/locale/plugin.extra_prompt.en.ini +0 -0
- pygpt_net/data/locale/plugin.extra_prompt.es.ini +0 -0
- pygpt_net/data/locale/plugin.extra_prompt.fr.ini +0 -0
- pygpt_net/data/locale/plugin.extra_prompt.it.ini +0 -0
- pygpt_net/data/locale/plugin.extra_prompt.pl.ini +0 -0
- pygpt_net/data/locale/plugin.extra_prompt.uk.ini +0 -0
- pygpt_net/data/locale/plugin.extra_prompt.zh.ini +0 -0
- pygpt_net/data/locale/plugin.idx_llama_index.de.ini +0 -0
- pygpt_net/data/locale/plugin.idx_llama_index.en.ini +0 -0
- pygpt_net/data/locale/plugin.idx_llama_index.es.ini +0 -0
- pygpt_net/data/locale/plugin.idx_llama_index.fr.ini +0 -0
- pygpt_net/data/locale/plugin.idx_llama_index.it.ini +0 -0
- pygpt_net/data/locale/plugin.idx_llama_index.pl.ini +0 -0
- pygpt_net/data/locale/plugin.idx_llama_index.uk.ini +0 -0
- pygpt_net/data/locale/plugin.idx_llama_index.zh.ini +0 -0
- pygpt_net/data/locale/plugin.mailer.en.ini +0 -0
- pygpt_net/data/locale/plugin.mcp.en.ini +0 -0
- pygpt_net/data/locale/plugin.openai_dalle.de.ini +0 -0
- pygpt_net/data/locale/plugin.openai_dalle.en.ini +0 -0
- pygpt_net/data/locale/plugin.openai_dalle.es.ini +0 -0
- pygpt_net/data/locale/plugin.openai_dalle.fr.ini +0 -0
- pygpt_net/data/locale/plugin.openai_dalle.it.ini +0 -0
- pygpt_net/data/locale/plugin.openai_dalle.pl.ini +0 -0
- pygpt_net/data/locale/plugin.openai_dalle.uk.ini +0 -0
- pygpt_net/data/locale/plugin.openai_dalle.zh.ini +0 -0
- pygpt_net/data/locale/plugin.openai_vision.de.ini +0 -0
- pygpt_net/data/locale/plugin.openai_vision.en.ini +0 -0
- pygpt_net/data/locale/plugin.openai_vision.es.ini +0 -0
- pygpt_net/data/locale/plugin.openai_vision.fr.ini +0 -0
- pygpt_net/data/locale/plugin.openai_vision.it.ini +0 -0
- pygpt_net/data/locale/plugin.openai_vision.pl.ini +0 -0
- pygpt_net/data/locale/plugin.openai_vision.uk.ini +0 -0
- pygpt_net/data/locale/plugin.openai_vision.zh.ini +0 -0
- pygpt_net/data/locale/plugin.osm.en.ini +0 -0
- pygpt_net/data/locale/plugin.real_time.de.ini +0 -0
- pygpt_net/data/locale/plugin.real_time.en.ini +0 -0
- pygpt_net/data/locale/plugin.real_time.es.ini +0 -0
- pygpt_net/data/locale/plugin.real_time.fr.ini +0 -0
- pygpt_net/data/locale/plugin.real_time.it.ini +0 -0
- pygpt_net/data/locale/plugin.real_time.pl.ini +0 -0
- pygpt_net/data/locale/plugin.real_time.uk.ini +0 -0
- pygpt_net/data/locale/plugin.real_time.zh.ini +0 -0
- pygpt_net/data/locale/plugin.voice_control.de.ini +0 -0
- pygpt_net/data/locale/plugin.voice_control.en.ini +0 -0
- pygpt_net/data/locale/plugin.voice_control.es.ini +0 -0
- pygpt_net/data/locale/plugin.voice_control.fr.ini +0 -0
- pygpt_net/data/locale/plugin.voice_control.it.ini +0 -0
- pygpt_net/data/locale/plugin.voice_control.pl.ini +0 -0
- pygpt_net/data/locale/plugin.voice_control.uk.ini +0 -0
- pygpt_net/data/locale/plugin.voice_control.zh.ini +0 -0
- pygpt_net/data/locale/plugin.wolfram.en.ini +0 -0
- 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/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 +0 -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 +18 -0
- pygpt_net/provider/core/config/patches/__init__.py +0 -0
- pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +0 -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/base.py +0 -0
- pygpt_net/provider/llms/deepseek_api.py +0 -0
- pygpt_net/provider/llms/google.py +0 -0
- pygpt_net/provider/llms/hugging_face_api.py +0 -0
- pygpt_net/provider/llms/hugging_face_embedding.py +0 -0
- pygpt_net/provider/llms/hugging_face_router.py +0 -0
- pygpt_net/provider/llms/local.py +0 -0
- pygpt_net/provider/llms/mistral.py +0 -0
- pygpt_net/provider/llms/open_router.py +0 -0
- pygpt_net/provider/llms/perplexity.py +0 -0
- pygpt_net/provider/llms/utils.py +0 -0
- pygpt_net/provider/llms/voyage.py +0 -0
- pygpt_net/provider/llms/x_ai.py +0 -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/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/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/input.py +17 -8
- pygpt_net/ui/widget/textarea/output.py +21 -1
- pygpt_net/ui/widget/textarea/web.py +29 -2
- pygpt_net/utils.py +40 -0
- {pygpt_net-2.6.54.dist-info → pygpt_net-2.6.56.dist-info}/METADATA +13 -2
- {pygpt_net-2.6.54.dist-info → pygpt_net-2.6.56.dist-info}/RECORD +86 -47
- pygpt_net/data/js/app.js +0 -5869
- {pygpt_net-2.6.54.dist-info → pygpt_net-2.6.56.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.54.dist-info → pygpt_net-2.6.56.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.54.dist-info → pygpt_net-2.6.56.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,1137 @@
|
|
|
1
|
+
// ==========================================================================
|
|
2
|
+
// Markdown runtime (markdown-it + code wrapper + math placeholders)
|
|
3
|
+
// ==========================================================================
|
|
4
|
+
|
|
5
|
+
class MarkdownRenderer {
|
|
6
|
+
|
|
7
|
+
// Markdown renderer for text, code blocks and math placeholders.
|
|
8
|
+
constructor(cfg, customMarkup, logger, asyncer, raf) {
|
|
9
|
+
// Store configuration and dependencies
|
|
10
|
+
this.cfg = cfg;
|
|
11
|
+
this.customMarkup = customMarkup;
|
|
12
|
+
this.MD = null;
|
|
13
|
+
|
|
14
|
+
// Logger instance (fallback to default Logger)
|
|
15
|
+
this.logger = logger || new Logger(cfg);
|
|
16
|
+
|
|
17
|
+
// Cooperative async utilities available in renderer for heavy decode/render paths
|
|
18
|
+
this.asyncer = asyncer || new AsyncRunner(cfg, raf);
|
|
19
|
+
this.raf = raf || null;
|
|
20
|
+
|
|
21
|
+
// Fast-path streaming renderer without linkify to reduce regex work on hot path.
|
|
22
|
+
this.MD_STREAM = null;
|
|
23
|
+
|
|
24
|
+
// Default hook callbacks used by outside runtime (can be overridden)
|
|
25
|
+
this.hooks = {
|
|
26
|
+
observeNewCode: () => {},
|
|
27
|
+
observeMsgBoxes: () => {},
|
|
28
|
+
scheduleMathRender: () => {},
|
|
29
|
+
codeScrollInit: () => {}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Registry for FULL and STREAM render (env -> id -> string)
|
|
33
|
+
this._codeByEnv = new WeakMap();
|
|
34
|
+
this._codeSeq = 0;
|
|
35
|
+
|
|
36
|
+
// Internal flag: was init() already called?
|
|
37
|
+
this._inited = false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Debug helper: write structured log lines for the stream engine.
|
|
41
|
+
_d(tag, data) {
|
|
42
|
+
try {
|
|
43
|
+
const lg = this.logger || (this.cfg && this.cfg.logger) || (window.runtime && runtime.logger) || null;
|
|
44
|
+
if (!lg || typeof lg.debug !== 'function') return;
|
|
45
|
+
lg.debug_obj("MD", tag, data);
|
|
46
|
+
} catch (_) {}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// --- Registry (placeholders) ---
|
|
50
|
+
|
|
51
|
+
// Register code content for later resolution in the given env.
|
|
52
|
+
_regCode(env, content) {
|
|
53
|
+
// Use temporary env if none was provided
|
|
54
|
+
if (!env) env = (this._tmpEnv || (this._tmpEnv = {}));
|
|
55
|
+
// Get or create per-env map
|
|
56
|
+
let m = this._codeByEnv.get(env);
|
|
57
|
+
if (!m) {
|
|
58
|
+
m = new Map();
|
|
59
|
+
this._codeByEnv.set(env, m);
|
|
60
|
+
}
|
|
61
|
+
// Generate simple increasing id and store content
|
|
62
|
+
const id = `c${++this._codeSeq}`;
|
|
63
|
+
m.set(id, content);
|
|
64
|
+
// DEBUG
|
|
65
|
+
this._d('code.reg', {
|
|
66
|
+
id,
|
|
67
|
+
len: (content || '').length
|
|
68
|
+
});
|
|
69
|
+
return id;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Resolve registered code content in the given root element for the given env.
|
|
73
|
+
_resolveCodesIn(root, env) {
|
|
74
|
+
const m = this._codeByEnv.get(env);
|
|
75
|
+
if (!m || !root) return;
|
|
76
|
+
let count = 0;
|
|
77
|
+
root.querySelectorAll('code[data-code-id]').forEach(el => {
|
|
78
|
+
const id = el.getAttribute('data-code-id');
|
|
79
|
+
const s = m.get(id);
|
|
80
|
+
if (s != null) {
|
|
81
|
+
if (!el.firstChild) el.textContent = s; // minimal allocation
|
|
82
|
+
el.removeAttribute('data-code-id');
|
|
83
|
+
m.delete(id);
|
|
84
|
+
count++;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
// DEBUG
|
|
88
|
+
if (count) this._d('code.resolve', {
|
|
89
|
+
count
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Release all registered code content for the given env.
|
|
94
|
+
_releaseEnvCodes(env) {
|
|
95
|
+
this._codeByEnv.delete(env);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Initialize markdown-it instances and plugins.
|
|
99
|
+
init() {
|
|
100
|
+
// Guard against double init
|
|
101
|
+
if (this._inited) return;
|
|
102
|
+
if (!window.markdownit) {
|
|
103
|
+
this._d('init.skip', {
|
|
104
|
+
reason: 'no-markdownit'
|
|
105
|
+
});
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
this._inited = true;
|
|
109
|
+
|
|
110
|
+
// Full renderer (used for non-hot paths, final results)
|
|
111
|
+
this.MD = window.markdownit({
|
|
112
|
+
html: false,
|
|
113
|
+
linkify: true,
|
|
114
|
+
breaks: true,
|
|
115
|
+
highlight: () => ''
|
|
116
|
+
});
|
|
117
|
+
// Streaming renderer (no linkify) – hot path
|
|
118
|
+
this.MD_STREAM = window.markdownit({
|
|
119
|
+
html: false,
|
|
120
|
+
linkify: false,
|
|
121
|
+
breaks: true,
|
|
122
|
+
highlight: () => ''
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Allow local file-like schemes in links/images (markdown-it blocks file:// by default).
|
|
126
|
+
const installLinkValidator = (md) => {
|
|
127
|
+
// Patch validateLink to allow additional safe schemes in this app
|
|
128
|
+
const orig = (md && typeof md.validateLink === 'function') ? md.validateLink.bind(md) : null;
|
|
129
|
+
md.validateLink = (url) => {
|
|
130
|
+
try {
|
|
131
|
+
const s = String(url || '').trim().toLowerCase();
|
|
132
|
+
if (s.startsWith('file:')) return true; // local files
|
|
133
|
+
if (s.startsWith('qrc:')) return true; // Qt resources
|
|
134
|
+
if (s.startsWith('bridge:')) return true; // app bridge scheme
|
|
135
|
+
if (s.startsWith('blob:')) return true; // blobs
|
|
136
|
+
if (s.startsWith('data:image/')) return true; // inline images
|
|
137
|
+
} catch (_) {}
|
|
138
|
+
return orig ? orig(url) : true;
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Install relaxed link validators on both engines
|
|
143
|
+
installLinkValidator(this.MD);
|
|
144
|
+
installLinkValidator(this.MD_STREAM);
|
|
145
|
+
|
|
146
|
+
// SAFETY: disable CommonMark "indented code blocks" unless explicitly enabled.
|
|
147
|
+
if (!this.cfg.MD || this.cfg.MD.ALLOW_INDENTED_CODE !== true) {
|
|
148
|
+
try {
|
|
149
|
+
this.MD.block.ruler.disable('code');
|
|
150
|
+
} catch (_) {}
|
|
151
|
+
try {
|
|
152
|
+
this.MD_STREAM.block.ruler.disable('code');
|
|
153
|
+
} catch (_) {}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const escapeHtml = Utils.escapeHtml;
|
|
157
|
+
|
|
158
|
+
// ------------------ Math (placeholders) ------------------
|
|
159
|
+
// Plugin that recognizes $...$ and $$...$$ and emits safe placeholders
|
|
160
|
+
const mathDollarPlaceholderPlugin = (md) => {
|
|
161
|
+
function notEscaped(src, pos) {
|
|
162
|
+
let back = 0;
|
|
163
|
+
while (pos - back - 1 >= 0 && src.charCodeAt(pos - back - 1) === 0x5C) back++;
|
|
164
|
+
return (back % 2) === 0;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function math_block_dollar(state, startLine, endLine, silent) {
|
|
168
|
+
const pos = state.bMarks[startLine] + state.tShift[startLine];
|
|
169
|
+
const max = state.eMarks[startLine];
|
|
170
|
+
if (pos + 1 >= max) return false;
|
|
171
|
+
if (state.src.charCodeAt(pos) !== 0x24 || state.src.charCodeAt(pos + 1) !== 0x24) return false;
|
|
172
|
+
let nextLine = startLine + 1,
|
|
173
|
+
found = false;
|
|
174
|
+
for (; nextLine < endLine; nextLine++) {
|
|
175
|
+
let p = state.bMarks[nextLine] + state.tShift[nextLine];
|
|
176
|
+
const pe = state.eMarks[nextLine];
|
|
177
|
+
if (p + 1 < pe && state.src.charCodeAt(p) === 0x24 && state.src.charCodeAt(p + 1) === 0x24) {
|
|
178
|
+
found = true;
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (!found) return false;
|
|
183
|
+
if (silent) return true;
|
|
184
|
+
const contentStart = state.bMarks[startLine] + state.tShift[startLine] + 2;
|
|
185
|
+
const contentEndLine = nextLine - 1;
|
|
186
|
+
let content = '';
|
|
187
|
+
if (contentEndLine >= startLine + 1) {
|
|
188
|
+
const startIdx = state.bMarks[startLine + 1];
|
|
189
|
+
const endIdx = state.eMarks[contentEndLine];
|
|
190
|
+
content = state.src.slice(startIdx, endIdx);
|
|
191
|
+
}
|
|
192
|
+
const token = state.push('math_block_dollar', '', 0);
|
|
193
|
+
token.block = true;
|
|
194
|
+
token.content = content;
|
|
195
|
+
state.line = nextLine + 1;
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function math_inline_dollar(state, silent) {
|
|
200
|
+
const pos = state.pos,
|
|
201
|
+
src = state.src,
|
|
202
|
+
max = state.posMax;
|
|
203
|
+
if (pos >= max) return false;
|
|
204
|
+
if (src.charCodeAt(pos) !== 0x24) return false;
|
|
205
|
+
if (pos + 1 < max && src.charCodeAt(pos + 1) === 0x24) return false;
|
|
206
|
+
const after = pos + 1 < max ? src.charCodeAt(pos + 1) : 0;
|
|
207
|
+
if (after === 0x20 || after === 0x0A || after === 0x0D) return false;
|
|
208
|
+
let i = pos + 1;
|
|
209
|
+
while (i < max) {
|
|
210
|
+
const ch = src.charCodeAt(i);
|
|
211
|
+
if (ch === 0x24 && notEscaped(src, i)) {
|
|
212
|
+
const before = i - 1 >= 0 ? src.charCodeAt(i - 1) : 0;
|
|
213
|
+
if (before === 0x20 || before === 0x0A || before === 0x0D) {
|
|
214
|
+
i++;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
i++;
|
|
220
|
+
}
|
|
221
|
+
if (i >= max || src.charCodeAt(i) !== 0x24) return false;
|
|
222
|
+
if (!silent) {
|
|
223
|
+
const token = state.push('math_inline_dollar', '', 0);
|
|
224
|
+
token.block = false;
|
|
225
|
+
token.content = src.slice(pos + 1, i);
|
|
226
|
+
}
|
|
227
|
+
state.pos = i + 1;
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
md.block.ruler.before('fence', 'math_block_dollar', math_block_dollar, {
|
|
231
|
+
alt: ['paragraph', 'reference', 'blockquote', 'list']
|
|
232
|
+
});
|
|
233
|
+
md.inline.ruler.before('escape', 'math_inline_dollar', math_inline_dollar);
|
|
234
|
+
md.renderer.rules.math_inline_dollar = (tokens, idx) => {
|
|
235
|
+
const tex = tokens[idx].content || '';
|
|
236
|
+
return `<span class="math-pending" data-display="0"><span class="math-fallback">$${escapeHtml(tex)}$</span><script type="math/tex">${escapeHtml(tex)}</script></span>`;
|
|
237
|
+
};
|
|
238
|
+
md.renderer.rules.math_block_dollar = (tokens, idx) => {
|
|
239
|
+
const tex = tokens[idx].content || '';
|
|
240
|
+
return `<div class="math-pending" data-display="1"><div class="math-fallback">$$${escapeHtml(tex)}$$</div><script type="math/tex; mode=display">${escapeHtml(tex)}</script></div>`;
|
|
241
|
+
};
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
// \( ... \) and \[ ... \] math delimiters (TeX style)
|
|
245
|
+
const mathBracketsPlaceholderPlugin = (md) => {
|
|
246
|
+
function math_brackets(state, silent) {
|
|
247
|
+
const src = state.src,
|
|
248
|
+
pos = state.pos,
|
|
249
|
+
max = state.posMax;
|
|
250
|
+
if (pos + 1 >= max || src.charCodeAt(pos) !== 0x5C) return false;
|
|
251
|
+
const next = src.charCodeAt(pos + 1);
|
|
252
|
+
if (next !== 0x28 && next !== 0x5B) return false;
|
|
253
|
+
const isInline = (next === 0x28);
|
|
254
|
+
const close = isInline ? '\\)' : '\\]';
|
|
255
|
+
const start = pos + 2;
|
|
256
|
+
const end = src.indexOf(close, start);
|
|
257
|
+
if (end < 0) return false;
|
|
258
|
+
const content = src.slice(start, end);
|
|
259
|
+
if (!silent) {
|
|
260
|
+
const t = state.push(isInline ? 'math_inline_bracket' : 'math_block_bracket', '', 0);
|
|
261
|
+
t.content = content;
|
|
262
|
+
t.block = !isInline;
|
|
263
|
+
}
|
|
264
|
+
state.pos = end + 2;
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
md.inline.ruler.before('escape', 'math_brackets', math_brackets);
|
|
268
|
+
md.renderer.rules.math_inline_bracket = (tokens, idx) => {
|
|
269
|
+
const tex = tokens[idx].content || '';
|
|
270
|
+
return `<span class="math-pending" data-display="0"><span class="math-fallback">\\(${escapeHtml(tex)}\\)</span><script type="math/tex">${escapeHtml(tex)}</script></span>`;
|
|
271
|
+
};
|
|
272
|
+
md.renderer.rules.math_block_bracket = (tokens, idx) => {
|
|
273
|
+
const tex = tokens[idx].content || '';
|
|
274
|
+
return `<div class="math-pending" data-display="1"><div class="math-fallback">\\${'['}${escapeHtml(tex)}\\${']'}</div><script type="math/tex; mode=display">${escapeHtml(tex)}</script></div>`;
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
// Enable math placeholders for both renderers
|
|
279
|
+
this.MD.use(mathDollarPlaceholderPlugin);
|
|
280
|
+
this.MD.use(mathBracketsPlaceholderPlugin);
|
|
281
|
+
this.MD_STREAM.use(mathDollarPlaceholderPlugin);
|
|
282
|
+
this.MD_STREAM.use(mathBracketsPlaceholderPlugin);
|
|
283
|
+
|
|
284
|
+
const cfg = this.cfg;
|
|
285
|
+
const logger = this.logger;
|
|
286
|
+
|
|
287
|
+
// ------------------ STREAMING wrapper plugin (hot path; inline) ------------------
|
|
288
|
+
(function codeWrapperPlugin(md, logger, renderer) {
|
|
289
|
+
let CODE_IDX = 1;
|
|
290
|
+
const ALIAS = {
|
|
291
|
+
txt: 'plaintext',
|
|
292
|
+
text: 'plaintext',
|
|
293
|
+
plaintext: 'plaintext',
|
|
294
|
+
sh: 'bash',
|
|
295
|
+
shell: 'bash',
|
|
296
|
+
zsh: 'bash',
|
|
297
|
+
'shell-session': 'bash',
|
|
298
|
+
py: 'python',
|
|
299
|
+
python3: 'python',
|
|
300
|
+
py3: 'python',
|
|
301
|
+
js: 'javascript',
|
|
302
|
+
node: 'javascript',
|
|
303
|
+
nodejs: 'javascript',
|
|
304
|
+
ts: 'typescript',
|
|
305
|
+
'ts-node': 'typescript',
|
|
306
|
+
yml: 'yaml',
|
|
307
|
+
kt: 'kotlin',
|
|
308
|
+
rs: 'rust',
|
|
309
|
+
csharp: 'csharp',
|
|
310
|
+
'c#': 'csharp',
|
|
311
|
+
'c++': 'cpp',
|
|
312
|
+
ps: 'powershell',
|
|
313
|
+
ps1: 'powershell',
|
|
314
|
+
pwsh: 'powershell',
|
|
315
|
+
powershell7: 'powershell',
|
|
316
|
+
docker: 'dockerfile'
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
function normLang(s) {
|
|
320
|
+
if (!s) return '';
|
|
321
|
+
const v = String(s).trim().toLowerCase();
|
|
322
|
+
return ALIAS[v] || v;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function isSupportedByHLJS(lang) {
|
|
326
|
+
try {
|
|
327
|
+
return !!(window.hljs && hljs.getLanguage && hljs.getLanguage(lang));
|
|
328
|
+
} catch (_) {
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function classForHighlight(lang) {
|
|
334
|
+
if (!lang) return 'plaintext';
|
|
335
|
+
return isSupportedByHLJS(lang) ? lang : 'plaintext';
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function stripBOM(s) {
|
|
339
|
+
return (s && s.charCodeAt(0) === 0xFEFF) ? s.slice(1) : s;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function normForFP(s) {
|
|
343
|
+
if (!s) return '';
|
|
344
|
+
let t = String(s);
|
|
345
|
+
if (t.charCodeAt(0) === 0xFEFF) t = t.slice(1);
|
|
346
|
+
t = t.replace(/\r\n?/g, '\n');
|
|
347
|
+
if (t.endsWith('\n')) t = t.slice(0, -1);
|
|
348
|
+
return t;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function hash32FNV(str) {
|
|
352
|
+
let h = 0x811c9dc5 >>> 0;
|
|
353
|
+
for (let i = 0; i < str.length; i++) {
|
|
354
|
+
h ^= str.charCodeAt(i);
|
|
355
|
+
h = (h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24))) >>> 0;
|
|
356
|
+
}
|
|
357
|
+
return ('00000000' + h.toString(16)).slice(-8);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function makeStableFP(langToken, rawContent) {
|
|
361
|
+
const norm = normForFP(rawContent || '');
|
|
362
|
+
return `${langToken || 'plaintext'}|${norm.length}|${hash32FNV(norm)}`;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function detectFromFirstLine(raw, rid) {
|
|
366
|
+
if (!raw) return {
|
|
367
|
+
lang: '',
|
|
368
|
+
content: raw,
|
|
369
|
+
isOutput: false
|
|
370
|
+
};
|
|
371
|
+
const lines = raw.split(/\r?\n/);
|
|
372
|
+
if (!lines.length) return {
|
|
373
|
+
lang: '',
|
|
374
|
+
content: raw,
|
|
375
|
+
isOutput: false
|
|
376
|
+
};
|
|
377
|
+
let i = 0;
|
|
378
|
+
while (i < lines.length && !lines[i].trim()) i++;
|
|
379
|
+
if (i >= lines.length) return {
|
|
380
|
+
lang: '',
|
|
381
|
+
content: raw,
|
|
382
|
+
isOutput: false
|
|
383
|
+
};
|
|
384
|
+
let first = stripBOM(lines[i]).trim();
|
|
385
|
+
first = first.replace(/^\s*lang(?:uage)?\s*[:=]\s*/i, '').trim();
|
|
386
|
+
let token = first.split(/\s+/)[0].replace(/:$/, '');
|
|
387
|
+
if (!/^[A-Za-z][\w#+\-\.]{0,30}$/.test(token)) return {
|
|
388
|
+
lang: '',
|
|
389
|
+
content: raw,
|
|
390
|
+
isOutput: false
|
|
391
|
+
};
|
|
392
|
+
let cand = normLang(token);
|
|
393
|
+
if (cand === 'output') {
|
|
394
|
+
const content = lines.slice(i + 1).join('\n');
|
|
395
|
+
return {
|
|
396
|
+
lang: 'python',
|
|
397
|
+
headerLabel: 'output',
|
|
398
|
+
content,
|
|
399
|
+
isOutput: true
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
const rest = lines.slice(i + 1).join('\n');
|
|
403
|
+
if (!rest.trim()) return {
|
|
404
|
+
lang: '',
|
|
405
|
+
content: raw,
|
|
406
|
+
isOutput: false
|
|
407
|
+
};
|
|
408
|
+
return {
|
|
409
|
+
lang: cand,
|
|
410
|
+
headerLabel: cand,
|
|
411
|
+
content: rest,
|
|
412
|
+
isOutput: false
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function resolveLanguageAndContent(info, raw, rid) {
|
|
417
|
+
const infoLangRaw = (info || '').trim().split(/\s+/)[0] || '';
|
|
418
|
+
let cand = normLang(infoLangRaw);
|
|
419
|
+
|
|
420
|
+
// Treat too-short/unsupported tokens as unreliable; ignore and fall back.
|
|
421
|
+
const shortOrUnsupported = !cand || cand.length < 3 || !isSupportedByHLJS(cand);
|
|
422
|
+
|
|
423
|
+
if (cand === 'output') {
|
|
424
|
+
return {
|
|
425
|
+
lang: 'python',
|
|
426
|
+
headerLabel: 'output',
|
|
427
|
+
content: raw,
|
|
428
|
+
isOutput: true
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
if (!shortOrUnsupported) {
|
|
432
|
+
return {
|
|
433
|
+
lang: cand,
|
|
434
|
+
headerLabel: cand,
|
|
435
|
+
content: raw,
|
|
436
|
+
isOutput: false
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Fallback: try to detect from first code line (directive like "python" etc.)
|
|
441
|
+
const det = detectFromFirstLine(raw, rid);
|
|
442
|
+
if (det && (det.lang || det.isOutput)) return det;
|
|
443
|
+
|
|
444
|
+
// Last resort
|
|
445
|
+
return {
|
|
446
|
+
lang: '',
|
|
447
|
+
headerLabel: 'code',
|
|
448
|
+
content: raw,
|
|
449
|
+
isOutput: false
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
md.renderer.rules.fence = (tokens, idx, options, env) => renderFence(tokens[idx], env);
|
|
454
|
+
md.renderer.rules.code_block = (tokens, idx, options, env) => renderFence({
|
|
455
|
+
info: '',
|
|
456
|
+
content: tokens[idx].content || ''
|
|
457
|
+
}, env);
|
|
458
|
+
|
|
459
|
+
function renderFence(token, env) {
|
|
460
|
+
const rendererRef = renderer || (window.runtime && window.runtime.renderer) || null;
|
|
461
|
+
const raw = token.content || '';
|
|
462
|
+
const rid = String(CODE_IDX + '');
|
|
463
|
+
|
|
464
|
+
const res = resolveLanguageAndContent(token.info || '', raw, rid);
|
|
465
|
+
const isOutput = !!res.isOutput;
|
|
466
|
+
const rawToken = (res.lang || '').trim();
|
|
467
|
+
const langClass = isOutput ? 'python' : classForHighlight(rawToken);
|
|
468
|
+
|
|
469
|
+
let headerLabel = isOutput ? 'output' : (res.headerLabel || (rawToken || 'code'));
|
|
470
|
+
if (!isOutput) {
|
|
471
|
+
if (rawToken && !isSupportedByHLJS(rawToken) && rawToken.length < 3) headerLabel = 'code';
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const content = res.content || '';
|
|
475
|
+
const len = content.length;
|
|
476
|
+
const head = content.slice(0, 64);
|
|
477
|
+
const tail = content.slice(-64);
|
|
478
|
+
const headEsc = Utils.escapeHtml(head);
|
|
479
|
+
const tailEsc = Utils.escapeHtml(tail);
|
|
480
|
+
const nl = Utils.countNewlines(content);
|
|
481
|
+
const fpStable = makeStableFP(langClass, content);
|
|
482
|
+
|
|
483
|
+
const idxLocal = CODE_IDX++;
|
|
484
|
+
|
|
485
|
+
let actions = '';
|
|
486
|
+
if (langClass === 'html') {
|
|
487
|
+
actions += `<a href="empty:${idxLocal}" class="code-header-action code-header-preview"><img src="${cfg.ICONS.CODE_PREVIEW}" class="action-img" data-id="${idxLocal}"><span>${Utils.escapeHtml(cfg.LOCALE.PREVIEW)}</span></a>`;
|
|
488
|
+
} else if (langClass === 'python' && headerLabel !== 'output') {
|
|
489
|
+
actions += `<a href="empty:${idxLocal}" class="code-header-action code-header-run"><img src="${cfg.ICONS.CODE_RUN}" class="action-img" data-id="${idxLocal}"><span>${Utils.escapeHtml(cfg.LOCALE.RUN)}</span></a>`;
|
|
490
|
+
}
|
|
491
|
+
actions += `<a href="empty:${idxLocal}" class="code-header-action code-header-collapse" title="${Utils.escapeHtml(cfg.LOCALE.COLLAPSE)}"><img src="${cfg.ICONS.CODE_MENU}" class="action-img" data-id="${idxLocal}"></a>`;
|
|
492
|
+
actions += `<a href="empty:${idxLocal}" class="code-header-action code-header-copy" title="${Utils.escapeHtml(cfg.LOCALE.COPY)}"><img src="${cfg.ICONS.CODE_COPY}" class="action-img" data-id="${idxLocal}"></a>`;
|
|
493
|
+
|
|
494
|
+
const canUseRegistry = !!(rendererRef && typeof rendererRef._regCode === 'function' && env);
|
|
495
|
+
if (canUseRegistry) {
|
|
496
|
+
const codeId = rendererRef._regCode(env, content);
|
|
497
|
+
// DEBUG
|
|
498
|
+
rendererRef._d && rendererRef._d('fence.stream', {
|
|
499
|
+
idxLocal,
|
|
500
|
+
lang: langClass,
|
|
501
|
+
headerLabel,
|
|
502
|
+
len
|
|
503
|
+
});
|
|
504
|
+
return (
|
|
505
|
+
`<div class="code-wrapper highlight" data-index="${idxLocal}"` +
|
|
506
|
+
` data-code-lang="${Utils.escapeHtml(res.lang || '')}"` +
|
|
507
|
+
` data-code-len="${String(len)}" data-code-head="${headEsc}" data-code-tail="${tailEsc}" data-code-nl="${String(nl)}"` +
|
|
508
|
+
` data-fp="${Utils.escapeHtml(fpStable)}"` +
|
|
509
|
+
` data-locale-collapse="${Utils.escapeHtml(cfg.LOCALE.COLLAPSE)}" data-locale-expand="${Utils.escapeHtml(cfg.LOCALE.EXPAND)}"` +
|
|
510
|
+
` data-locale-copy="${Utils.escapeHtml(cfg.LOCALE.COPY)}" data-locale-copied="${Utils.escapeHtml(cfg.LOCALE.COPIED)}" data-style="${Utils.escapeHtml(cfg.CODE_STYLE)}">` +
|
|
511
|
+
`<p class="code-header-wrapper"><span><span class="code-header-lang">${Utils.escapeHtml(headerLabel)} </span>${actions}</span></p>` +
|
|
512
|
+
`<pre><code class="language-${Utils.escapeHtml(langClass)} hljs" data-code-id="${String(codeId)}"></code></pre>` +
|
|
513
|
+
`</div>`
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Fallback
|
|
518
|
+
rendererRef && rendererRef._d && rendererRef._d('fence.stream.fallback', {
|
|
519
|
+
idxLocal,
|
|
520
|
+
lang: langClass,
|
|
521
|
+
len
|
|
522
|
+
});
|
|
523
|
+
return (
|
|
524
|
+
`<div class="code-wrapper highlight" data-index="${idxLocal}"` +
|
|
525
|
+
` data-code-lang="${Utils.escapeHtml(res.lang || '')}"` +
|
|
526
|
+
` data-code-len="${String(len)}" data-code-head="${headEsc}" data-code-tail="${tailEsc}" data-code-nl="${String(nl)}"` +
|
|
527
|
+
` data-fp="${Utils.escapeHtml(fpStable)}"` +
|
|
528
|
+
` data-locale-collapse="${Utils.escapeHtml(cfg.LOCALE.COLLAPSE)}" data-locale-expand="${Utils.escapeHtml(cfg.LOCALE.EXPAND)}"` +
|
|
529
|
+
` data-locale-copy="${Utils.escapeHtml(cfg.LOCALE.COPY)}" data-locale-copied="${Utils.escapeHtml(cfg.LOCALE.COPIED)}" data-style="${Utils.escapeHtml(cfg.CODE_STYLE)}">` +
|
|
530
|
+
`<p class="code-header-wrapper"><span><span class="code-header-lang">${Utils.escapeHtml(headerLabel)} </span>${actions}</span></p>` +
|
|
531
|
+
`<pre><code class="language-${Utils.escapeHtml(langClass)} hljs">${Utils.escapeHtml(content)}</code></pre>` +
|
|
532
|
+
`</div>`
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
})(this.MD_STREAM, this.logger, this);
|
|
536
|
+
|
|
537
|
+
// ---------------- FULL renderer wrapper plugin ----------------
|
|
538
|
+
(function codeWrapperPlugin(md, logger, renderer) {
|
|
539
|
+
let CODE_IDX = 1;
|
|
540
|
+
const ALIAS = {
|
|
541
|
+
txt: 'plaintext',
|
|
542
|
+
text: 'plaintext',
|
|
543
|
+
plaintext: 'plaintext',
|
|
544
|
+
sh: 'bash',
|
|
545
|
+
shell: 'bash',
|
|
546
|
+
zsh: 'bash',
|
|
547
|
+
'shell-session': 'bash',
|
|
548
|
+
py: 'python',
|
|
549
|
+
python3: 'python',
|
|
550
|
+
py3: 'python',
|
|
551
|
+
js: 'javascript',
|
|
552
|
+
node: 'javascript',
|
|
553
|
+
nodejs: 'javascript',
|
|
554
|
+
ts: 'typescript',
|
|
555
|
+
'ts-node': 'typescript',
|
|
556
|
+
yml: 'yaml',
|
|
557
|
+
kt: 'kotlin',
|
|
558
|
+
rs: 'rust',
|
|
559
|
+
csharp: 'csharp',
|
|
560
|
+
'c#': 'csharp',
|
|
561
|
+
'c++': 'cpp',
|
|
562
|
+
ps: 'powershell',
|
|
563
|
+
ps1: 'powershell',
|
|
564
|
+
pwsh: 'powershell',
|
|
565
|
+
powershell7: 'powershell',
|
|
566
|
+
docker: 'dockerfile'
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
function normLang(s) {
|
|
570
|
+
if (!s) return '';
|
|
571
|
+
const v = String(s).trim().toLowerCase();
|
|
572
|
+
return ALIAS[v] || v;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
function isSupportedByHLJS(lang) {
|
|
576
|
+
try {
|
|
577
|
+
return !!(window.hljs && hljs.getLanguage && hljs.getLanguage(lang));
|
|
578
|
+
} catch (_) {
|
|
579
|
+
return false;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function classForHighlight(lang) {
|
|
584
|
+
if (!lang) return 'plaintext';
|
|
585
|
+
return isSupportedByHLJS(lang) ? lang : 'plaintext';
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function stripBOM(s) {
|
|
589
|
+
return (s && s.charCodeAt(0) === 0xFEFF) ? s.slice(1) : s;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function normForFP(s) {
|
|
593
|
+
if (!s) return '';
|
|
594
|
+
let t = String(s);
|
|
595
|
+
if (t.charCodeAt(0) === 0xFEFF) t = t.slice(1);
|
|
596
|
+
t = t.replace(/\r\n?/g, '\n');
|
|
597
|
+
if (t.endsWith('\n')) t = t.slice(0, -1);
|
|
598
|
+
return t;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
function hash32FNV(str) {
|
|
602
|
+
let h = 0x811c9dc5 >>> 0;
|
|
603
|
+
for (let i = 0; i < str.length; i++) {
|
|
604
|
+
h ^= str.charCodeAt(i);
|
|
605
|
+
h = (h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24))) >>> 0;
|
|
606
|
+
}
|
|
607
|
+
return ('00000000' + h.toString(16)).slice(-8);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function makeStableFP(langToken, rawContent) {
|
|
611
|
+
const norm = normForFP(rawContent || '');
|
|
612
|
+
return `${langToken || 'plaintext'}|${norm.length}|${hash32FNV(norm)}`;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function detectFromFirstLine(raw, rid) {
|
|
616
|
+
if (!raw) return {
|
|
617
|
+
lang: '',
|
|
618
|
+
content: raw,
|
|
619
|
+
isOutput: false
|
|
620
|
+
};
|
|
621
|
+
const lines = raw.split(/\r?\n/);
|
|
622
|
+
if (!lines.length) return {
|
|
623
|
+
lang: '',
|
|
624
|
+
content: raw,
|
|
625
|
+
isOutput: false
|
|
626
|
+
};
|
|
627
|
+
let i = 0;
|
|
628
|
+
while (i < lines.length && !lines[i].trim()) i++;
|
|
629
|
+
if (i >= lines.length) return {
|
|
630
|
+
lang: '',
|
|
631
|
+
content: raw,
|
|
632
|
+
isOutput: false
|
|
633
|
+
};
|
|
634
|
+
let first = stripBOM(lines[i]).trim();
|
|
635
|
+
first = first.replace(/^\s*lang(?:uage)?\s*[:=]\s*/i, '').trim();
|
|
636
|
+
let token = first.split(/\s+/)[0].replace(/:$/, '');
|
|
637
|
+
if (!/^[A-Za-z][\w#+\-\.]{0,30}$/.test(token)) return {
|
|
638
|
+
lang: '',
|
|
639
|
+
content: raw,
|
|
640
|
+
isOutput: false
|
|
641
|
+
};
|
|
642
|
+
let cand = normLang(token);
|
|
643
|
+
if (cand === 'output') {
|
|
644
|
+
const content = lines.slice(i + 1).join('\n');
|
|
645
|
+
return {
|
|
646
|
+
lang: 'python',
|
|
647
|
+
headerLabel: 'output',
|
|
648
|
+
content,
|
|
649
|
+
isOutput: true
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
const rest = lines.slice(i + 1).join('\n');
|
|
653
|
+
if (!rest.trim()) return {
|
|
654
|
+
lang: '',
|
|
655
|
+
content: raw,
|
|
656
|
+
isOutput: false
|
|
657
|
+
};
|
|
658
|
+
return {
|
|
659
|
+
lang: cand,
|
|
660
|
+
headerLabel: cand,
|
|
661
|
+
content: rest,
|
|
662
|
+
isOutput: false
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function resolveLanguageAndContent(info, raw, rid) {
|
|
667
|
+
const infoLangRaw = (info || '').trim().split(/\s+/)[0] || '';
|
|
668
|
+
let cand = normLang(infoLangRaw);
|
|
669
|
+
|
|
670
|
+
// Treat too-short/unsupported tokens as unreliable; ignore and fall back.
|
|
671
|
+
const shortOrUnsupported = !cand || cand.length < 3 || !isSupportedByHLJS(cand);
|
|
672
|
+
|
|
673
|
+
if (cand === 'output') {
|
|
674
|
+
return {
|
|
675
|
+
lang: 'python',
|
|
676
|
+
headerLabel: 'output',
|
|
677
|
+
content: raw,
|
|
678
|
+
isOutput: true
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
if (!shortOrUnsupported) {
|
|
682
|
+
return {
|
|
683
|
+
lang: cand,
|
|
684
|
+
headerLabel: cand,
|
|
685
|
+
content: raw,
|
|
686
|
+
isOutput: false
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// Fallback: try to detect from first code line (directive like "python" etc.)
|
|
691
|
+
const det = detectFromFirstLine(raw, rid);
|
|
692
|
+
if (det && (det.lang || det.isOutput)) return det;
|
|
693
|
+
|
|
694
|
+
// Last resort
|
|
695
|
+
return {
|
|
696
|
+
lang: '',
|
|
697
|
+
headerLabel: 'code',
|
|
698
|
+
content: raw,
|
|
699
|
+
isOutput: false
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
md.renderer.rules.fence = (tokens, idx, options, env, slf) => renderFence(tokens[idx], env);
|
|
704
|
+
md.renderer.rules.code_block = (tokens, idx, options, env, slf) => renderFence({
|
|
705
|
+
info: '',
|
|
706
|
+
content: tokens[idx].content || ''
|
|
707
|
+
}, env);
|
|
708
|
+
|
|
709
|
+
function renderFence(token, env) {
|
|
710
|
+
const rendererRef = renderer || (window.runtime && window.runtime.renderer) || null;
|
|
711
|
+
const raw = token.content || '';
|
|
712
|
+
const rid = String(CODE_IDX + '');
|
|
713
|
+
const res = resolveLanguageAndContent(token.info || '', raw, rid);
|
|
714
|
+
const isOutput = !!res.isOutput;
|
|
715
|
+
const rawToken = (res.lang || '').trim();
|
|
716
|
+
const langClass = isOutput ? 'python' : classForHighlight(rawToken);
|
|
717
|
+
let headerLabel = isOutput ? 'output' : (res.headerLabel || (rawToken || 'code'));
|
|
718
|
+
if (!isOutput) {
|
|
719
|
+
if (rawToken && !isSupportedByHLJS(rawToken) && rawToken.length < 3) headerLabel = 'code';
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
const content = res.content || '';
|
|
723
|
+
const len = content.length;
|
|
724
|
+
const head = content.slice(0, 64);
|
|
725
|
+
const tail = content.slice(-64);
|
|
726
|
+
const headEsc = Utils.escapeHtml(head);
|
|
727
|
+
const tailEsc = Utils.escapeHtml(tail);
|
|
728
|
+
const nl = Utils.countNewlines(content);
|
|
729
|
+
const fpStable = makeStableFP(langClass, content);
|
|
730
|
+
|
|
731
|
+
const idxLocal = CODE_IDX++;
|
|
732
|
+
|
|
733
|
+
let actions = '';
|
|
734
|
+
if (langClass === 'html') {
|
|
735
|
+
actions += `<a href="empty:${idxLocal}" class="code-header-action code-header-preview"><img src="${cfg.ICONS.CODE_PREVIEW}" class="action-img" data-id="${idxLocal}"><span>${Utils.escapeHtml(cfg.LOCALE.PREVIEW)}</span></a>`;
|
|
736
|
+
} else if (langClass === 'python' && headerLabel !== 'output') {
|
|
737
|
+
actions += `<a href="empty:${idxLocal}" class="code-header-action code-header-run"><img src="${cfg.ICONS.CODE_RUN}" class="action-img" data-id="${idxLocal}"><span>${Utils.escapeHtml(cfg.LOCALE.RUN)}</span></a>`;
|
|
738
|
+
}
|
|
739
|
+
// icon-only (no label) + title
|
|
740
|
+
actions += `<a href="empty:${idxLocal}" class="code-header-action code-header-collapse" title="${Utils.escapeHtml(cfg.LOCALE.COLLAPSE)}"><img src="${cfg.ICONS.CODE_MENU}" class="action-img" data-id="${idxLocal}"></a>`;
|
|
741
|
+
actions += `<a href="empty:${idxLocal}" class="code-header-action code-header-copy" title="${Utils.escapeHtml(cfg.LOCALE.COPY)}"><img src="${cfg.ICONS.CODE_COPY}" class="action-img" data-id="${idxLocal}"></a>`;
|
|
742
|
+
|
|
743
|
+
const canUseRegistry = !!(rendererRef && typeof rendererRef._regCode === 'function' && env);
|
|
744
|
+
if (canUseRegistry) {
|
|
745
|
+
const codeId = rendererRef._regCode(env, content);
|
|
746
|
+
rendererRef._d && rendererRef._d('fence.full', {
|
|
747
|
+
idxLocal,
|
|
748
|
+
lang: langClass,
|
|
749
|
+
headerLabel,
|
|
750
|
+
len
|
|
751
|
+
});
|
|
752
|
+
return (
|
|
753
|
+
`<div class="code-wrapper highlight" data-index="${idxLocal}"` +
|
|
754
|
+
` data-code-lang="${Utils.escapeHtml(res.lang || '')}"` +
|
|
755
|
+
` data-code-len="${String(len)}" data-code-head="${headEsc}" data-code-tail="${tailEsc}" data-code-nl="${String(nl)}" data-fp="${Utils.escapeHtml(fpStable)}"` +
|
|
756
|
+
` data-locale-collapse="${Utils.escapeHtml(cfg.LOCALE.COLLAPSE)}" data-locale-expand="${Utils.escapeHtml(cfg.LOCALE.EXPAND)}"` +
|
|
757
|
+
` data-locale-copy="${Utils.escapeHtml(cfg.LOCALE.COPY)}" data-locale-copied="${Utils.escapeHtml(cfg.LOCALE.COPIED)}" data-style="${Utils.escapeHtml(cfg.CODE_STYLE)}">` +
|
|
758
|
+
`<p class="code-header-wrapper"><span><span class="code-header-lang">${Utils.escapeHtml(headerLabel)} </span>${actions}</span></p>` +
|
|
759
|
+
`<pre><code class="language-${Utils.escapeHtml(langClass)} hljs" data-code-id="${String(codeId)}"></code></pre>` +
|
|
760
|
+
`</div>`
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
rendererRef && rendererRef._d && rendererRef._d('fence.full.fallback', {
|
|
765
|
+
idxLocal,
|
|
766
|
+
lang: langClass,
|
|
767
|
+
len
|
|
768
|
+
});
|
|
769
|
+
const inner = Utils.escapeHtml(content);
|
|
770
|
+
return (
|
|
771
|
+
`<div class="code-wrapper highlight" data-index="${idxLocal}"` +
|
|
772
|
+
` data-code-lang="${Utils.escapeHtml(res.lang || '')}"` +
|
|
773
|
+
` data-code-len="${String(len)}" data-code-head="${headEsc}" data-code-tail="${tailEsc}" data-code-nl="${String(nl)}" data-fp="${Utils.escapeHtml(fpStable)}"` +
|
|
774
|
+
` data-locale-collapse="${Utils.escapeHtml(cfg.LOCALE.COLLAPSE)}" data-locale-expand="${Utils.escapeHtml(cfg.LOCALE.EXPAND)}"` +
|
|
775
|
+
` data-locale-copy="${Utils.escapeHtml(cfg.LOCALE.COPY)}" data-locale-copied="${Utils.escapeHtml(cfg.LOCALE.COPIED)}" data-style="${Utils.escapeHtml(cfg.CODE_STYLE)}">` +
|
|
776
|
+
`<p class="code-header-wrapper"><span><span class="code-header-lang">${Utils.escapeHtml(headerLabel)} </span>${actions}</span></p>` +
|
|
777
|
+
`<pre><code class="language-${Utils.escapeHtml(langClass)} hljs">${inner}</code></pre>` +
|
|
778
|
+
`</div>`
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
})(this.MD, this.logger, this);
|
|
782
|
+
|
|
783
|
+
this._d('init.done', {});
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// Replace "sandbox:" links with file:// in markdown source (host policy).
|
|
787
|
+
preprocessMD(s) {
|
|
788
|
+
const out = (s || '').replace(/\]\(sandbox:/g, '](file://');
|
|
789
|
+
if (out !== s) this._d('md.preprocess', {
|
|
790
|
+
replaced: true
|
|
791
|
+
});
|
|
792
|
+
return out;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// Decode base64 UTF-8 to string (shared TextDecoder).
|
|
796
|
+
b64ToUtf8(b64) {
|
|
797
|
+
const bin = atob(b64);
|
|
798
|
+
const bytes = new Uint8Array(bin.length);
|
|
799
|
+
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
|
|
800
|
+
return Utils.utf8Decode(bytes);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
// Apply custom markup for bot messages only (method name kept for API).
|
|
804
|
+
applyCustomMarkupForBots(root) {
|
|
805
|
+
const MD = this.MD;
|
|
806
|
+
try {
|
|
807
|
+
const scope = root || document;
|
|
808
|
+
const targets = [];
|
|
809
|
+
|
|
810
|
+
if (scope && scope.nodeType === 1 && scope.classList && scope.classList.contains('msg-box') &&
|
|
811
|
+
scope.classList.contains('msg-bot')) {
|
|
812
|
+
targets.push(scope);
|
|
813
|
+
}
|
|
814
|
+
if (scope && typeof scope.querySelectorAll === 'function') {
|
|
815
|
+
const list = scope.querySelectorAll('.msg-box.msg-bot');
|
|
816
|
+
for (let i = 0; i < list.length; i++) targets.push(list[i]);
|
|
817
|
+
}
|
|
818
|
+
if (scope && scope.nodeType === 1 && typeof scope.closest === 'function') {
|
|
819
|
+
const closestMsg = scope.closest('.msg-box.msg-bot');
|
|
820
|
+
if (closestMsg) targets.push(closestMsg);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
const seen = new Set();
|
|
824
|
+
for (const el of targets) {
|
|
825
|
+
if (!el || !el.isConnected || seen.has(el)) continue;
|
|
826
|
+
seen.add(el);
|
|
827
|
+
this.customMarkup.apply(el, MD);
|
|
828
|
+
}
|
|
829
|
+
this._d('cm.applyBots', {
|
|
830
|
+
count: seen.size
|
|
831
|
+
});
|
|
832
|
+
} catch (_) {}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// Helper: choose renderer (hot vs full) for snapshot use.
|
|
836
|
+
_md(streamingHint) {
|
|
837
|
+
return streamingHint ? (this.MD_STREAM || this.MD) : (this.MD || this.MD_STREAM);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// Async, batched processing of [data-md64] / [md-block-markdown] to keep UI responsive on heavy loads.
|
|
841
|
+
async renderPendingMarkdown(root) {
|
|
842
|
+
const MD = this.MD;
|
|
843
|
+
if (!MD) return;
|
|
844
|
+
|
|
845
|
+
const scope = root || document;
|
|
846
|
+
const nodes = Array.from(scope.querySelectorAll('[data-md64], [md-block-markdown]'));
|
|
847
|
+
if (nodes.length === 0) {
|
|
848
|
+
try {
|
|
849
|
+
const hasBots = !!(scope && scope.querySelector && scope.querySelector('.msg-box.msg-bot'));
|
|
850
|
+
const hasWrappers = !!(scope && scope.querySelector && scope.querySelector('.code-wrapper'));
|
|
851
|
+
const hasCodes = !!(scope && scope.querySelector && scope.querySelector('.msg-box.msg-bot pre code'));
|
|
852
|
+
const hasUnhighlighted = !!(scope && scope.querySelector && scope.querySelector('.msg-box.msg-bot pre code:not([data-highlighted="yes"])'));
|
|
853
|
+
const hasMath = !!(scope && scope.querySelector && scope.querySelector('script[type^="math/tex"]'));
|
|
854
|
+
|
|
855
|
+
if (hasBots) this.applyCustomMarkupForBots(scope);
|
|
856
|
+
if (hasWrappers) this.restoreCollapsedCode(scope);
|
|
857
|
+
this.hooks.codeScrollInit(scope);
|
|
858
|
+
|
|
859
|
+
if (hasCodes) {
|
|
860
|
+
this.hooks.observeMsgBoxes(scope);
|
|
861
|
+
this.hooks.observeNewCode(scope, {
|
|
862
|
+
deferLastIfStreaming: true,
|
|
863
|
+
minLinesForLast: this.cfg.PROFILE_CODE.minLinesForHL,
|
|
864
|
+
minCharsForLast: this.cfg.PROFILE_CODE.minCharsForHL
|
|
865
|
+
});
|
|
866
|
+
if (hasUnhighlighted && typeof runtime !== 'undefined' && runtime.highlighter) {
|
|
867
|
+
runtime.highlighter.scanVisibleCodesInRoot(scope, runtime.stream.activeCode || null);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
if (hasMath) this.hooks.scheduleMathRender(scope);
|
|
872
|
+
} catch (_) {}
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
this._d('md.pending.start', {
|
|
877
|
+
nodes: nodes.length
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
const touchedBoxes = new Set();
|
|
881
|
+
const perSlice = (this.cfg.ASYNC && this.cfg.ASYNC.MD_NODES_PER_SLICE) || 12;
|
|
882
|
+
let sliceCount = 0;
|
|
883
|
+
let startedAt = Utils.now();
|
|
884
|
+
|
|
885
|
+
for (let j = 0; j < nodes.length; j++) {
|
|
886
|
+
const el = nodes[j];
|
|
887
|
+
if (!el || !el.isConnected) continue;
|
|
888
|
+
|
|
889
|
+
let md = '';
|
|
890
|
+
const isNative = el.hasAttribute('md-block-markdown');
|
|
891
|
+
const msgBox = (el.closest && el.closest('.msg-box.msg-bot, .msg-box.msg-user')) || null;
|
|
892
|
+
const isUserMsg = !!(msgBox && msgBox.classList.contains('msg-user'));
|
|
893
|
+
const isBotMsg = !!(msgBox && msgBox.classList.contains('msg-bot'));
|
|
894
|
+
|
|
895
|
+
if (isNative) {
|
|
896
|
+
try {
|
|
897
|
+
md = isUserMsg ? (el.textContent || '') : this.preprocessMD(el.textContent || '');
|
|
898
|
+
} catch (_) {
|
|
899
|
+
md = '';
|
|
900
|
+
}
|
|
901
|
+
try {
|
|
902
|
+
el.removeAttribute('md-block-markdown');
|
|
903
|
+
} catch (_) {}
|
|
904
|
+
} else {
|
|
905
|
+
const b64 = el.getAttribute('data-md64');
|
|
906
|
+
if (!b64) continue;
|
|
907
|
+
try {
|
|
908
|
+
md = this.b64ToUtf8(b64);
|
|
909
|
+
} catch (_) {
|
|
910
|
+
md = '';
|
|
911
|
+
}
|
|
912
|
+
el.removeAttribute('data-md64');
|
|
913
|
+
if (!isUserMsg) {
|
|
914
|
+
try {
|
|
915
|
+
md = this.preprocessMD(md);
|
|
916
|
+
} catch (_) {}
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
if (isUserMsg) {
|
|
921
|
+
const span = document.createElement('span');
|
|
922
|
+
span.textContent = md;
|
|
923
|
+
el.replaceWith(span);
|
|
924
|
+
} else if (isBotMsg) {
|
|
925
|
+
let html = '';
|
|
926
|
+
const env = {
|
|
927
|
+
__box: msgBox
|
|
928
|
+
};
|
|
929
|
+
try {
|
|
930
|
+
let src = md;
|
|
931
|
+
if (this.customMarkup && typeof this.customMarkup.transformSource === 'function') {
|
|
932
|
+
src = this.customMarkup.transformSource(src, {
|
|
933
|
+
streaming: false
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
html = this.MD.render(src, env);
|
|
937
|
+
} catch (_) {
|
|
938
|
+
html = Utils.escapeHtml(md);
|
|
939
|
+
}
|
|
940
|
+
const tpl = document.createElement('template');
|
|
941
|
+
tpl.innerHTML = html;
|
|
942
|
+
const frag = tpl.content;
|
|
943
|
+
try {
|
|
944
|
+
this._resolveCodesIn(frag, env);
|
|
945
|
+
this._releaseEnvCodes(env);
|
|
946
|
+
} catch (_) {}
|
|
947
|
+
el.replaceWith(frag);
|
|
948
|
+
touchedBoxes.add(msgBox);
|
|
949
|
+
} else {
|
|
950
|
+
const span = document.createElement('span');
|
|
951
|
+
span.textContent = md;
|
|
952
|
+
el.replaceWith(span);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
sliceCount++;
|
|
956
|
+
if (sliceCount >= perSlice || this.asyncer.shouldYield(startedAt)) {
|
|
957
|
+
await this.asyncer.yield();
|
|
958
|
+
startedAt = Utils.now();
|
|
959
|
+
sliceCount = 0;
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
try {
|
|
964
|
+
touchedBoxes.forEach(box => {
|
|
965
|
+
try {
|
|
966
|
+
this.customMarkup.apply(box, this.MD);
|
|
967
|
+
} catch (_) {}
|
|
968
|
+
});
|
|
969
|
+
} catch (_) {}
|
|
970
|
+
|
|
971
|
+
this.restoreCollapsedCode(scope);
|
|
972
|
+
this.hooks.observeNewCode(scope, {
|
|
973
|
+
deferLastIfStreaming: true,
|
|
974
|
+
minLinesForLast: this.cfg.PROFILE_CODE.minLinesForHL,
|
|
975
|
+
minCharsForLast: this.cfg.PROFILE_CODE.minCharsForHL
|
|
976
|
+
});
|
|
977
|
+
this.hooks.observeMsgBoxes(scope);
|
|
978
|
+
this.hooks.scheduleMathRender(scope);
|
|
979
|
+
this.hooks.codeScrollInit(scope);
|
|
980
|
+
|
|
981
|
+
if (typeof runtime !== 'undefined' && runtime.highlighter) {
|
|
982
|
+
runtime.highlighter.scanVisibleCodesInRoot(scope, runtime.stream.activeCode || null);
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
this._d('md.pending.end', {
|
|
986
|
+
boxes: touchedBoxes.size
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// Render streaming snapshot (string).
|
|
991
|
+
renderStreamingSnapshot(src) {
|
|
992
|
+
const md = this._md(true);
|
|
993
|
+
if (!md) return '';
|
|
994
|
+
try {
|
|
995
|
+
let s = String(src || '');
|
|
996
|
+
if (this.customMarkup && typeof this.customMarkup.transformSource === 'function') {
|
|
997
|
+
s = this.customMarkup.transformSource(s, {
|
|
998
|
+
streaming: true
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
return md.render(s, {}); // env present in fragment variant
|
|
1002
|
+
} catch (_) {
|
|
1003
|
+
return Utils.escapeHtml(src);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
// Render streaming snapshot as DocumentFragment (GC-friendly for callers).
|
|
1008
|
+
renderStreamingSnapshotFragment(src) {
|
|
1009
|
+
const md = this._md(true);
|
|
1010
|
+
if (!md) {
|
|
1011
|
+
const tpl0 = document.createElement('template');
|
|
1012
|
+
tpl0.innerHTML = '';
|
|
1013
|
+
return tpl0.content;
|
|
1014
|
+
}
|
|
1015
|
+
let html = '';
|
|
1016
|
+
const env = {};
|
|
1017
|
+
try {
|
|
1018
|
+
let s = String(src || '');
|
|
1019
|
+
if (this.customMarkup && typeof this.customMarkup.transformSource === 'function') {
|
|
1020
|
+
s = this.customMarkup.transformSource(s, {
|
|
1021
|
+
streaming: true
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
html = md.render(s, env);
|
|
1025
|
+
} catch (_) {
|
|
1026
|
+
html = Utils.escapeHtml(src || '');
|
|
1027
|
+
}
|
|
1028
|
+
const tpl = document.createElement('template');
|
|
1029
|
+
tpl.innerHTML = html;
|
|
1030
|
+
const frag = tpl.content;
|
|
1031
|
+
|
|
1032
|
+
try {
|
|
1033
|
+
this._resolveCodesIn(frag, env);
|
|
1034
|
+
this._releaseEnvCodes(env);
|
|
1035
|
+
} catch (_) {}
|
|
1036
|
+
this._d('md.render.stream.frag', {
|
|
1037
|
+
srcLen: (src || '').length,
|
|
1038
|
+
htmlLen: html.length
|
|
1039
|
+
});
|
|
1040
|
+
return frag;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
// fast inline-only renderer (used by plain streaming incremental MD) ===
|
|
1044
|
+
renderInlineStreaming(src) {
|
|
1045
|
+
const md = this._md(true);
|
|
1046
|
+
if (!md || typeof md.renderInline !== 'function') return Utils.escapeHtml(src || '');
|
|
1047
|
+
try {
|
|
1048
|
+
const s = String(src || '');
|
|
1049
|
+
return md.renderInline(s);
|
|
1050
|
+
} catch (_) {
|
|
1051
|
+
return Utils.escapeHtml(src || '');
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
// Render final snapshot (string).
|
|
1056
|
+
renderFinalSnapshot(src) {
|
|
1057
|
+
const md = this._md(false);
|
|
1058
|
+
if (!md) return '';
|
|
1059
|
+
try {
|
|
1060
|
+
let s = String(src || '');
|
|
1061
|
+
if (this.customMarkup && typeof this.customMarkup.transformSource === 'function') {
|
|
1062
|
+
s = this.customMarkup.transformSource(s, {
|
|
1063
|
+
streaming: false
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
return md.render(s);
|
|
1067
|
+
} catch (_) {
|
|
1068
|
+
return Utils.escapeHtml(src);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
// Render final snapshot as DocumentFragment (GC-friendly for callers).
|
|
1073
|
+
renderFinalSnapshotFragment(src) {
|
|
1074
|
+
const md = this._md(false);
|
|
1075
|
+
if (!md) {
|
|
1076
|
+
const tpl0 = document.createElement('template');
|
|
1077
|
+
tpl0.innerHTML = '';
|
|
1078
|
+
return tpl0.content;
|
|
1079
|
+
}
|
|
1080
|
+
let html = '';
|
|
1081
|
+
const env = {};
|
|
1082
|
+
try {
|
|
1083
|
+
let s = String(src || '');
|
|
1084
|
+
if (this.customMarkup && typeof this.customMarkup.transformSource === 'function') {
|
|
1085
|
+
s = this.customMarkup.transformSource(s, {
|
|
1086
|
+
streaming: false
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
html = md.render(s, env);
|
|
1090
|
+
} catch (_) {
|
|
1091
|
+
html = Utils.escapeHtml(src);
|
|
1092
|
+
}
|
|
1093
|
+
const tpl = document.createElement('template');
|
|
1094
|
+
tpl.innerHTML = html;
|
|
1095
|
+
const frag = tpl.content;
|
|
1096
|
+
|
|
1097
|
+
try {
|
|
1098
|
+
this._resolveCodesIn(frag, env);
|
|
1099
|
+
this._releaseEnvCodes(env);
|
|
1100
|
+
} catch (_) {}
|
|
1101
|
+
this._d('md.render.final.frag', {
|
|
1102
|
+
srcLen: (src || '').length,
|
|
1103
|
+
htmlLen: html.length
|
|
1104
|
+
});
|
|
1105
|
+
return frag;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
// Restore collapse/expand state of code blocks after DOM updates.
|
|
1109
|
+
restoreCollapsedCode(root) {
|
|
1110
|
+
const scope = root || document;
|
|
1111
|
+
const wrappers = scope.querySelectorAll('.code-wrapper');
|
|
1112
|
+
wrappers.forEach((wrapper) => {
|
|
1113
|
+
const index = wrapper.getAttribute('data-index');
|
|
1114
|
+
const localeCollapse = wrapper.getAttribute('data-locale-collapse');
|
|
1115
|
+
const localeExpand = wrapper.getAttribute('data-locale-expand');
|
|
1116
|
+
const source = wrapper.querySelector('code');
|
|
1117
|
+
const isCollapsed = (window.__collapsed_idx || []).includes(index);
|
|
1118
|
+
if (!source) return;
|
|
1119
|
+
const btn = wrapper.querySelector('.code-header-collapse');
|
|
1120
|
+
if (isCollapsed) {
|
|
1121
|
+
source.style.display = 'none';
|
|
1122
|
+
if (btn) {
|
|
1123
|
+
const span = btn.querySelector('span');
|
|
1124
|
+
if (span) span.textContent = localeExpand;
|
|
1125
|
+
btn.setAttribute('title', localeExpand || 'Expand');
|
|
1126
|
+
}
|
|
1127
|
+
} else {
|
|
1128
|
+
source.style.display = 'block';
|
|
1129
|
+
if (btn) {
|
|
1130
|
+
const span = btn.querySelector('span');
|
|
1131
|
+
if (span) span.textContent = localeCollapse;
|
|
1132
|
+
btn.setAttribute('title', localeCollapse || 'Collapse');
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
}
|