backtrace-console 0.0.1 → 0.0.2

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.
@@ -0,0 +1,290 @@
1
+ :root {
2
+ --bg-color: #f7f7f8;
3
+ --chat-bg: #ffffff;
4
+ --text-main: #374151;
5
+ --text-muted: #6b7280;
6
+ --accent-color: #10a37f;
7
+ --accent-hover: #0d8c6d;
8
+ --border-color: #e5e7eb;
9
+ --user-msg-bg: #f3f4f6;
10
+ --agent-msg-bg: #ffffff;
11
+ }
12
+
13
+ * {
14
+ box-sizing: border-box;
15
+ margin: 0;
16
+ padding: 0;
17
+ }
18
+
19
+ body {
20
+ font-family: 'Space Grotesk', 'Noto Sans SC', sans-serif;
21
+ background-color: var(--bg-color);
22
+ color: var(--text-main);
23
+ display: flex;
24
+ height: 100vh;
25
+ overflow: hidden;
26
+ }
27
+
28
+ /* 侧边栏 (可选) */
29
+ .sidebar {
30
+ width: 280px;
31
+ background: linear-gradient(180deg, #1d1f24 0%, #17181c 100%);
32
+ color: white;
33
+ display: flex;
34
+ flex-direction: column;
35
+ padding: 1rem;
36
+ border-right: 1px solid rgba(255,255,255,0.06);
37
+ }
38
+
39
+ .sidebar-header {
40
+ font-weight: 700;
41
+ font-size: 1.05rem;
42
+ margin-bottom: 1.25rem;
43
+ padding-bottom: 1rem;
44
+ border-bottom: 1px solid rgba(255,255,255,0.08);
45
+ letter-spacing: 0.02em;
46
+ }
47
+
48
+ .new-chat-btn {
49
+ background: rgba(255,255,255,0.02);
50
+ border: 1px solid rgba(255,255,255,0.12);
51
+ color: white;
52
+ padding: 0.85rem 0.9rem;
53
+ border-radius: 10px;
54
+ cursor: pointer;
55
+ display: flex;
56
+ align-items: center;
57
+ gap: 0.5rem;
58
+ transition: background 0.2s, border-color 0.2s, transform 0.2s;
59
+ font-weight: 600;
60
+ }
61
+
62
+ .new-chat-btn:hover {
63
+ background-color: rgba(255,255,255,0.08);
64
+ border-color: rgba(255,255,255,0.2);
65
+ transform: translateY(-1px);
66
+ }
67
+
68
+ .session-list {
69
+ margin-top: 1rem;
70
+ overflow-y: auto;
71
+ flex: 1;
72
+ padding-right: 0.25rem;
73
+ }
74
+
75
+ .session-empty {
76
+ color: #9ca3af;
77
+ font-size: 0.9rem;
78
+ margin-top: 1rem;
79
+ line-height: 1.6;
80
+ }
81
+
82
+ .session-item {
83
+ width: 100%;
84
+ text-align: left;
85
+ background: transparent;
86
+ color: white;
87
+ border: 1px solid rgba(255,255,255,0.08);
88
+ border-radius: 10px;
89
+ padding: 0.85rem 0.9rem;
90
+ margin-top: 0.6rem;
91
+ cursor: pointer;
92
+ transition: background 0.2s, border-color 0.2s, transform 0.2s;
93
+ }
94
+
95
+ .session-item:hover {
96
+ background: rgba(255,255,255,0.06);
97
+ border-color: rgba(255,255,255,0.14);
98
+ transform: translateY(-1px);
99
+ }
100
+
101
+ .session-item.active {
102
+ background: rgba(16,163,127,0.16);
103
+ border-color: rgba(16,163,127,0.55);
104
+ box-shadow: inset 0 0 0 1px rgba(16,163,127,0.25);
105
+ position: relative;
106
+ }
107
+
108
+ .session-item.active::before {
109
+ content: '';
110
+ position: absolute;
111
+ left: -1px;
112
+ top: 10px;
113
+ bottom: 10px;
114
+ width: 4px;
115
+ border-radius: 999px;
116
+ background: linear-gradient(180deg, #34d399 0%, #10a37f 100%);
117
+ }
118
+
119
+ .session-title {
120
+ font-size: 0.92rem;
121
+ font-weight: 600;
122
+ line-height: 1.45;
123
+ color: #f3f4f6;
124
+ }
125
+
126
+ .session-time {
127
+ font-size: 0.76rem;
128
+ color: #9ca3af;
129
+ margin-top: 0.35rem;
130
+ }
131
+
132
+ /* 右侧信息面板 */
133
+ .info-panel {
134
+ width: 320px;
135
+ background-color: var(--chat-bg);
136
+ border-left: 1px solid var(--border-color);
137
+ display: flex;
138
+ flex-direction: column;
139
+ padding: 1rem;
140
+ overflow-y: auto;
141
+ }
142
+
143
+ .info-panel-header {
144
+ font-weight: 700;
145
+ font-size: 1.1rem;
146
+ margin-bottom: 1rem;
147
+ padding-bottom: 1rem;
148
+ border-bottom: 1px solid var(--border-color);
149
+ color: var(--text-main);
150
+ }
151
+
152
+ .info-item {
153
+ margin-bottom: 1rem;
154
+ }
155
+
156
+ .info-label {
157
+ font-size: 0.85rem;
158
+ color: var(--text-muted);
159
+ margin-bottom: 0.25rem;
160
+ }
161
+
162
+ .info-value {
163
+ font-size: 0.95rem;
164
+ color: var(--text-main);
165
+ word-break: break-all;
166
+ }
167
+
168
+ .info-value.error-message {
169
+ font-family: monospace;
170
+ background: var(--bg-color);
171
+ padding: 0.5rem;
172
+ border-radius: 4px;
173
+ font-size: 0.85rem;
174
+ }
175
+
176
+ /* 聊天主区域 */
177
+ .main-content {
178
+ flex: 1;
179
+ display: flex;
180
+ flex-direction: column;
181
+ position: relative;
182
+ }
183
+
184
+ .header {
185
+ padding: 1rem 2rem;
186
+ background: var(--chat-bg);
187
+ border-bottom: 1px solid var(--border-color);
188
+ display: flex;
189
+ justify-content: space-between;
190
+ align-items: center;
191
+ }
192
+
193
+ .header h2 {
194
+ font-size: 1.25rem;
195
+ font-weight: 600;
196
+ }
197
+
198
+ .back-link {
199
+ color: var(--text-muted);
200
+ text-decoration: none;
201
+ font-size: 0.9rem;
202
+ }
203
+
204
+ .back-link:hover {
205
+ color: var(--accent-color);
206
+ }
207
+
208
+ .chat-container {
209
+ flex: 1;
210
+ overflow-y: auto;
211
+ padding: 2rem 0 2.5rem;
212
+ scroll-behavior: smooth;
213
+ background:
214
+ radial-gradient(circle at top, rgba(16,163,127,0.05), transparent 28%),
215
+ linear-gradient(180deg, #fafafa 0%, #f3f4f6 100%);
216
+ }
217
+
218
+ .turn-container {
219
+ max-width: 980px;
220
+ margin: 0 auto 1.25rem;
221
+ display: flex;
222
+ flex-direction: column;
223
+ gap: 0.75rem;
224
+ width: calc(100% - 4rem);
225
+ }
226
+
227
+ .turn-container > .message {
228
+ margin-bottom: 0;
229
+ max-width: none;
230
+ width: auto;
231
+ padding: 0;
232
+ }
233
+
234
+
235
+ .message.user {
236
+ flex-direction: row-reverse;
237
+ }
238
+
239
+ .message.agent {
240
+ background-color: transparent;
241
+ }
242
+
243
+ .avatar {
244
+ width: 40px;
245
+ height: 40px;
246
+ border-radius: 12px;
247
+ display: flex;
248
+ align-items: center;
249
+ justify-content: center;
250
+ font-weight: 700;
251
+ font-size: 0.8rem;
252
+ color: white;
253
+ flex-shrink: 0;
254
+ box-shadow: 0 10px 24px rgba(15, 23, 42, 0.12);
255
+ }
256
+
257
+ .user .avatar {
258
+ background: linear-gradient(135deg, #6d4aff 0%, #5436da 100%);
259
+ }
260
+
261
+ .agent .avatar {
262
+ background: linear-gradient(135deg, #10a37f 0%, #0d8c6d 100%);
263
+ }
264
+
265
+ .message-content {
266
+ flex: 1;
267
+ line-height: 1.72;
268
+ font-size: 0.98rem;
269
+ padding: 1rem 1.1rem;
270
+ border-radius: 16px;
271
+ border: 1px solid rgba(15, 23, 42, 0.06);
272
+ box-shadow: 0 12px 28px rgba(15, 23, 42, 0.06);
273
+ background: rgba(255,255,255,0.98);
274
+ color: var(--text-main);
275
+ word-break: break-word;
276
+ white-space: pre-wrap;
277
+ }
278
+
279
+ .message.user .message-content {
280
+ background: linear-gradient(180deg, #f7f7ff 0%, #eef0ff 100%);
281
+ border-color: rgba(84,54,218,0.16);
282
+ }
283
+
284
+ .agent-block {
285
+ margin-top: 0.85rem;
286
+ padding-top: 0.85rem;
287
+ border-top: 1px solid rgba(15, 23, 42, 0.08);
288
+ line-height: 1.72;
289
+ }
290
+
@@ -0,0 +1,308 @@
1
+ function createMessageElement(text, isUser) {
2
+ const msgDiv = document.createElement('div');
3
+ msgDiv.className = `message ${isUser ? 'user' : 'agent'}`;
4
+
5
+ const avatar = document.createElement('div');
6
+ avatar.className = 'avatar';
7
+ avatar.textContent = isUser ? 'U' : 'AI';
8
+
9
+ const content = document.createElement('div');
10
+ content.className = 'message-content';
11
+ setMessageContent(content, text);
12
+
13
+ msgDiv.appendChild(avatar);
14
+ msgDiv.appendChild(content);
15
+ return msgDiv;
16
+ }
17
+
18
+
19
+ function setMessageContent(content, text) {
20
+ content.textContent = String(text || '');
21
+ content.style.whiteSpace = 'pre-wrap';
22
+ }
23
+
24
+ function escapeHtml(text) {
25
+ return String(text || '')
26
+ .replace(/&/g, '&')
27
+ .replace(/</g, '&lt;')
28
+ .replace(/>/g, '&gt;')
29
+ .replace(/"/g, '&quot;')
30
+ .replace(/'/g, '&#39;');
31
+ }
32
+
33
+ function formatFileSize(bytes) {
34
+ if (!bytes || bytes < 1024) return bytes + ' B';
35
+ if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
36
+ return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
37
+ }
38
+ function createSpecialMessageElement(label, text, className) {
39
+ const msgDiv = document.createElement('div');
40
+ msgDiv.className = `message agent ${className || ''}`.trim();
41
+
42
+ const avatar = document.createElement('div');
43
+ avatar.className = 'avatar';
44
+ avatar.textContent = label;
45
+
46
+ const content = document.createElement('div');
47
+ content.className = 'message-content';
48
+ setMessageContent(content, text);
49
+
50
+ msgDiv.appendChild(avatar);
51
+ msgDiv.appendChild(content);
52
+ return msgDiv;
53
+ }
54
+
55
+ function createToolMessageElement(kind, payload = {}) {
56
+ const label = kind === 'tool_call_started' ? 'RUN' : kind === 'tool_call_completed' ? 'DONE' : 'TOOL';
57
+ const msgDiv = createSpecialMessageElement(label, '', 'tool-message');
58
+ msgDiv.dataset.toolKind = kind;
59
+ updateToolMessageElement(msgDiv, kind, payload);
60
+ return msgDiv;
61
+ }
62
+
63
+ function updateToolMessageElement(element, kind, payload = {}) {
64
+ const content = element.querySelector('.message-content');
65
+ const avatar = element.querySelector('.avatar');
66
+ const toolName = payload.toolName || payload.itemType || payload.text || 'tool';
67
+ const toolId = payload.toolId || '';
68
+ const args = payload.args || payload.meta?.args || payload.meta?.input || null;
69
+ const output = payload.output || payload.text || '';
70
+ const result = payload.result || payload.output || payload.text || '';
71
+
72
+ if (kind === 'tool_call_started') {
73
+ avatar.textContent = 'RUN';
74
+ } else if (kind === 'tool_call_output') {
75
+ avatar.textContent = 'TOOL';
76
+ } else if (kind === 'tool_call_completed') {
77
+ avatar.textContent = 'DONE';
78
+ } else {
79
+ avatar.textContent = 'TOOL';
80
+ }
81
+
82
+ const sections = [];
83
+ sections.push(`<div class="tool-section"><div class="tool-section-title">状态</div><div class="tool-section-body">${kind === 'tool_call_started' ? `正在调用工具:${toolName}` : kind === 'tool_call_output' ? `工具执行中:${toolName}` : kind === 'tool_call_completed' ? `工具执行完成:${toolName}` : `[${kind}] ${toolName}`}</div></div>`);
84
+ if (toolId) {
85
+ sections.push(`<div class="tool-section"><div class="tool-section-title">Tool ID</div><div class="tool-section-body">${toolId}</div></div>`);
86
+ }
87
+ if (args) {
88
+ sections.push(`<div class="tool-section"><div class="tool-section-title">参数</div><div class="tool-section-body">${escapeHtml(typeof args === 'string' ? args : JSON.stringify(args, null, 2))}</div></div>`);
89
+ }
90
+ if (kind === 'tool_call_output' && output) {
91
+ sections.push(`<div class="tool-section"><div class="tool-section-title">输出</div><div class="tool-section-body">${escapeHtml(output)}</div></div>`);
92
+ }
93
+ if (kind === 'tool_call_completed' && result) {
94
+ sections.push(`<div class="tool-section"><div class="tool-section-title">结果</div><div class="tool-section-body">${escapeHtml(result)}</div></div>`);
95
+ }
96
+ if (kind === 'tool_call_started' && payload.meta) {
97
+ sections.push(`<div class="tool-section"><div class="tool-section-title">Meta</div><div class="tool-section-body">${escapeHtml(JSON.stringify(payload.meta, null, 2))}</div></div>`);
98
+ }
99
+ content.innerHTML = sections.join('');
100
+ }
101
+
102
+ function createRunningMessageElement(text = '正在处理中,请稍候…') {
103
+ const msgDiv = createSpecialMessageElement('AI', '', 'running-message');
104
+ updateRunningMessageElement(msgDiv, text);
105
+ return msgDiv;
106
+ }
107
+
108
+ function createSystemMessageElement(text) {
109
+ const msgDiv = createSpecialMessageElement('SYS', text, 'system-message');
110
+ const content = msgDiv.querySelector('.message-content');
111
+ const badge = document.createElement('div');
112
+ badge.className = 'system-badge';
113
+ badge.textContent = '上下文说明';
114
+ content.prepend(badge);
115
+ return msgDiv;
116
+ }
117
+
118
+ function updateRunningMessageElement(element, text) {
119
+ const content = element.querySelector('.message-content');
120
+ const status = document.createElement('div');
121
+ status.className = 'running-status';
122
+ status.innerHTML = `<span class="running-spinner"></span><span>${text}</span>`;
123
+ content.innerHTML = '';
124
+ content.appendChild(status);
125
+ }
126
+
127
+ function setComposerStatus(text) {
128
+ if (!composerStatus) return;
129
+ const normalized = String(text || '').trim();
130
+ if (!normalized) {
131
+ composerStatus.innerHTML = '';
132
+ return;
133
+ }
134
+ const status = document.createElement('div');
135
+ status.className = 'running-status';
136
+ const spinner = document.createElement('span');
137
+ spinner.className = 'running-spinner';
138
+ const label = document.createElement('span');
139
+ label.textContent = normalized;
140
+ status.appendChild(spinner);
141
+ status.appendChild(label);
142
+ composerStatus.innerHTML = '';
143
+ composerStatus.appendChild(status);
144
+ }
145
+
146
+ function clearComposerStatus() {
147
+ setComposerStatus('');
148
+ }
149
+
150
+ function renderInlineMarkdown(text) {
151
+ return escapeHtml(text)
152
+ .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
153
+ .replace(/`([^`]+)`/g, '<code>$1</code>');
154
+ }
155
+
156
+ function renderAgentBlockContent(block, text) {
157
+ const source = String(text || '').replace(/\r\n/g, '\n');
158
+ const lines = source.split('\n');
159
+ const fragments = [];
160
+ let paragraphLines = [];
161
+ let listType = null;
162
+ let listItems = [];
163
+ let inCodeBlock = false;
164
+ let codeLines = [];
165
+
166
+ function flushParagraph() {
167
+ if (paragraphLines.length === 0) return;
168
+ const paragraphHtml = paragraphLines.map(line => renderInlineMarkdown(line)).join('<br/>');
169
+ fragments.push(`<p>${paragraphHtml}</p>`);
170
+ paragraphLines = [];
171
+ }
172
+
173
+ function flushList() {
174
+ if (!listType || listItems.length === 0) return;
175
+ fragments.push(`<${listType}>${listItems.map(item => `<li>${renderInlineMarkdown(item)}</li>`).join('')}</${listType}>`);
176
+ listType = null;
177
+ listItems = [];
178
+ }
179
+
180
+ function flushCodeBlock() {
181
+ fragments.push(`<pre><code>${escapeHtml(codeLines.join('\n'))}</code></pre>`);
182
+ codeLines = [];
183
+ }
184
+
185
+ for (let i = 0; i < lines.length; i += 1) {
186
+ const line = lines[i];
187
+ const trimmed = line.trim();
188
+
189
+ if (/^```/.test(trimmed)) {
190
+ if (!inCodeBlock) {
191
+ flushParagraph();
192
+ flushList();
193
+ inCodeBlock = true;
194
+ codeLines = [];
195
+ } else {
196
+ flushCodeBlock();
197
+ inCodeBlock = false;
198
+ }
199
+ continue;
200
+ }
201
+
202
+ if (inCodeBlock) {
203
+ codeLines.push(line);
204
+ continue;
205
+ }
206
+
207
+ if (!trimmed) {
208
+ flushParagraph();
209
+ flushList();
210
+ continue;
211
+ }
212
+
213
+ const headingMatch = trimmed.match(/^(#{1,3})\s+(.+)$/);
214
+ if (headingMatch) {
215
+ flushParagraph();
216
+ flushList();
217
+ const level = headingMatch[1].length;
218
+ fragments.push(`<h${level}>${renderInlineMarkdown(headingMatch[2])}</h${level}>`);
219
+ continue;
220
+ }
221
+
222
+ const orderedMatch = trimmed.match(/^\d+[.)]\s+(.+)$/);
223
+ if (orderedMatch) {
224
+ flushParagraph();
225
+ if (listType && listType !== 'ol') {
226
+ flushList();
227
+ }
228
+ listType = 'ol';
229
+ listItems.push(orderedMatch[1]);
230
+ continue;
231
+ }
232
+
233
+ const unorderedMatch = trimmed.match(/^[-*]\s+(.+)$/);
234
+ if (unorderedMatch) {
235
+ flushParagraph();
236
+ if (listType && listType !== 'ul') {
237
+ flushList();
238
+ }
239
+ listType = 'ul';
240
+ listItems.push(unorderedMatch[1]);
241
+ continue;
242
+ }
243
+
244
+ if (listType) {
245
+ flushList();
246
+ }
247
+ paragraphLines.push(line);
248
+ }
249
+
250
+ if (inCodeBlock) {
251
+ flushCodeBlock();
252
+ }
253
+ flushParagraph();
254
+ flushList();
255
+ block.innerHTML = fragments.join('') || `<p>${renderInlineMarkdown(source)}</p>`;
256
+ }
257
+
258
+ function renderAgentBlocks(content, blocks) {
259
+ content.innerHTML = '';
260
+ const normalizedBlocks = Array.isArray(blocks) ? blocks.filter(block => typeof block === 'string' && block.trim()) : [];
261
+ if (normalizedBlocks.length === 0) {
262
+ return;
263
+ }
264
+ normalizedBlocks.forEach(text => {
265
+ const block = document.createElement('div');
266
+ block.className = 'agent-block';
267
+ renderAgentBlockContent(block, text);
268
+ content.appendChild(block);
269
+ });
270
+ content.style.whiteSpace = 'normal';
271
+ }
272
+
273
+ function rerenderFullMessage(state) {
274
+ const blocks = state.blocks.slice();
275
+ if (state.currentBlockText && state.currentBlockText.trim()) {
276
+ blocks.push(state.currentBlockText);
277
+ }
278
+ state.renderedText = blocks.join('\n\n');
279
+ renderAgentBlocks(state.contentContainer, blocks);
280
+ state.scrollContainer.scrollTo({
281
+ top: state.scrollContainer.scrollHeight,
282
+ behavior: 'auto'
283
+ });
284
+ }
285
+
286
+ function startTypewriter(state) {
287
+ if (state.typingActive) return;
288
+ state.typingActive = true;
289
+
290
+ state.typewritingInterval = setInterval(() => {
291
+ if (state.queue.length === 0) {
292
+ return;
293
+ }
294
+
295
+ const char = state.queue.charAt(0);
296
+ state.queue = state.queue.substring(1);
297
+ state.currentBlockText += char;
298
+ rerenderFullMessage(state);
299
+ }, 20);
300
+ }
301
+
302
+ function stopTypewriter(state) {
303
+ if (state.typewritingInterval) {
304
+ clearInterval(state.typewritingInterval);
305
+ state.typewritingInterval = null;
306
+ }
307
+ state.typingActive = false;
308
+ }