pygpt-net 2.6.55__py3-none-any.whl → 2.6.57__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pygpt_net/CHANGELOG.txt +12 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +26 -22
- pygpt_net/config.py +44 -0
- pygpt_net/controller/audio/audio.py +0 -0
- pygpt_net/controller/calendar/calendar.py +0 -0
- pygpt_net/controller/calendar/note.py +0 -0
- pygpt_net/controller/chat/chat.py +0 -0
- pygpt_net/controller/chat/handler/openai_stream.py +2 -1
- pygpt_net/controller/chat/handler/worker.py +0 -0
- pygpt_net/controller/chat/remote_tools.py +5 -3
- pygpt_net/controller/chat/render.py +0 -0
- pygpt_net/controller/chat/text.py +0 -0
- pygpt_net/controller/ctx/common.py +0 -0
- pygpt_net/controller/debug/debug.py +26 -2
- pygpt_net/controller/debug/fixtures.py +1 -1
- pygpt_net/controller/dialogs/confirm.py +15 -1
- pygpt_net/controller/dialogs/debug.py +2 -0
- pygpt_net/controller/lang/mapping.py +0 -0
- pygpt_net/controller/launcher/launcher.py +0 -0
- pygpt_net/controller/mode/mode.py +0 -0
- pygpt_net/controller/presets/presets.py +0 -0
- pygpt_net/controller/realtime/realtime.py +0 -0
- pygpt_net/controller/theme/theme.py +0 -0
- pygpt_net/controller/ui/mode.py +5 -3
- pygpt_net/controller/ui/tabs.py +0 -0
- pygpt_net/core/agents/agents.py +3 -1
- pygpt_net/core/agents/custom.py +150 -0
- pygpt_net/core/agents/provider.py +0 -0
- pygpt_net/core/builder/__init__.py +12 -0
- pygpt_net/core/builder/graph.py +478 -0
- pygpt_net/core/calendar/calendar.py +0 -0
- pygpt_net/core/ctx/ctx.py +0 -0
- pygpt_net/core/ctx/output.py +0 -0
- pygpt_net/core/debug/agent.py +0 -0
- pygpt_net/core/debug/agent_builder.py +29 -0
- pygpt_net/core/debug/console/console.py +0 -0
- pygpt_net/core/debug/db.py +0 -0
- pygpt_net/core/debug/debug.py +0 -0
- pygpt_net/core/debug/events.py +0 -0
- pygpt_net/core/debug/indexes.py +0 -0
- pygpt_net/core/debug/kernel.py +0 -0
- pygpt_net/core/debug/tabs.py +0 -0
- pygpt_net/core/filesystem/filesystem.py +0 -0
- pygpt_net/core/fixtures/__init__ +0 -0
- pygpt_net/core/fixtures/stream/__init__.py +0 -0
- pygpt_net/core/fixtures/stream/generator.py +0 -0
- pygpt_net/core/models/models.py +2 -1
- pygpt_net/core/plugins/plugins.py +60 -0
- pygpt_net/core/render/plain/pid.py +0 -0
- pygpt_net/core/render/plain/renderer.py +26 -4
- pygpt_net/core/render/web/body.py +46 -4
- pygpt_net/core/render/web/debug.py +0 -0
- pygpt_net/core/render/web/helpers.py +0 -0
- pygpt_net/core/render/web/pid.py +0 -0
- pygpt_net/core/render/web/renderer.py +15 -20
- pygpt_net/core/tabs/tab.py +0 -0
- pygpt_net/core/tabs/tabs.py +0 -0
- pygpt_net/core/text/utils.py +0 -0
- pygpt_net/css.qrc +0 -0
- pygpt_net/css_rc.py +0 -0
- pygpt_net/data/config/config.json +8 -7
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/config/settings.json +14 -0
- pygpt_net/data/css/web-blocks.css +9 -0
- pygpt_net/data/css/web-blocks.dark.css +6 -0
- pygpt_net/data/css/web-blocks.darkest.css +6 -0
- pygpt_net/data/css/web-chatgpt.css +14 -6
- pygpt_net/data/css/web-chatgpt.dark.css +6 -0
- pygpt_net/data/css/web-chatgpt.darkest.css +6 -0
- pygpt_net/data/css/web-chatgpt.light.css +6 -0
- pygpt_net/data/css/web-chatgpt_wide.css +14 -6
- pygpt_net/data/css/web-chatgpt_wide.dark.css +6 -0
- pygpt_net/data/css/web-chatgpt_wide.darkest.css +6 -0
- pygpt_net/data/css/web-chatgpt_wide.light.css +6 -0
- pygpt_net/data/fixtures/fake_stream.txt +14 -1
- pygpt_net/data/icons/case.svg +0 -0
- pygpt_net/data/icons/chat1.svg +0 -0
- pygpt_net/data/icons/chat2.svg +0 -0
- pygpt_net/data/icons/chat3.svg +0 -0
- pygpt_net/data/icons/chat4.svg +0 -0
- pygpt_net/data/icons/fit.svg +0 -0
- pygpt_net/data/icons/note1.svg +0 -0
- pygpt_net/data/icons/note2.svg +0 -0
- pygpt_net/data/icons/note3.svg +0 -0
- pygpt_net/data/icons/stt.svg +0 -0
- pygpt_net/data/icons/translate.svg +0 -0
- pygpt_net/data/icons/tts.svg +0 -0
- pygpt_net/data/icons/url.svg +0 -0
- pygpt_net/data/icons/vision.svg +0 -0
- pygpt_net/data/icons/web_off.svg +0 -0
- pygpt_net/data/icons/web_on.svg +0 -0
- pygpt_net/data/js/app/async.js +166 -0
- pygpt_net/data/js/app/bridge.js +88 -0
- pygpt_net/data/js/app/common.js +212 -0
- pygpt_net/data/js/app/config.js +223 -0
- pygpt_net/data/js/app/custom.js +961 -0
- pygpt_net/data/js/app/data.js +84 -0
- pygpt_net/data/js/app/dom.js +322 -0
- pygpt_net/data/js/app/events.js +400 -0
- pygpt_net/data/js/app/highlight.js +542 -0
- pygpt_net/data/js/app/logger.js +305 -0
- pygpt_net/data/js/app/markdown.js +1137 -0
- pygpt_net/data/js/app/math.js +167 -0
- pygpt_net/data/js/app/nodes.js +395 -0
- pygpt_net/data/js/app/queue.js +260 -0
- pygpt_net/data/js/app/raf.js +250 -0
- pygpt_net/data/js/app/runtime.js +582 -0
- pygpt_net/data/js/app/scroll.js +433 -0
- pygpt_net/data/js/app/stream.js +2708 -0
- pygpt_net/data/js/app/template.js +287 -0
- pygpt_net/data/js/app/tool.js +87 -0
- pygpt_net/data/js/app/ui.js +86 -0
- pygpt_net/data/js/app/user.js +380 -0
- pygpt_net/data/js/app/utils.js +64 -0
- pygpt_net/data/js/app.min.js +880 -0
- pygpt_net/data/js/markdown-it/markdown-it-katex.min.js +1 -1
- pygpt_net/data/js/markdown-it/markdown-it.min.js +0 -0
- pygpt_net/data/locale/locale.de.ini +3 -1
- pygpt_net/data/locale/locale.en.ini +8 -0
- pygpt_net/data/locale/locale.es.ini +2 -0
- pygpt_net/data/locale/locale.fr.ini +2 -0
- pygpt_net/data/locale/locale.it.ini +2 -0
- pygpt_net/data/locale/locale.pl.ini +3 -1
- pygpt_net/data/locale/locale.uk.ini +3 -1
- pygpt_net/data/locale/locale.zh.ini +2 -0
- pygpt_net/data/locale/plugin.osm.en.ini +24 -24
- pygpt_net/data/locale/plugin.wolfram.en.ini +9 -9
- pygpt_net/fonts.qrc +0 -0
- pygpt_net/fonts_rc.py +0 -0
- pygpt_net/icons.qrc +0 -0
- pygpt_net/icons_rc.py +0 -0
- pygpt_net/item/agent.py +62 -0
- pygpt_net/item/builder_layout.py +62 -0
- pygpt_net/js.qrc +24 -1
- pygpt_net/js_rc.py +51394 -33687
- pygpt_net/plugin/base/worker.py +0 -0
- pygpt_net/plugin/cmd_web/config.py +17 -17
- pygpt_net/plugin/cmd_web/worker.py +325 -171
- pygpt_net/plugin/mcp/__init__.py +0 -0
- pygpt_net/plugin/mcp/config.py +0 -0
- pygpt_net/plugin/mcp/plugin.py +0 -0
- pygpt_net/plugin/mcp/worker.py +0 -0
- pygpt_net/plugin/osm/__init__.py +0 -0
- pygpt_net/plugin/osm/config.py +0 -0
- pygpt_net/plugin/osm/plugin.py +0 -0
- pygpt_net/plugin/osm/worker.py +0 -0
- pygpt_net/plugin/wolfram/__init__.py +0 -0
- pygpt_net/plugin/wolfram/config.py +0 -0
- pygpt_net/plugin/wolfram/plugin.py +0 -0
- pygpt_net/plugin/wolfram/worker.py +0 -0
- pygpt_net/provider/api/anthropic/tools.py +0 -0
- pygpt_net/provider/api/google/__init__.py +0 -0
- pygpt_net/provider/api/google/video.py +0 -0
- pygpt_net/provider/api/openai/agents/experts.py +0 -0
- pygpt_net/provider/api/openai/agents/remote_tools.py +0 -0
- pygpt_net/provider/api/openai/remote_tools.py +0 -0
- pygpt_net/provider/api/openai/responses.py +0 -0
- pygpt_net/provider/api/x_ai/__init__.py +2 -0
- pygpt_net/provider/api/x_ai/remote.py +0 -0
- pygpt_net/provider/core/agent/__init__.py +10 -0
- pygpt_net/provider/core/agent/base.py +51 -0
- pygpt_net/provider/core/agent/json_file.py +200 -0
- pygpt_net/provider/core/config/patch.py +33 -0
- pygpt_net/provider/core/config/patches/__init__.py +0 -0
- pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +1 -0
- pygpt_net/provider/core/ctx/db_sqlite/storage.py +0 -0
- pygpt_net/provider/core/model/patches/__init__.py +0 -0
- pygpt_net/provider/core/model/patches/patch_before_2_6_42.py +0 -0
- pygpt_net/provider/core/preset/patch.py +0 -0
- pygpt_net/provider/core/preset/patches/__init__.py +0 -0
- pygpt_net/provider/core/preset/patches/patch_before_2_6_42.py +0 -0
- pygpt_net/provider/llms/anthropic.py +4 -0
- pygpt_net/provider/llms/base.py +2 -0
- pygpt_net/provider/llms/deepseek_api.py +2 -0
- pygpt_net/provider/llms/google.py +2 -0
- pygpt_net/provider/llms/hugging_face_api.py +4 -0
- pygpt_net/provider/llms/hugging_face_embedding.py +0 -0
- pygpt_net/provider/llms/hugging_face_router.py +2 -0
- pygpt_net/provider/llms/local.py +0 -0
- pygpt_net/provider/llms/mistral.py +4 -0
- pygpt_net/provider/llms/open_router.py +0 -0
- pygpt_net/provider/llms/perplexity.py +2 -0
- pygpt_net/provider/llms/utils.py +0 -0
- pygpt_net/provider/llms/voyage.py +0 -0
- pygpt_net/provider/llms/x_ai.py +2 -0
- pygpt_net/tools/agent_builder/__init__.py +12 -0
- pygpt_net/tools/agent_builder/tool.py +292 -0
- pygpt_net/tools/agent_builder/ui/__init__.py +0 -0
- pygpt_net/tools/agent_builder/ui/dialogs.py +152 -0
- pygpt_net/tools/agent_builder/ui/list.py +228 -0
- pygpt_net/tools/code_interpreter/ui/html.py +0 -0
- pygpt_net/tools/code_interpreter/ui/widgets.py +0 -0
- pygpt_net/tools/html_canvas/tool.py +23 -6
- pygpt_net/tools/html_canvas/ui/widgets.py +224 -2
- pygpt_net/ui/layout/chat/chat.py +0 -0
- pygpt_net/ui/layout/chat/output.py +5 -5
- pygpt_net/ui/main.py +10 -9
- pygpt_net/ui/menu/debug.py +39 -1
- pygpt_net/ui/widget/builder/__init__.py +12 -0
- pygpt_net/ui/widget/builder/editor.py +2001 -0
- pygpt_net/ui/widget/dialog/base.py +4 -1
- pygpt_net/ui/widget/draw/painter.py +0 -0
- pygpt_net/ui/widget/element/labels.py +9 -4
- pygpt_net/ui/widget/lists/db.py +0 -0
- pygpt_net/ui/widget/lists/debug.py +0 -0
- pygpt_net/ui/widget/tabs/body.py +0 -0
- pygpt_net/ui/widget/textarea/html.py +1 -0
- pygpt_net/ui/widget/textarea/input.py +28 -10
- pygpt_net/ui/widget/textarea/output.py +21 -1
- pygpt_net/ui/widget/textarea/web.py +31 -3
- pygpt_net/utils.py +40 -0
- {pygpt_net-2.6.55.dist-info → pygpt_net-2.6.57.dist-info}/METADATA +16 -2
- {pygpt_net-2.6.55.dist-info → pygpt_net-2.6.57.dist-info}/RECORD +116 -77
- pygpt_net/data/js/app.js +0 -5869
- {pygpt_net-2.6.55.dist-info → pygpt_net-2.6.57.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.55.dist-info → pygpt_net-2.6.57.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.55.dist-info → pygpt_net-2.6.57.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
// ==========================================================================
|
|
2
|
+
// Template engine for JSON nodes
|
|
3
|
+
// ==========================================================================
|
|
4
|
+
|
|
5
|
+
class NodeTemplateEngine {
|
|
6
|
+
|
|
7
|
+
// JS-side templates for nodes rendered from JSON payload (RenderBlock).
|
|
8
|
+
constructor(cfg, logger) {
|
|
9
|
+
this.cfg = cfg || {};
|
|
10
|
+
this.logger = logger || {
|
|
11
|
+
debug: () => {}
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Escapes a string for safe HTML rendering.
|
|
16
|
+
_esc(s) {
|
|
17
|
+
return (s == null) ? '' : String(s);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Escapes a string for safe HTML rendering.
|
|
21
|
+
_escapeHtml(s) {
|
|
22
|
+
return (typeof Utils !== 'undefined') ? Utils.escapeHtml(s) : String(s).replace(/[&<>"']/g, m => ({
|
|
23
|
+
'&': '&',
|
|
24
|
+
'<': '<',
|
|
25
|
+
'>': '>',
|
|
26
|
+
'"': '"',
|
|
27
|
+
"'": '''
|
|
28
|
+
} [m]));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Render name header given role
|
|
32
|
+
_nameHeader(role, name, avatarUrl) {
|
|
33
|
+
if (!name && !avatarUrl) return '';
|
|
34
|
+
const cls = (role === 'user') ? 'name-user' : 'name-bot';
|
|
35
|
+
const img = avatarUrl ? `<img src="${this._esc(avatarUrl)}" class="avatar"> ` : '';
|
|
36
|
+
return `<div class="name-header ${cls}">${img}${this._esc(name || '')}</div>`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Render user message block
|
|
40
|
+
_renderUser(block) {
|
|
41
|
+
const id = block.id;
|
|
42
|
+
const inp = block.input || {};
|
|
43
|
+
const msgId = `msg-user-${id}`;
|
|
44
|
+
|
|
45
|
+
// NOTE: timestamps intentionally disabled on frontend
|
|
46
|
+
// let ts = '';
|
|
47
|
+
// if (inp.timestamp) { ... }
|
|
48
|
+
|
|
49
|
+
const personalize = !!(block && block.extra && block.extra.personalize === true);
|
|
50
|
+
const nameHeader = personalize ? this._nameHeader('user', inp.name || '', inp.avatar_img || null) : '';
|
|
51
|
+
|
|
52
|
+
const content = this._escapeHtml(inp.text || '').replace(/\r?\n/g, '<br>');
|
|
53
|
+
|
|
54
|
+
// Use existing copy icon and locale strings to keep public API stable.
|
|
55
|
+
const I = (this.cfg && this.cfg.ICONS) || {};
|
|
56
|
+
const L = (this.cfg && this.cfg.LOCALE) || {};
|
|
57
|
+
const copyIcon = I.CODE_COPY || '';
|
|
58
|
+
const copyTitle = L.COPY || 'Copy';
|
|
59
|
+
|
|
60
|
+
// Single icon, no label; positioned via CSS; visible on hover.
|
|
61
|
+
const copyBtn = `<a href="empty:${this._esc(id)}" class="msg-copy-btn" data-id="${this._esc(id)}" data-tip="${this._escapeHtml(copyTitle)}" title="${this._escapeHtml(copyTitle)}" aria-label="${this._escapeHtml(copyTitle)}" role="button"><img src="${this._esc(copyIcon)}" class="copy-img" alt="${this._escapeHtml(copyTitle)}" data-id="${this._esc(id)}"></a>`;
|
|
62
|
+
|
|
63
|
+
return `<div class="msg-box msg-user" id="${msgId}">${nameHeader}<div class="msg">${copyBtn}<p style="margin:0">${content}</p></div></div>`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Render extra blocks (images/files/urls/docs/tool-extra)
|
|
67
|
+
_renderExtras(block) {
|
|
68
|
+
const parts = [];
|
|
69
|
+
|
|
70
|
+
// images
|
|
71
|
+
const images = block.images || {};
|
|
72
|
+
const keysI = Object.keys(images);
|
|
73
|
+
if (keysI.length) {
|
|
74
|
+
keysI.forEach((k) => {
|
|
75
|
+
const it = images[k];
|
|
76
|
+
if (!it) return;
|
|
77
|
+
const url = this._esc(it.url);
|
|
78
|
+
const path = this._esc(it.path);
|
|
79
|
+
const bn = this._esc(it.basename || '');
|
|
80
|
+
if (it.is_video) {
|
|
81
|
+
const src = (it.ext === '.webm' || !it.webm_path) ? path : this._esc(it.webm_path);
|
|
82
|
+
const ext = (src.endsWith('.webm') ? 'webm' : (path.split('.').pop() || 'mp4'));
|
|
83
|
+
parts.push(
|
|
84
|
+
`<div class="extra-src-video-box" title="${url}">` +
|
|
85
|
+
`<video class="video-player" controls>` +
|
|
86
|
+
`<source src="${src}" type="video/${ext}">` +
|
|
87
|
+
`</video>` +
|
|
88
|
+
`<p><a href="bridge://play_video/${url}" class="title">${this._escapeHtml(bn)}</a></p>` +
|
|
89
|
+
`</div>`
|
|
90
|
+
);
|
|
91
|
+
} else {
|
|
92
|
+
parts.push(
|
|
93
|
+
`<div class="extra-src-img-box" title="${url}">` +
|
|
94
|
+
`<div class="img-outer"><div class="img-wrapper"><a href="${url}"><img src="${path}" class="image"></a></div>` +
|
|
95
|
+
`<a href="${url}" class="title">${this._escapeHtml(bn)}</a></div>` +
|
|
96
|
+
`</div><br/>`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// files
|
|
103
|
+
const files = block.files || {};
|
|
104
|
+
const kF = Object.keys(files);
|
|
105
|
+
if (kF.length) {
|
|
106
|
+
const rows = [];
|
|
107
|
+
kF.forEach((k) => {
|
|
108
|
+
const it = files[k];
|
|
109
|
+
if (!it) return;
|
|
110
|
+
const url = this._esc(it.url);
|
|
111
|
+
const path = this._esc(it.path);
|
|
112
|
+
const icon = (typeof window !== 'undefined' && window.ICON_ATTACHMENTS) ? `<img src="${window.ICON_ATTACHMENTS}" class="extra-src-icon">` : '';
|
|
113
|
+
rows.push(`${icon} <b> [${k}] </b> <a href="${url}">${path}</a>`);
|
|
114
|
+
});
|
|
115
|
+
if (rows.length) parts.push(`<div>${rows.join("<br/><br/>")}</div>`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// urls
|
|
119
|
+
const urls = block.urls || {};
|
|
120
|
+
const kU = Object.keys(urls);
|
|
121
|
+
if (kU.length) {
|
|
122
|
+
const rows = [];
|
|
123
|
+
kU.forEach((k) => {
|
|
124
|
+
const it = urls[k];
|
|
125
|
+
if (!it) return;
|
|
126
|
+
const url = this._esc(it.url);
|
|
127
|
+
const icon = (typeof window !== 'undefined' && window.ICON_URL) ? `<img src="${window.ICON_URL}" class="extra-src-icon">` : '';
|
|
128
|
+
rows.push(`${icon}<a href="${url}" title="${url}">${url}</a> <small> [${k}] </small>`);
|
|
129
|
+
});
|
|
130
|
+
if (rows.length) parts.push(`<div>${rows.join("<br/><br/>")}</div>`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// docs (render on JS) or fallback to docs_html
|
|
134
|
+
const extra = block.extra || {};
|
|
135
|
+
const docsRaw = Array.isArray(extra.docs) ? extra.docs : null;
|
|
136
|
+
|
|
137
|
+
if (docsRaw && docsRaw.length) {
|
|
138
|
+
const icon = (typeof window !== 'undefined' && window.ICON_DB) ? `<img src="${window.ICON_DB}" class="extra-src-icon">` : '';
|
|
139
|
+
const prefix = (typeof window !== 'undefined' && window.LOCALE_DOC_PREFIX) ? String(window.LOCALE_DOC_PREFIX) : 'Doc:';
|
|
140
|
+
const limit = 3;
|
|
141
|
+
|
|
142
|
+
// normalize: [{uuid, meta}] OR [{ uuid: {...} }]
|
|
143
|
+
const normalized = [];
|
|
144
|
+
docsRaw.forEach((it) => {
|
|
145
|
+
if (!it || typeof it !== 'object') return;
|
|
146
|
+
if ('uuid' in it && 'meta' in it && typeof it.meta === 'object') {
|
|
147
|
+
normalized.push({
|
|
148
|
+
uuid: String(it.uuid),
|
|
149
|
+
meta: it.meta || {}
|
|
150
|
+
});
|
|
151
|
+
} else {
|
|
152
|
+
const keys = Object.keys(it);
|
|
153
|
+
if (keys.length === 1) {
|
|
154
|
+
const uuid = keys[0];
|
|
155
|
+
const meta = it[uuid];
|
|
156
|
+
if (meta && typeof meta === 'object') {
|
|
157
|
+
normalized.push({
|
|
158
|
+
uuid: String(uuid),
|
|
159
|
+
meta
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const rows = [];
|
|
167
|
+
for (let i = 0; i < Math.min(limit, normalized.length); i++) {
|
|
168
|
+
const d = normalized[i];
|
|
169
|
+
const meta = d.meta || {};
|
|
170
|
+
const entries = Object.keys(meta).map(k => `<b>${this._escapeHtml(k)}:</b> ${this._escapeHtml(String(meta[k]))}`).join(', ');
|
|
171
|
+
rows.push(`<p><small>[${i + 1}] ${this._escapeHtml(d.uuid)}: ${entries}</small></p>`);
|
|
172
|
+
}
|
|
173
|
+
if (rows.length) {
|
|
174
|
+
parts.push(`<p>${icon}<small><b>${this._escapeHtml(prefix)}:</b></small></p>`);
|
|
175
|
+
parts.push(`<div class="cmd"><p>${rows.join('')}</p></div>`);
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
// backward compat
|
|
179
|
+
const docs_html = extra && extra.docs_html ? String(extra.docs_html) : '';
|
|
180
|
+
if (docs_html) parts.push(docs_html);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// plugin-driven tool extra HTML
|
|
184
|
+
const tool_extra_html = extra && extra.tool_extra_html ? String(extra.tool_extra_html) : '';
|
|
185
|
+
if (tool_extra_html) parts.push(`<div class="msg-extra">${tool_extra_html}</div>`);
|
|
186
|
+
|
|
187
|
+
return parts.join('');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Render message-level actions
|
|
191
|
+
_renderActions(block) {
|
|
192
|
+
const extra = block.extra || {};
|
|
193
|
+
const actions = extra.actions || [];
|
|
194
|
+
if (!actions || !actions.length) return '';
|
|
195
|
+
const parts = actions.map((a) => {
|
|
196
|
+
const href = this._esc(a.href || '#');
|
|
197
|
+
const title = this._esc(a.title || '');
|
|
198
|
+
const icon = this._esc(a.icon || '');
|
|
199
|
+
const id = this._esc(a.id || block.id);
|
|
200
|
+
return `<a href="${href}" class="action-icon" data-id="${id}" role="button"><span class="cmd"><img src="${icon}" class="action-img" title="${title}" alt="${title}" data-id="${id}"></span></a>`;
|
|
201
|
+
});
|
|
202
|
+
return `<div class="action-icons" data-id="${this._esc(block.id)}">${parts.join('')}</div>`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Render tool output wrapper (always collapsed by default; wrapper visibility depends on flag)
|
|
206
|
+
// Inside class NodeTemplateEngine
|
|
207
|
+
_renderToolOutputWrapper(block) {
|
|
208
|
+
const extra = block.extra || {};
|
|
209
|
+
|
|
210
|
+
// IMPORTANT: keep initial tool output verbatim (HTML-ready).
|
|
211
|
+
// Do NOT HTML-escape here – the host already provides a safe/HTML-ready string.
|
|
212
|
+
// Escaping again would double-encode entities (e.g. " -> "), which
|
|
213
|
+
// caused visible """ in the UI instead of quotes.
|
|
214
|
+
const tool_output_html = (extra.tool_output != null) ? String(extra.tool_output) : '';
|
|
215
|
+
|
|
216
|
+
// Wrapper visibility: show/hide based on tool_output_visible...
|
|
217
|
+
const wrapperDisplay = (extra.tool_output_visible === true) ? '' : 'display:none';
|
|
218
|
+
|
|
219
|
+
const toggleTitle = (typeof trans !== 'undefined' && trans) ? trans('action.cmd.expand') : 'Expand';
|
|
220
|
+
const expIcon = (typeof window !== 'undefined' && window.ICON_EXPAND) ? window.ICON_EXPAND : '';
|
|
221
|
+
|
|
222
|
+
return (
|
|
223
|
+
`<div class='tool-output' style='${wrapperDisplay}'>` +
|
|
224
|
+
`<span class='toggle-cmd-output' onclick='toggleToolOutput(${this._esc(block.id)});' ` +
|
|
225
|
+
`title='${this._escapeHtml(toggleTitle)}' role='button'>` +
|
|
226
|
+
`<img src='${this._esc(expIcon)}' width='25' height='25' valign='middle'>` +
|
|
227
|
+
`</span>` +
|
|
228
|
+
// Content is initially collapsed. We intentionally do NOT escape here,
|
|
229
|
+
// to keep behavior consistent with ToolOutput.append/update (HTML-in).
|
|
230
|
+
`<div class='content' style='display:none' data-trusted='1'>${tool_output_html}</div>` +
|
|
231
|
+
`</div>`
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Render bot message block (md-block-markdown)
|
|
236
|
+
_renderBot(block) {
|
|
237
|
+
const id = block.id;
|
|
238
|
+
const out = block.output || {};
|
|
239
|
+
const msgId = `msg-bot-${id}`;
|
|
240
|
+
|
|
241
|
+
// timestamps intentionally disabled on frontend
|
|
242
|
+
// let ts = '';
|
|
243
|
+
// if (out.timestamp) { ... }
|
|
244
|
+
|
|
245
|
+
const personalize = !!(block && block.extra && block.extra.personalize === true);
|
|
246
|
+
const nameHeader = personalize ? this._nameHeader('bot', out.name || '', out.avatar_img || null) : '';
|
|
247
|
+
|
|
248
|
+
const mdText = this._escapeHtml(out.text || '');
|
|
249
|
+
const toolWrap = this._renderToolOutputWrapper(block);
|
|
250
|
+
const extras = this._renderExtras(block);
|
|
251
|
+
const actions = (block.extra && block.extra.footer_icons) ? this._renderActions(block) : '';
|
|
252
|
+
const debug = (block.extra && block.extra.debug_html) ? String(block.extra.debug_html) : '';
|
|
253
|
+
|
|
254
|
+
return (
|
|
255
|
+
`<div class='msg-box msg-bot' id='${msgId}'>` +
|
|
256
|
+
`${nameHeader}` +
|
|
257
|
+
`<div class='msg'>` +
|
|
258
|
+
`<div class='md-block' md-block-markdown='1'>${mdText}</div>` +
|
|
259
|
+
`<div class='msg-tool-extra'></div>` +
|
|
260
|
+
`${toolWrap}` +
|
|
261
|
+
`<div class='msg-extra'>${extras}</div>` +
|
|
262
|
+
`${actions}${debug}` +
|
|
263
|
+
`</div>` +
|
|
264
|
+
`</div>`
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Render one RenderBlock into HTML (may produce 1 or 2 messages – input and/or output)
|
|
269
|
+
renderNode(block) {
|
|
270
|
+
const parts = [];
|
|
271
|
+
if (block && block.input && block.input.text) parts.push(this._renderUser(block));
|
|
272
|
+
if (block && block.output && block.output.text) parts.push(this._renderBot(block));
|
|
273
|
+
return parts.join('');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Render array of blocks
|
|
277
|
+
renderNodes(blocks) {
|
|
278
|
+
if (!Array.isArray(blocks)) return '';
|
|
279
|
+
const out = [];
|
|
280
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
281
|
+
const b = blocks[i] || null;
|
|
282
|
+
if (!b) continue;
|
|
283
|
+
out.push(this.renderNode(b));
|
|
284
|
+
}
|
|
285
|
+
return out.join('');
|
|
286
|
+
}
|
|
287
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// ==========================================================================
|
|
2
|
+
// Tool output
|
|
3
|
+
// ==========================================================================
|
|
4
|
+
|
|
5
|
+
class ToolOutput {
|
|
6
|
+
|
|
7
|
+
// Placeholder for loader show (can be extended by host).
|
|
8
|
+
showLoader() {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Hide spinner elements in bot messages.
|
|
13
|
+
hideLoader() {
|
|
14
|
+
const elements = document.querySelectorAll('.msg-bot');
|
|
15
|
+
if (elements.length > 0) elements.forEach(el => {
|
|
16
|
+
const s = el.querySelector('.spinner');
|
|
17
|
+
if (s) s.style.display = 'none';
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Begins a new tool session.
|
|
22
|
+
begin() {
|
|
23
|
+
this.showLoader();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Ends the current tool session.
|
|
27
|
+
end() {
|
|
28
|
+
this.hideLoader();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Enables the tool output area.
|
|
32
|
+
enable() {
|
|
33
|
+
const els = document.querySelectorAll('.tool-output');
|
|
34
|
+
if (els.length) els[els.length - 1].style.display = 'block';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Disables the tool output area.
|
|
38
|
+
disable() {
|
|
39
|
+
const els = document.querySelectorAll('.tool-output');
|
|
40
|
+
if (els.length) els[els.length - 1].style.display = 'none';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Append HTML into the latest tool-output content area.
|
|
44
|
+
append(content) {
|
|
45
|
+
this.hideLoader();
|
|
46
|
+
this.enable();
|
|
47
|
+
const els = document.querySelectorAll('.tool-output');
|
|
48
|
+
if (els.length) {
|
|
49
|
+
const contentEl = els[els.length - 1].querySelector('.content');
|
|
50
|
+
if (contentEl) contentEl.insertAdjacentHTML('beforeend', content);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Replace inner HTML for the latest tool-output content area.
|
|
55
|
+
update(content) {
|
|
56
|
+
this.hideLoader();
|
|
57
|
+
this.enable();
|
|
58
|
+
const els = document.querySelectorAll('.tool-output');
|
|
59
|
+
if (els.length) {
|
|
60
|
+
const contentEl = els[els.length - 1].querySelector('.content');
|
|
61
|
+
if (contentEl) contentEl.innerHTML = content;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Remove children from the latest tool-output content area.
|
|
66
|
+
clear() {
|
|
67
|
+
this.hideLoader();
|
|
68
|
+
this.enable();
|
|
69
|
+
const els = document.querySelectorAll('.tool-output');
|
|
70
|
+
if (els.length) {
|
|
71
|
+
const contentEl = els[els.length - 1].querySelector('.content');
|
|
72
|
+
if (contentEl) contentEl.replaceChildren();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Toggle visibility of a specific tool output block by message id.
|
|
77
|
+
toggle(id) {
|
|
78
|
+
const el = document.getElementById('msg-bot-' + id);
|
|
79
|
+
if (!el) return;
|
|
80
|
+
const outputEl = el.querySelector('.tool-output');
|
|
81
|
+
if (!outputEl) return;
|
|
82
|
+
const contentEl = outputEl.querySelector('.content');
|
|
83
|
+
if (contentEl) contentEl.style.display = (contentEl.style.display === 'none') ? 'block' : 'none';
|
|
84
|
+
const toggleEl = outputEl.querySelector('.toggle-cmd-output img');
|
|
85
|
+
if (toggleEl) toggleEl.classList.toggle('toggle-expanded');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// ==========================================================================
|
|
2
|
+
// UI manager
|
|
3
|
+
// ==========================================================================
|
|
4
|
+
|
|
5
|
+
class UIManager {
|
|
6
|
+
|
|
7
|
+
// Replace or insert app-level CSS in a <style> tag.
|
|
8
|
+
updateCSS(styles) {
|
|
9
|
+
let style = document.getElementById('app-style');
|
|
10
|
+
if (!style) {
|
|
11
|
+
style = document.createElement('style');
|
|
12
|
+
style.id = 'app-style';
|
|
13
|
+
document.head.appendChild(style);
|
|
14
|
+
}
|
|
15
|
+
style.textContent = styles;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Ensure base styles for code header sticky behavior exist.
|
|
19
|
+
ensureStickyHeaderStyle() {
|
|
20
|
+
let style = document.getElementById('code-sticky-style');
|
|
21
|
+
if (style) return;
|
|
22
|
+
style = document.createElement('style');
|
|
23
|
+
style.id = 'code-sticky-style';
|
|
24
|
+
style.textContent = [
|
|
25
|
+
'.code-wrapper { position: relative; }',
|
|
26
|
+
'.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); }',
|
|
27
|
+
'.code-wrapper pre { overflow: visible; margin-top: 0; }',
|
|
28
|
+
'.code-wrapper pre code { display: block; white-space: pre; max-height: 100dvh; overflow: auto;',
|
|
29
|
+
' overscroll-behavior: contain; -webkit-overflow-scrolling: touch; overflow-anchor: none; scrollbar-gutter: stable both-edges; scroll-behavior: auto; }',
|
|
30
|
+
'#_loader_.hidden { display: none !important; visibility: hidden !important; }',
|
|
31
|
+
'#_loader_.visible { display: block; visibility: visible; }',
|
|
32
|
+
|
|
33
|
+
/* User message collapse (uc-*) */
|
|
34
|
+
'.msg-box.msg-user .msg { position: relative; }',
|
|
35
|
+
'.msg-box.msg-user .msg > .uc-content { display: block; overflow: visible; }',
|
|
36
|
+
'.msg-box.msg-user .msg > .uc-content.uc-collapsed { max-height: 1000px; overflow: hidden; }',
|
|
37
|
+
'.msg-box.msg-user .msg > .uc-toggle { display: none; margin-top: 8px; text-align: center; cursor: pointer; user-select: none; }',
|
|
38
|
+
'.msg-box.msg-user .msg > .uc-toggle.visible { display: block; }',
|
|
39
|
+
|
|
40
|
+
/* Increased toggle icon size to a comfortable/default size.
|
|
41
|
+
Overridable via CSS var --uc-toggle-icon-size to keep host-level control. */
|
|
42
|
+
'.msg-box.msg-user .msg > .uc-toggle img { width: var(--uc-toggle-icon-size, 26px); height: var(--uc-toggle-icon-size, 26px); opacity: .8; }',
|
|
43
|
+
'.msg-box.msg-user .msg > .uc-toggle:hover img { opacity: 1; }',
|
|
44
|
+
|
|
45
|
+
/* User copy icon – single icon, top-right, visible on hover/focus */
|
|
46
|
+
'.msg-box.msg-user .msg .msg-copy-btn { position: absolute; top: 2px; right: 0px; z-index: 3;',
|
|
47
|
+
' opacity: 0; pointer-events: none; transition: opacity .15s ease, transform .15s ease, background-color .15s ease, border-color .15s ease;',
|
|
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; }',
|
|
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
|
+
'.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
|
+
'.msg-box.msg-user .msg .msg-copy-btn img { display: block; width: 18px; height: 18px; }',
|
|
53
|
+
|
|
54
|
+
/* Code header action hover and click feedback (icon-only, bot messages/code blocks) */
|
|
55
|
+
'.code-wrapper .code-header-action.code-header-copy,',
|
|
56
|
+
'.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; }',
|
|
57
|
+
'.code-wrapper .code-header-action.code-header-copy:hover,',
|
|
58
|
+
'.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)); }',
|
|
59
|
+
'.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; }',
|
|
60
|
+
|
|
61
|
+
/* Small scale pop when copied */
|
|
62
|
+
'@keyframes msg-copy-pop { 0%{ transform: scale(1); } 60%{ transform: scale(1.1); } 100%{ transform: scale(1); } }'
|
|
63
|
+
].join('\n');
|
|
64
|
+
document.head.appendChild(style);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Toggle classes controlling optional UI features.
|
|
68
|
+
enableEditIcons() {
|
|
69
|
+
document.body && document.body.classList.add('display-edit-icons');
|
|
70
|
+
}
|
|
71
|
+
disableEditIcons() {
|
|
72
|
+
document.body && document.body.classList.remove('display-edit-icons');
|
|
73
|
+
}
|
|
74
|
+
enableTimestamp() {
|
|
75
|
+
document.body && document.body.classList.add('display-timestamp');
|
|
76
|
+
}
|
|
77
|
+
disableTimestamp() {
|
|
78
|
+
document.body && document.body.classList.remove('display-timestamp');
|
|
79
|
+
}
|
|
80
|
+
enableBlocks() {
|
|
81
|
+
document.body && document.body.classList.add('display-blocks');
|
|
82
|
+
}
|
|
83
|
+
disableBlocks() {
|
|
84
|
+
document.body && document.body.classList.remove('display-blocks');
|
|
85
|
+
}
|
|
86
|
+
}
|