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,305 @@
|
|
|
1
|
+
// ==========================================================================
|
|
2
|
+
// Logger (bridge-based; injected into classes)
|
|
3
|
+
// ==========================================================================
|
|
4
|
+
|
|
5
|
+
class Logger {
|
|
6
|
+
|
|
7
|
+
// logger with queue, batch sending and soft caps.
|
|
8
|
+
constructor(cfg) {
|
|
9
|
+
this.cfg = cfg || {
|
|
10
|
+
LOG: {}
|
|
11
|
+
};
|
|
12
|
+
// Queue holds strings waiting to be sent over QWebChannel to Python.
|
|
13
|
+
this.queue = [];
|
|
14
|
+
this.queueBytes = 0; // approximate UTF-16 bytes (chars * 2)
|
|
15
|
+
this.armed = false;
|
|
16
|
+
this.maxTries = 480; // ~8s @60fps
|
|
17
|
+
this.tries = 0;
|
|
18
|
+
this.bridge = null;
|
|
19
|
+
this._idleId = 0; // idle-callback handle (backoff-friendly)
|
|
20
|
+
this._lastFlushQueueBytes = 0; // last known bytes to detect progress
|
|
21
|
+
|
|
22
|
+
// Centralized rAF manager handle (bound by runtime); falls back to microtasks until available.
|
|
23
|
+
this.raf = null;
|
|
24
|
+
this._rafScheduled = false;
|
|
25
|
+
this._rafKey = {
|
|
26
|
+
t: 'Logger:tick'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Soft limits; tunable from window.* if desired.
|
|
30
|
+
const L = this.cfg.LOG || {};
|
|
31
|
+
this.MAX_QUEUE = Utils.g('LOG_MAX_QUEUE', L.MAX_QUEUE ?? 400);
|
|
32
|
+
this.MAX_BYTES = Utils.g('LOG_MAX_BYTES', L.MAX_BYTES ?? 256 * 1024); // 256 KiB
|
|
33
|
+
this.BATCH_MAX = Utils.g('LOG_BATCH_MAX', L.BATCH_MAX ?? 64);
|
|
34
|
+
this.RATE_LIMIT_PER_SEC = Utils.g('LOG_RATE_LIMIT_PER_SEC', L.RATE_LIMIT_PER_SEC ?? 0); // 0 == off
|
|
35
|
+
this._rlWindowMs = 1000;
|
|
36
|
+
this._rlCount = 0;
|
|
37
|
+
this._rlWindowStart = Utils.now();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Connect a QWebChannel bridge and flush any pending messages.
|
|
41
|
+
bindBridge(bridge) {
|
|
42
|
+
this.bridge = bridge || null;
|
|
43
|
+
// When bridge arrives, flush pending messages respecting caps.
|
|
44
|
+
this.flush();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Bind RafManager instance – ensures no direct requestAnimationFrame usage in Logger.
|
|
48
|
+
bindRaf(raf) {
|
|
49
|
+
this.raf = raf || null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Check if debug logging is enabled for a namespace.
|
|
53
|
+
isEnabled(ns) {
|
|
54
|
+
if (!ns) return !!(window.STREAM_DEBUG || window.MD_LANG_DEBUG);
|
|
55
|
+
const key1 = ns + '_DEBUG';
|
|
56
|
+
const key2 = ns.toUpperCase() + '_DEBUG';
|
|
57
|
+
return !!(window[key1] || window[key2] || window.STREAM_DEBUG || window.MD_LANG_DEBUG);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Pretty-view string (safe truncation for logs).
|
|
61
|
+
pv(s, n = 120) {
|
|
62
|
+
if (!s) return '';
|
|
63
|
+
s = String(s);
|
|
64
|
+
if (s.length <= n) return s.replace(/\n/g, '\\n');
|
|
65
|
+
const k = Math.floor(n / 2);
|
|
66
|
+
return (s.slice(0, k) + ' … ' + s.slice(-k)).replace(/\n/g, '\\n');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Serializes an object to JSON.
|
|
70
|
+
j(o) {
|
|
71
|
+
try {
|
|
72
|
+
return JSON.stringify(o);
|
|
73
|
+
} catch (_) {
|
|
74
|
+
try {
|
|
75
|
+
return String(o);
|
|
76
|
+
} catch (__) {
|
|
77
|
+
return '[unserializable]';
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Emits a log message.
|
|
83
|
+
_emit(msg) {
|
|
84
|
+
// Attempt batch-capable sink if provided by the bridge
|
|
85
|
+
try {
|
|
86
|
+
if (this.bridge) {
|
|
87
|
+
if (typeof this.bridge.log_batch === 'function') {
|
|
88
|
+
this.bridge.log_batch([String(msg)]);
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
if (typeof this.bridge.logBatch === 'function') {
|
|
92
|
+
this.bridge.logBatch([String(msg)]);
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
if (typeof this.bridge.log === 'function') {
|
|
96
|
+
this.bridge.log(String(msg));
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (window.runtime && runtime.bridge && typeof runtime.bridge.log === 'function') {
|
|
101
|
+
runtime.bridge.log(String(msg));
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
} catch (_) {}
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Emits a batch of log messages.
|
|
109
|
+
_emitBatch(arr) {
|
|
110
|
+
try {
|
|
111
|
+
if (!arr || !arr.length) return 0;
|
|
112
|
+
// Prefer batch API if present; otherwise fallback to per-line
|
|
113
|
+
if (this.bridge && typeof this.bridge.log_batch === 'function') {
|
|
114
|
+
this.bridge.log_batch(arr.map(String));
|
|
115
|
+
return arr.length;
|
|
116
|
+
}
|
|
117
|
+
if (this.bridge && typeof this.bridge.logBatch === 'function') {
|
|
118
|
+
this.bridge.logBatch(arr.map(String));
|
|
119
|
+
return arr.length;
|
|
120
|
+
}
|
|
121
|
+
if (this.bridge && typeof this.bridge.log === 'function') {
|
|
122
|
+
for (let i = 0; i < arr.length; i++) this.bridge.log(String(arr[i]));
|
|
123
|
+
return arr.length;
|
|
124
|
+
}
|
|
125
|
+
} catch (_) {}
|
|
126
|
+
return 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Maybe drop logs for caps.
|
|
130
|
+
_maybeDropForCaps(len) {
|
|
131
|
+
// Hard cap queue size and memory to guard Python process via QWebChannel.
|
|
132
|
+
if (this.queue.length <= this.MAX_QUEUE && this.queueBytes <= this.MAX_BYTES) return;
|
|
133
|
+
const targetLen = Math.floor(this.MAX_QUEUE * 0.8);
|
|
134
|
+
const targetBytes = Math.floor(this.MAX_BYTES * 0.8);
|
|
135
|
+
// Drop oldest first; keep newest events
|
|
136
|
+
while ((this.queue.length > targetLen || this.queueBytes > targetBytes) && this.queue.length) {
|
|
137
|
+
const removed = this.queue.shift();
|
|
138
|
+
this.queueBytes -= (removed ? removed.length * 2 : 0);
|
|
139
|
+
}
|
|
140
|
+
// Add one synthetic notice to indicate dropped logs (avoid recursion)
|
|
141
|
+
const notice = '[LOGGER] queue trimmed due to caps';
|
|
142
|
+
this.queue.unshift(notice);
|
|
143
|
+
this.queueBytes += notice.length * 2;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Checks if the logger is within the rate limit.
|
|
147
|
+
_passRateLimit() {
|
|
148
|
+
if (!this.RATE_LIMIT_PER_SEC || this.RATE_LIMIT_PER_SEC <= 0) return true;
|
|
149
|
+
const now = Utils.now();
|
|
150
|
+
if (now - this._rlWindowStart > this._rlWindowMs) {
|
|
151
|
+
this._rlWindowStart = now;
|
|
152
|
+
this._rlCount = 0;
|
|
153
|
+
}
|
|
154
|
+
if (this._rlCount >= this.RATE_LIMIT_PER_SEC) return false;
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Push a log line; try immediate send, otherwise enqueue.
|
|
159
|
+
log(text) {
|
|
160
|
+
const msg = String(text);
|
|
161
|
+
// If bridge is available, try immediate emit to avoid queueing overhead.
|
|
162
|
+
if (this.bridge && (typeof this.bridge.log === 'function' || typeof this.bridge.log_batch === 'function' || typeof this.bridge.logBatch === 'function')) {
|
|
163
|
+
if (this._passRateLimit()) {
|
|
164
|
+
const ok = this._emit(msg);
|
|
165
|
+
if (ok) return true;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Enqueue with caps; avoid unbounded growth
|
|
169
|
+
this.queue.push(msg);
|
|
170
|
+
this.queueBytes += msg.length * 2;
|
|
171
|
+
this._maybeDropForCaps(msg.length);
|
|
172
|
+
this._arm();
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Debug wrapper respecting per-namespace switch.
|
|
177
|
+
debug(ns, line, ctx) {
|
|
178
|
+
if (!this.isEnabled(ns)) return false;
|
|
179
|
+
let msg = `[${ns}] ${line || ''}`;
|
|
180
|
+
if (typeof ctx !== 'undefined') msg += ' ' + this.j(ctx);
|
|
181
|
+
return this.log(msg);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Debug an object with safe serialization.
|
|
185
|
+
debug_obj(ns, tag, data) {
|
|
186
|
+
try {
|
|
187
|
+
const lg = this.logger || (this.cfg && this.cfg.logger) || (window.runtime && runtime.logger) || null;
|
|
188
|
+
if (!this.isEnabled(ns)) return false;
|
|
189
|
+
const safeJson = (v) => {
|
|
190
|
+
try {
|
|
191
|
+
const seen = new WeakSet();
|
|
192
|
+
const s = JSON.stringify(v, (k, val) => {
|
|
193
|
+
if (typeof val === 'object' && val !== null) {
|
|
194
|
+
if (val.nodeType) {
|
|
195
|
+
const nm = (val.nodeName || val.tagName || 'DOM').toString();
|
|
196
|
+
return `[DOM ${nm}]`;
|
|
197
|
+
}
|
|
198
|
+
if (seen.has(val)) return '[Circular]';
|
|
199
|
+
seen.add(val);
|
|
200
|
+
}
|
|
201
|
+
if (typeof val === 'string' && val.length > 800) return val.slice(0, 800) + '…';
|
|
202
|
+
return val;
|
|
203
|
+
});
|
|
204
|
+
return s;
|
|
205
|
+
} catch (_) {
|
|
206
|
+
try { return String(v); } catch { return ''; }
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
let line = String(tag || '');
|
|
211
|
+
if (typeof data !== 'undefined') {
|
|
212
|
+
const payload = safeJson(data);
|
|
213
|
+
if (payload && payload !== '""') line += ' ' + payload;
|
|
214
|
+
}
|
|
215
|
+
this.debug(ns, line);
|
|
216
|
+
} catch (_) {}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Send a batch of lines if bridge is ready.
|
|
220
|
+
flush(maxPerTick = this.BATCH_MAX) {
|
|
221
|
+
// Centralized flush – no direct cancelAnimationFrame here; RafManager governs frames.
|
|
222
|
+
if (!this.bridge && !(window.runtime && runtime.bridge && typeof runtime.bridge.log === 'function')) return 0;
|
|
223
|
+
const n = Math.min(maxPerTick, this.queue.length);
|
|
224
|
+
if (!n) return 0;
|
|
225
|
+
const batch = this.queue.splice(0, n);
|
|
226
|
+
// Update memory accounting before attempting emit to eagerly unlock memory
|
|
227
|
+
let bytes = 0;
|
|
228
|
+
for (let i = 0; i < batch.length; i++) bytes += batch[i].length * 2;
|
|
229
|
+
this.queueBytes = Math.max(0, this.queueBytes - bytes);
|
|
230
|
+
const sent = this._emitBatch(batch);
|
|
231
|
+
|
|
232
|
+
// fix byte accounting for partial sends (re-queue remaining with accurate bytes).
|
|
233
|
+
if (sent < batch.length) {
|
|
234
|
+
const remain = batch.slice(sent);
|
|
235
|
+
let remBytes = 0;
|
|
236
|
+
for (let i = 0; i < remain.length; i++) remBytes += remain[i].length * 2;
|
|
237
|
+
// Prepend remaining back to the queue preserving order
|
|
238
|
+
for (let i = remain.length - 1; i >= 0; i--) this.queue.unshift(remain[i]);
|
|
239
|
+
this.queueBytes += remBytes;
|
|
240
|
+
}
|
|
241
|
+
return sent;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Schedules a tick for log flushing.
|
|
245
|
+
_scheduleTick(tick) {
|
|
246
|
+
// Prefer idle scheduling when bridge isn't available yet – avoids 60fps RAF churn.
|
|
247
|
+
const preferIdle = !this.bridge;
|
|
248
|
+
|
|
249
|
+
const scheduleIdle = () => {
|
|
250
|
+
try {
|
|
251
|
+
if (this._idleId) Utils.cancelIdle(this._idleId);
|
|
252
|
+
} catch (_) {}
|
|
253
|
+
// Use requestIdleCallback when available, fallback to small timeout. Keeps UI cold on idle.
|
|
254
|
+
this._idleId = Utils.idle(() => {
|
|
255
|
+
this._idleId = 0;
|
|
256
|
+
tick();
|
|
257
|
+
}, 800);
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
if (preferIdle) {
|
|
261
|
+
scheduleIdle();
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (this._rafScheduled) return;
|
|
266
|
+
this._rafScheduled = true;
|
|
267
|
+
const run = () => {
|
|
268
|
+
this._rafScheduled = false;
|
|
269
|
+
tick();
|
|
270
|
+
};
|
|
271
|
+
try {
|
|
272
|
+
if (this.raf && typeof this.raf.schedule === 'function') {
|
|
273
|
+
this.raf.schedule(this._rafKey, run, 'Logger', 3);
|
|
274
|
+
} else if (typeof runtime !== 'undefined' && runtime.raf && typeof runtime.raf.schedule === 'function') {
|
|
275
|
+
runtime.raf.schedule(this._rafKey, run, 'Logger', 3);
|
|
276
|
+
} else {
|
|
277
|
+
Promise.resolve().then(run);
|
|
278
|
+
}
|
|
279
|
+
} catch (_) {
|
|
280
|
+
Promise.resolve().then(run);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Arms the logger for flushing.
|
|
285
|
+
_arm() {
|
|
286
|
+
if (this.armed) return;
|
|
287
|
+
this.armed = true;
|
|
288
|
+
this.tries = 0;
|
|
289
|
+
const tick = () => {
|
|
290
|
+
if (!this.armed) return;
|
|
291
|
+
this.flush();
|
|
292
|
+
this.tries++;
|
|
293
|
+
if (this.queue.length === 0 || this.tries > this.maxTries) {
|
|
294
|
+
this.armed = false;
|
|
295
|
+
try {
|
|
296
|
+
if (this._idleId) Utils.cancelIdle(this._idleId);
|
|
297
|
+
} catch (_) {}
|
|
298
|
+
this._idleId = 0;
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
this._scheduleTick(tick);
|
|
302
|
+
};
|
|
303
|
+
this._scheduleTick(tick);
|
|
304
|
+
}
|
|
305
|
+
}
|