@hirohsu/user-web-feedback 2.8.21 → 2.8.22
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.
- package/dist/static/modules/conversation-panel.js +312 -243
- package/dist/static/style.css +360 -169
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* conversation-panel.js
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* AI Console - 現代化對話面板
|
|
4
|
+
* 支援即時對話流、歷史記錄瀏覽、完整 Debug 資訊
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { escapeHtml } from './ui-helpers.js';
|
|
@@ -16,304 +16,372 @@ export const ConversationEntryType = {
|
|
|
16
16
|
DEBUG: 'debug'
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
const
|
|
20
|
-
prompt:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
icon: '🤔',
|
|
28
|
-
title: 'AI 思考中',
|
|
29
|
-
className: 'entry-thinking',
|
|
30
|
-
borderColor: 'var(--accent-yellow)'
|
|
31
|
-
},
|
|
32
|
-
tool: {
|
|
33
|
-
icon: '🔧',
|
|
34
|
-
title: '工具呼叫',
|
|
35
|
-
className: 'entry-tool',
|
|
36
|
-
borderColor: 'var(--accent-purple, #a855f7)'
|
|
37
|
-
},
|
|
38
|
-
result: {
|
|
39
|
-
icon: '📥',
|
|
40
|
-
title: '工具結果',
|
|
41
|
-
className: 'entry-result',
|
|
42
|
-
borderColor: 'var(--accent-cyan, #06b6d4)'
|
|
43
|
-
},
|
|
44
|
-
ai: {
|
|
45
|
-
icon: '🤖',
|
|
46
|
-
title: 'AI 回覆',
|
|
47
|
-
className: 'entry-ai',
|
|
48
|
-
borderColor: 'var(--accent-green)'
|
|
49
|
-
},
|
|
50
|
-
error: {
|
|
51
|
-
icon: '❌',
|
|
52
|
-
title: '錯誤',
|
|
53
|
-
className: 'entry-error',
|
|
54
|
-
borderColor: 'var(--accent-red)'
|
|
55
|
-
},
|
|
56
|
-
debug: {
|
|
57
|
-
icon: '🐛',
|
|
58
|
-
title: 'Debug',
|
|
59
|
-
className: 'entry-debug',
|
|
60
|
-
borderColor: 'var(--accent-orange, #f97316)'
|
|
61
|
-
}
|
|
19
|
+
const ENTRY_CONFIG = {
|
|
20
|
+
prompt: { icon: '📤', label: '提示詞', cls: 'entry-prompt', color: 'var(--console-blue, #3b82f6)' },
|
|
21
|
+
thinking: { icon: '🤔', label: 'AI 思考中', cls: 'entry-thinking', color: 'var(--console-yellow, #eab308)' },
|
|
22
|
+
tool: { icon: '🔧', label: '工具呼叫', cls: 'entry-tool', color: 'var(--console-purple, #a855f7)' },
|
|
23
|
+
result: { icon: '📥', label: '工具結果', cls: 'entry-result', color: 'var(--console-cyan, #06b6d4)' },
|
|
24
|
+
ai: { icon: '🤖', label: 'AI 回覆', cls: 'entry-ai', color: 'var(--console-green, #22c55e)' },
|
|
25
|
+
error: { icon: '❌', label: '錯誤', cls: 'entry-error', color: 'var(--console-red, #ef4444)' },
|
|
26
|
+
debug: { icon: '🐛', label: 'Debug', cls: 'entry-debug', color: 'var(--console-orange, #f97316)' }
|
|
62
27
|
};
|
|
63
28
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
29
|
+
const MAX_HISTORY = 50;
|
|
30
|
+
let conversationHistory = [];
|
|
31
|
+
let currentSession = null;
|
|
32
|
+
let activeTab = 'live';
|
|
33
|
+
let debugVisible = false;
|
|
34
|
+
|
|
35
|
+
function newSessionId() {
|
|
36
|
+
return Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function ts() {
|
|
40
|
+
return new Date().toLocaleTimeString('zh-TW', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function buildPanel() {
|
|
44
|
+
const el = document.createElement('div');
|
|
45
|
+
el.id = 'aiConsoleOverlay';
|
|
46
|
+
el.className = 'ai-console-overlay';
|
|
47
|
+
el.innerHTML = `
|
|
48
|
+
<div class="ai-console">
|
|
49
|
+
<header class="console-header">
|
|
50
|
+
<div class="console-title-group">
|
|
51
|
+
<span class="console-logo">⚡</span>
|
|
52
|
+
<h2 class="console-title" id="consoleTitle">AI Console</h2>
|
|
53
|
+
<span class="console-mode-badge" id="consoleBadge">準備中</span>
|
|
54
|
+
</div>
|
|
55
|
+
<div class="console-actions">
|
|
56
|
+
<div class="console-tabs">
|
|
57
|
+
<button class="tab-btn active" data-tab="live">即時</button>
|
|
58
|
+
<button class="tab-btn" data-tab="history">歷史 <span class="history-count" id="historyCount">0</span></button>
|
|
59
|
+
</div>
|
|
60
|
+
<label class="debug-switch" title="Debug 模式">
|
|
61
|
+
<input type="checkbox" id="consoleDebugToggle">
|
|
62
|
+
<span class="debug-switch-track"><span class="debug-switch-thumb"></span></span>
|
|
63
|
+
<span class="debug-switch-label">Debug</span>
|
|
64
|
+
</label>
|
|
65
|
+
<button class="console-close-btn" id="consoleCloseBtn" title="關閉">×</button>
|
|
66
|
+
</div>
|
|
67
|
+
</header>
|
|
68
|
+
<div class="console-body">
|
|
69
|
+
<div class="console-tab-content active" id="tabLive">
|
|
70
|
+
<div class="live-entries" id="liveEntries"></div>
|
|
71
|
+
</div>
|
|
72
|
+
<div class="console-tab-content" id="tabHistory">
|
|
73
|
+
<div class="history-list" id="historyList"></div>
|
|
78
74
|
</div>
|
|
79
|
-
<label class="debug-toggle" title="顯示 Debug 資訊">
|
|
80
|
-
<input type="checkbox" id="debugToggle">
|
|
81
|
-
<span>🐛</span>
|
|
82
|
-
</label>
|
|
83
75
|
</div>
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
</div
|
|
89
|
-
|
|
90
|
-
return panel;
|
|
76
|
+
<footer class="console-footer">
|
|
77
|
+
<div class="console-stats" id="consoleStats"></div>
|
|
78
|
+
<button class="console-btn-close" id="consoleFooterClose">關閉</button>
|
|
79
|
+
</footer>
|
|
80
|
+
</div>`;
|
|
81
|
+
return el;
|
|
91
82
|
}
|
|
92
83
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
84
|
+
function getOrCreatePanel() {
|
|
85
|
+
let overlay = document.getElementById('aiConsoleOverlay');
|
|
86
|
+
if (!overlay) {
|
|
87
|
+
overlay = buildPanel();
|
|
88
|
+
document.body.appendChild(overlay);
|
|
89
|
+
bindEvents(overlay);
|
|
90
|
+
}
|
|
91
|
+
return overlay;
|
|
92
|
+
}
|
|
98
93
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
94
|
+
function bindEvents(overlay) {
|
|
95
|
+
overlay.querySelector('#consoleCloseBtn').onclick = hideConversationPanel;
|
|
96
|
+
overlay.querySelector('#consoleFooterClose').onclick = hideConversationPanel;
|
|
102
97
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
} else if (content && typeof content === 'object') {
|
|
107
|
-
contentHtml = `<pre class="entry-content">${escapeHtml(JSON.stringify(content, null, 2))}</pre>`;
|
|
108
|
-
}
|
|
98
|
+
overlay.querySelectorAll('.tab-btn').forEach(btn => {
|
|
99
|
+
btn.onclick = () => switchTab(btn.dataset.tab);
|
|
100
|
+
});
|
|
109
101
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
</summary>
|
|
118
|
-
<div class="entry-body">
|
|
119
|
-
${contentHtml}
|
|
120
|
-
</div>
|
|
121
|
-
</details>
|
|
122
|
-
`;
|
|
102
|
+
const toggle = overlay.querySelector('#consoleDebugToggle');
|
|
103
|
+
toggle.addEventListener('change', (e) => {
|
|
104
|
+
debugVisible = e.target.checked;
|
|
105
|
+
overlay.querySelectorAll('.entry-debug').forEach(el => {
|
|
106
|
+
el.style.display = debugVisible ? '' : 'none';
|
|
107
|
+
});
|
|
108
|
+
});
|
|
123
109
|
|
|
124
|
-
|
|
110
|
+
overlay.addEventListener('click', (e) => {
|
|
111
|
+
if (e.target === overlay) hideConversationPanel();
|
|
112
|
+
});
|
|
125
113
|
}
|
|
126
114
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const config = entryConfig.debug;
|
|
132
|
-
const entry = document.createElement('div');
|
|
133
|
-
entry.className = `conversation-entry ${config.className}`;
|
|
134
|
-
entry.style.borderLeftColor = config.borderColor;
|
|
115
|
+
function switchTab(tab) {
|
|
116
|
+
activeTab = tab;
|
|
117
|
+
const overlay = document.getElementById('aiConsoleOverlay');
|
|
118
|
+
if (!overlay) return;
|
|
135
119
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
120
|
+
overlay.querySelectorAll('.tab-btn').forEach(b => b.classList.toggle('active', b.dataset.tab === tab));
|
|
121
|
+
overlay.querySelector('#tabLive').classList.toggle('active', tab === 'live');
|
|
122
|
+
overlay.querySelector('#tabHistory').classList.toggle('active', tab === 'history');
|
|
139
123
|
|
|
140
|
-
|
|
124
|
+
if (tab === 'history') renderHistory();
|
|
125
|
+
}
|
|
141
126
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
if (debugInfo.maxTokens !== undefined) {
|
|
152
|
-
bodyHtml += debugRow('📏 Max Tokens', debugInfo.maxTokens);
|
|
153
|
-
}
|
|
154
|
-
if (debugInfo.tokenEstimate !== undefined) {
|
|
155
|
-
bodyHtml += debugRow('🔢 Token 預估', `~${debugInfo.tokenEstimate}`);
|
|
156
|
-
}
|
|
157
|
-
if (debugInfo.totalPromptLength !== undefined) {
|
|
158
|
-
bodyHtml += debugRow('📐 Prompt 長度', `${debugInfo.totalPromptLength} chars`);
|
|
127
|
+
function renderHistory() {
|
|
128
|
+
const list = document.getElementById('historyList');
|
|
129
|
+
if (!list) return;
|
|
130
|
+
list.innerHTML = '';
|
|
131
|
+
|
|
132
|
+
if (conversationHistory.length === 0) {
|
|
133
|
+
list.innerHTML = '<div class="history-empty">尚無歷史記錄</div>';
|
|
134
|
+
return;
|
|
159
135
|
}
|
|
160
|
-
|
|
161
|
-
|
|
136
|
+
|
|
137
|
+
conversationHistory.slice().reverse().forEach(session => {
|
|
138
|
+
const card = document.createElement('div');
|
|
139
|
+
card.className = 'history-card';
|
|
140
|
+
|
|
141
|
+
const hasError = session.entries.some(e => e.type === 'error');
|
|
142
|
+
const statusIcon = hasError ? '❌' : '✅';
|
|
143
|
+
const entryCount = session.entries.length;
|
|
144
|
+
const debugEntry = session.entries.find(e => e.type === 'debug');
|
|
145
|
+
const elapsed = debugEntry?.data?.elapsedMs;
|
|
146
|
+
|
|
147
|
+
card.innerHTML = `
|
|
148
|
+
<div class="history-card-header">
|
|
149
|
+
<span class="history-status">${statusIcon}</span>
|
|
150
|
+
<span class="history-session-title">${escapeHtml(session.title)}</span>
|
|
151
|
+
<span class="history-time">${session.startTime}</span>
|
|
152
|
+
</div>
|
|
153
|
+
<div class="history-card-meta">
|
|
154
|
+
<span class="history-meta-item">📊 ${entryCount} 筆</span>
|
|
155
|
+
<span class="history-meta-item">${session.mode || '—'}</span>
|
|
156
|
+
${elapsed ? `<span class="history-meta-item">⏱ ${elapsed}ms</span>` : ''}
|
|
157
|
+
</div>
|
|
158
|
+
<div class="history-card-entries" style="display:none;"></div>
|
|
159
|
+
`;
|
|
160
|
+
|
|
161
|
+
card.querySelector('.history-card-header').onclick = () => {
|
|
162
|
+
const body = card.querySelector('.history-card-entries');
|
|
163
|
+
const open = body.style.display !== 'none';
|
|
164
|
+
body.style.display = open ? 'none' : '';
|
|
165
|
+
if (!open && body.childElementCount === 0) {
|
|
166
|
+
session.entries.forEach(e => {
|
|
167
|
+
body.appendChild(renderEntry(e.type, e.content, e.options, e.data));
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
list.appendChild(card);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function renderEntry(type, content, options = {}, debugData = null) {
|
|
177
|
+
const cfg = ENTRY_CONFIG[type] || ENTRY_CONFIG.ai;
|
|
178
|
+
const el = document.createElement('div');
|
|
179
|
+
el.className = `console-entry ${cfg.cls}`;
|
|
180
|
+
el.style.setProperty('--entry-accent', cfg.color);
|
|
181
|
+
|
|
182
|
+
if (type === 'debug' && debugData) {
|
|
183
|
+
el.innerHTML = buildDebugHtml(debugData, options);
|
|
184
|
+
if (!debugVisible) el.style.display = 'none';
|
|
185
|
+
return el;
|
|
162
186
|
}
|
|
163
|
-
|
|
164
|
-
|
|
187
|
+
|
|
188
|
+
const titleText = options.title || cfg.label;
|
|
189
|
+
const collapsed = options.collapsed ?? (type === 'prompt' || type === 'tool');
|
|
190
|
+
const time = options.timestamp ? ts() : '';
|
|
191
|
+
|
|
192
|
+
let bodyHtml = '';
|
|
193
|
+
if (typeof content === 'string') {
|
|
194
|
+
bodyHtml = `<pre class="entry-code">${escapeHtml(content)}</pre>`;
|
|
195
|
+
} else if (content && typeof content === 'object') {
|
|
196
|
+
bodyHtml = `<pre class="entry-code">${escapeHtml(JSON.stringify(content, null, 2))}</pre>`;
|
|
165
197
|
}
|
|
166
198
|
|
|
167
|
-
|
|
199
|
+
el.innerHTML = `
|
|
200
|
+
<details ${collapsed ? '' : 'open'}>
|
|
201
|
+
<summary class="entry-row">
|
|
202
|
+
<span class="entry-chevron"></span>
|
|
203
|
+
<span class="entry-dot"></span>
|
|
204
|
+
<span class="entry-icon">${cfg.icon}</span>
|
|
205
|
+
<span class="entry-title">${escapeHtml(titleText)}</span>
|
|
206
|
+
<span class="entry-spacer"></span>
|
|
207
|
+
${options.badge ? `<span class="entry-badge">${escapeHtml(String(options.badge))}</span>` : ''}
|
|
208
|
+
${time ? `<span class="entry-time">${time}</span>` : ''}
|
|
209
|
+
</summary>
|
|
210
|
+
<div class="entry-body">${bodyHtml}</div>
|
|
211
|
+
</details>`;
|
|
212
|
+
return el;
|
|
213
|
+
}
|
|
168
214
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
215
|
+
function buildDebugHtml(debugInfo, options = {}) {
|
|
216
|
+
const time = options.timestamp ? ts() : '';
|
|
217
|
+
const titleText = options.title || 'Debug';
|
|
218
|
+
const collapsed = options.collapsed ?? true;
|
|
219
|
+
|
|
220
|
+
const rows = [];
|
|
221
|
+
if (debugInfo.elapsedMs !== undefined) rows.push(['⏱️ 耗時', `${debugInfo.elapsedMs} ms`]);
|
|
222
|
+
if (debugInfo.model) rows.push(['🧠 模型', debugInfo.model]);
|
|
223
|
+
if (debugInfo.temperature !== undefined) rows.push(['🌡️ Temperature', debugInfo.temperature]);
|
|
224
|
+
if (debugInfo.maxTokens !== undefined) rows.push(['📏 Max Tokens', debugInfo.maxTokens]);
|
|
225
|
+
if (debugInfo.tokenEstimate !== undefined) rows.push(['🔢 Token 預估', `~${debugInfo.tokenEstimate}`]);
|
|
226
|
+
if (debugInfo.totalPromptLength !== undefined) rows.push(['📐 Prompt 長度', `${debugInfo.totalPromptLength} chars`]);
|
|
227
|
+
if (debugInfo.componentCount !== undefined) rows.push(['🧩 組件數量', debugInfo.componentCount]);
|
|
228
|
+
if (debugInfo.mcpToolsCount !== undefined) rows.push(['🔧 MCP 工具數', debugInfo.mcpToolsCount]);
|
|
229
|
+
|
|
230
|
+
let grid = '<div class="dbg-grid">' + rows.map(([k, v]) =>
|
|
231
|
+
`<div class="dbg-cell"><span class="dbg-key">${k}</span><span class="dbg-val">${escapeHtml(String(v))}</span></div>`
|
|
232
|
+
).join('') + '</div>';
|
|
233
|
+
|
|
234
|
+
if (debugInfo.sections?.length) {
|
|
235
|
+
grid += `<div class="dbg-section-title">📋 提示詞區段順序(實際送出)</div>
|
|
236
|
+
<table class="dbg-table"><thead><tr><th>#</th><th>區段名稱</th><th>順序</th><th>長度</th></tr></thead><tbody>` +
|
|
237
|
+
debugInfo.sections.map((s, i) => `<tr><td>${i + 1}</td><td>${escapeHtml(s.name)}</td><td>${s.order}</td><td>${s.length} chars</td></tr>`).join('') +
|
|
238
|
+
'</tbody></table>';
|
|
177
239
|
}
|
|
178
240
|
|
|
179
|
-
if (debugInfo.promptConfigs
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
});
|
|
187
|
-
bodyHtml += '</tbody></table></div>';
|
|
241
|
+
if (debugInfo.promptConfigs?.length) {
|
|
242
|
+
grid += `<div class="dbg-section-title">⚙️ 提示詞配置(設定值)</div>
|
|
243
|
+
<table class="dbg-table"><thead><tr><th>ID</th><th>啟用</th><th>第一次</th><th>第二次</th></tr></thead><tbody>` +
|
|
244
|
+
debugInfo.promptConfigs.map(c =>
|
|
245
|
+
`<tr><td>${escapeHtml(c.id)}</td><td>${c.enabled ? '✅' : '❌'}</td><td>${c.firstOrder}</td><td>${c.secondOrder}</td></tr>`
|
|
246
|
+
).join('') +
|
|
247
|
+
'</tbody></table>';
|
|
188
248
|
}
|
|
189
249
|
|
|
190
250
|
if (debugInfo.error) {
|
|
191
|
-
|
|
251
|
+
grid += `<div class="dbg-error">❌ ${escapeHtml(debugInfo.error)}</div>`;
|
|
192
252
|
}
|
|
193
253
|
|
|
194
|
-
|
|
254
|
+
return `
|
|
195
255
|
<details ${collapsed ? '' : 'open'}>
|
|
196
|
-
<summary class="entry-
|
|
197
|
-
<span class="entry-
|
|
198
|
-
<span class="entry-
|
|
199
|
-
|
|
200
|
-
|
|
256
|
+
<summary class="entry-row">
|
|
257
|
+
<span class="entry-chevron"></span>
|
|
258
|
+
<span class="entry-dot"></span>
|
|
259
|
+
<span class="entry-icon">🐛</span>
|
|
260
|
+
<span class="entry-title">${escapeHtml(titleText)}</span>
|
|
261
|
+
<span class="entry-spacer"></span>
|
|
262
|
+
${options.badge ? `<span class="entry-badge">${escapeHtml(String(options.badge))}</span>` : ''}
|
|
263
|
+
${time ? `<span class="entry-time">${time}</span>` : ''}
|
|
201
264
|
</summary>
|
|
202
|
-
<div class="entry-body
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
</details>
|
|
206
|
-
`;
|
|
265
|
+
<div class="entry-body dbg-body">${grid}</div>
|
|
266
|
+
</details>`;
|
|
267
|
+
}
|
|
207
268
|
|
|
208
|
-
|
|
269
|
+
function saveCurrentSession() {
|
|
270
|
+
if (!currentSession || currentSession.entries.length === 0) return;
|
|
271
|
+
conversationHistory.push({ ...currentSession });
|
|
272
|
+
if (conversationHistory.length > MAX_HISTORY) conversationHistory.shift();
|
|
273
|
+
updateHistoryCount();
|
|
209
274
|
}
|
|
210
275
|
|
|
211
|
-
function
|
|
212
|
-
|
|
276
|
+
function updateHistoryCount() {
|
|
277
|
+
const el = document.getElementById('historyCount');
|
|
278
|
+
if (el) el.textContent = conversationHistory.length;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function updateStats() {
|
|
282
|
+
const el = document.getElementById('consoleStats');
|
|
283
|
+
if (!el || !currentSession) return;
|
|
284
|
+
const count = currentSession.entries.length;
|
|
285
|
+
const debugEntry = currentSession.entries.find(e => e.type === 'debug');
|
|
286
|
+
const elapsed = debugEntry?.data?.elapsedMs;
|
|
287
|
+
let text = `${count} 筆記錄`;
|
|
288
|
+
if (elapsed) text += ` · ${elapsed}ms`;
|
|
289
|
+
if (currentSession.mode) text += ` · ${currentSession.mode}`;
|
|
290
|
+
el.textContent = text;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// ─── Public API (backward-compatible) ───
|
|
294
|
+
|
|
295
|
+
export function createConversationPanel() {
|
|
296
|
+
return buildPanel().querySelector('.ai-console');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export function createConversationEntry(type, content, options = {}) {
|
|
300
|
+
return renderEntry(type, content, options);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export function createDebugEntry(debugInfo, options = {}) {
|
|
304
|
+
return renderEntry('debug', null, options, debugInfo);
|
|
213
305
|
}
|
|
214
306
|
|
|
215
307
|
export function addConversationEntry(type, content, options = {}) {
|
|
216
|
-
const
|
|
217
|
-
if (!
|
|
308
|
+
const container = document.getElementById('liveEntries');
|
|
309
|
+
if (!container) return null;
|
|
218
310
|
|
|
219
|
-
const entry =
|
|
220
|
-
|
|
221
|
-
|
|
311
|
+
const entry = renderEntry(type, content, options);
|
|
312
|
+
container.appendChild(entry);
|
|
313
|
+
container.scrollTop = container.scrollHeight;
|
|
314
|
+
|
|
315
|
+
if (currentSession) {
|
|
316
|
+
currentSession.entries.push({ type, content, options, data: null });
|
|
317
|
+
}
|
|
318
|
+
updateStats();
|
|
222
319
|
return entry;
|
|
223
320
|
}
|
|
224
321
|
|
|
225
322
|
export function addDebugEntry(debugInfo, options = {}) {
|
|
226
|
-
const
|
|
227
|
-
if (!
|
|
323
|
+
const container = document.getElementById('liveEntries');
|
|
324
|
+
if (!container) return null;
|
|
228
325
|
|
|
229
|
-
const entry =
|
|
230
|
-
|
|
231
|
-
|
|
326
|
+
const entry = renderEntry('debug', null, options, debugInfo);
|
|
327
|
+
container.appendChild(entry);
|
|
328
|
+
container.scrollTop = container.scrollHeight;
|
|
232
329
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
entry.style.display = 'none';
|
|
330
|
+
if (currentSession) {
|
|
331
|
+
currentSession.entries.push({ type: 'debug', content: null, options, data: debugInfo });
|
|
236
332
|
}
|
|
237
|
-
|
|
333
|
+
updateStats();
|
|
238
334
|
return entry;
|
|
239
335
|
}
|
|
240
336
|
|
|
241
337
|
export function clearConversationPanel() {
|
|
242
|
-
const
|
|
243
|
-
if (
|
|
244
|
-
body.innerHTML = '';
|
|
245
|
-
}
|
|
338
|
+
const container = document.getElementById('liveEntries');
|
|
339
|
+
if (container) container.innerHTML = '';
|
|
246
340
|
}
|
|
247
341
|
|
|
248
342
|
export function updateConversationMode(mode, cliTool = null) {
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if (modeElement) {
|
|
253
|
-
if (mode === 'cli' && cliTool) {
|
|
254
|
-
modeElement.textContent = `CLI (${cliTool})`;
|
|
255
|
-
} else if (mode === 'api') {
|
|
256
|
-
modeElement.textContent = 'API';
|
|
257
|
-
} else {
|
|
258
|
-
modeElement.textContent = mode;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
343
|
+
const badge = document.getElementById('consoleBadge');
|
|
344
|
+
if (!badge) return;
|
|
261
345
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
346
|
+
let text = mode;
|
|
347
|
+
if (mode === 'cli' && cliTool) text = `CLI (${cliTool})`;
|
|
348
|
+
else if (mode === 'api') text = 'API';
|
|
349
|
+
badge.textContent = text;
|
|
350
|
+
badge.className = 'console-mode-badge mode-' + (mode === 'cli' ? 'cli' : mode === 'api' ? 'api' : 'pending');
|
|
351
|
+
|
|
352
|
+
if (currentSession) {
|
|
353
|
+
currentSession.mode = text;
|
|
269
354
|
}
|
|
270
355
|
}
|
|
271
356
|
|
|
272
357
|
export function updateConversationTitle(title) {
|
|
273
|
-
const
|
|
274
|
-
if (
|
|
275
|
-
|
|
276
|
-
}
|
|
358
|
+
const el = document.getElementById('consoleTitle');
|
|
359
|
+
if (el) el.textContent = title;
|
|
360
|
+
if (currentSession) currentSession.title = title;
|
|
277
361
|
}
|
|
278
362
|
|
|
279
363
|
export function showConversationPanel() {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const debugToggle = panel.querySelector('#debugToggle');
|
|
294
|
-
if (debugToggle) {
|
|
295
|
-
debugToggle.addEventListener('change', (e) => {
|
|
296
|
-
const show = e.target.checked;
|
|
297
|
-
panel.querySelectorAll('.entry-debug').forEach(el => {
|
|
298
|
-
el.style.display = show ? '' : 'none';
|
|
299
|
-
});
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
panel.style.display = 'flex';
|
|
364
|
+
const overlay = getOrCreatePanel();
|
|
365
|
+
overlay.style.display = 'flex';
|
|
366
|
+
|
|
367
|
+
saveCurrentSession();
|
|
368
|
+
currentSession = {
|
|
369
|
+
id: newSessionId(),
|
|
370
|
+
title: 'AI Console',
|
|
371
|
+
mode: '',
|
|
372
|
+
startTime: ts(),
|
|
373
|
+
entries: []
|
|
374
|
+
};
|
|
375
|
+
|
|
304
376
|
clearConversationPanel();
|
|
377
|
+
switchTab('live');
|
|
378
|
+
updateHistoryCount();
|
|
379
|
+
updateStats();
|
|
305
380
|
}
|
|
306
381
|
|
|
307
382
|
export function hideConversationPanel() {
|
|
308
|
-
const
|
|
309
|
-
if (
|
|
310
|
-
panel.style.display = 'none';
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
function formatTimestamp(timestamp) {
|
|
315
|
-
const date = new Date(timestamp);
|
|
316
|
-
return date.toLocaleTimeString('zh-TW', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
|
383
|
+
const overlay = document.getElementById('aiConsoleOverlay');
|
|
384
|
+
if (overlay) overlay.style.display = 'none';
|
|
317
385
|
}
|
|
318
386
|
|
|
319
387
|
export function addThinkingEntry(message = 'AI 思考中...') {
|
|
@@ -324,9 +392,10 @@ export function addThinkingEntry(message = 'AI 思考中...') {
|
|
|
324
392
|
}
|
|
325
393
|
|
|
326
394
|
export function removeThinkingEntry() {
|
|
327
|
-
const
|
|
328
|
-
if (!
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
395
|
+
const container = document.getElementById('liveEntries');
|
|
396
|
+
if (!container) return;
|
|
397
|
+
container.querySelectorAll('.entry-thinking').forEach(el => el.remove());
|
|
398
|
+
if (currentSession) {
|
|
399
|
+
currentSession.entries = currentSession.entries.filter(e => e.type !== 'thinking');
|
|
400
|
+
}
|
|
332
401
|
}
|
package/dist/static/style.css
CHANGED
|
@@ -1961,303 +1961,494 @@ textarea.form-control {
|
|
|
1961
1961
|
border-radius: 0 0 var(--radius-lg) var(--radius-lg);
|
|
1962
1962
|
}
|
|
1963
1963
|
|
|
1964
|
-
/* ============
|
|
1964
|
+
/* ============ AI Console ============ */
|
|
1965
1965
|
|
|
1966
|
-
|
|
1966
|
+
:root {
|
|
1967
|
+
--console-blue: #3b82f6;
|
|
1968
|
+
--console-green: #22c55e;
|
|
1969
|
+
--console-yellow: #eab308;
|
|
1970
|
+
--console-red: #ef4444;
|
|
1971
|
+
--console-purple: #a855f7;
|
|
1972
|
+
--console-cyan: #06b6d4;
|
|
1973
|
+
--console-orange: #f97316;
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
.ai-console-overlay {
|
|
1967
1977
|
position: fixed;
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
bottom: 0;
|
|
1972
|
-
background: rgba(0, 0, 0, 0.85);
|
|
1978
|
+
inset: 0;
|
|
1979
|
+
background: rgba(0, 0, 0, 0.7);
|
|
1980
|
+
backdrop-filter: blur(4px);
|
|
1973
1981
|
z-index: 2000;
|
|
1974
1982
|
display: flex;
|
|
1975
1983
|
align-items: center;
|
|
1976
1984
|
justify-content: center;
|
|
1985
|
+
animation: fadeIn 0.15s ease;
|
|
1977
1986
|
}
|
|
1978
1987
|
|
|
1979
|
-
|
|
1988
|
+
@keyframes fadeIn { from { opacity: 0 } to { opacity: 1 } }
|
|
1989
|
+
|
|
1990
|
+
.ai-console {
|
|
1980
1991
|
background: var(--bg-secondary);
|
|
1981
1992
|
border: 1px solid var(--border-color);
|
|
1982
|
-
border-radius:
|
|
1983
|
-
width:
|
|
1984
|
-
max-width:
|
|
1985
|
-
height:
|
|
1986
|
-
max-height:
|
|
1993
|
+
border-radius: 16px;
|
|
1994
|
+
width: 92%;
|
|
1995
|
+
max-width: 960px;
|
|
1996
|
+
height: 88vh;
|
|
1997
|
+
max-height: 88vh;
|
|
1987
1998
|
display: flex;
|
|
1988
1999
|
flex-direction: column;
|
|
1989
|
-
box-shadow:
|
|
2000
|
+
box-shadow: 0 24px 64px rgba(0,0,0,0.4), 0 0 0 1px rgba(255,255,255,0.05);
|
|
1990
2001
|
overflow: hidden;
|
|
2002
|
+
animation: slideUp 0.2s ease;
|
|
1991
2003
|
}
|
|
1992
2004
|
|
|
1993
|
-
|
|
2005
|
+
@keyframes slideUp { from { transform: translateY(16px); opacity: 0 } to { transform: translateY(0); opacity: 1 } }
|
|
2006
|
+
|
|
2007
|
+
/* ── Header ── */
|
|
2008
|
+
.console-header {
|
|
1994
2009
|
display: flex;
|
|
1995
2010
|
justify-content: space-between;
|
|
1996
2011
|
align-items: center;
|
|
1997
|
-
padding:
|
|
2012
|
+
padding: 12px 20px;
|
|
1998
2013
|
border-bottom: 1px solid var(--border-color);
|
|
1999
2014
|
background: var(--bg-tertiary);
|
|
2000
|
-
border-radius:
|
|
2015
|
+
border-radius: 16px 16px 0 0;
|
|
2016
|
+
gap: 12px;
|
|
2017
|
+
flex-wrap: wrap;
|
|
2001
2018
|
}
|
|
2002
2019
|
|
|
2003
|
-
.
|
|
2020
|
+
.console-title-group {
|
|
2004
2021
|
display: flex;
|
|
2005
2022
|
align-items: center;
|
|
2006
|
-
gap:
|
|
2023
|
+
gap: 10px;
|
|
2024
|
+
min-width: 0;
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
.console-logo {
|
|
2028
|
+
font-size: 20px;
|
|
2029
|
+
filter: drop-shadow(0 0 6px rgba(59,130,246,0.5));
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
.console-title {
|
|
2033
|
+
margin: 0;
|
|
2007
2034
|
font-size: 16px;
|
|
2008
|
-
font-weight:
|
|
2035
|
+
font-weight: 700;
|
|
2009
2036
|
color: var(--text-primary);
|
|
2037
|
+
white-space: nowrap;
|
|
2038
|
+
overflow: hidden;
|
|
2039
|
+
text-overflow: ellipsis;
|
|
2010
2040
|
}
|
|
2011
2041
|
|
|
2012
|
-
.
|
|
2042
|
+
.console-mode-badge {
|
|
2043
|
+
font-size: 11px;
|
|
2044
|
+
font-weight: 600;
|
|
2045
|
+
padding: 3px 10px;
|
|
2046
|
+
border-radius: 20px;
|
|
2047
|
+
background: var(--bg-hover);
|
|
2048
|
+
color: var(--text-secondary);
|
|
2049
|
+
white-space: nowrap;
|
|
2050
|
+
transition: background 0.2s, color 0.2s;
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
.console-mode-badge.mode-api {
|
|
2054
|
+
background: rgba(59,130,246,0.15);
|
|
2055
|
+
color: var(--console-blue);
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
.console-mode-badge.mode-cli {
|
|
2059
|
+
background: rgba(168,85,247,0.15);
|
|
2060
|
+
color: var(--console-purple);
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
.console-actions {
|
|
2013
2064
|
display: flex;
|
|
2014
2065
|
align-items: center;
|
|
2015
|
-
gap:
|
|
2066
|
+
gap: 12px;
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
/* ── Tabs ── */
|
|
2070
|
+
.console-tabs {
|
|
2071
|
+
display: flex;
|
|
2072
|
+
background: var(--bg-primary);
|
|
2073
|
+
border-radius: 8px;
|
|
2074
|
+
padding: 2px;
|
|
2075
|
+
gap: 2px;
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
.tab-btn {
|
|
2079
|
+
padding: 5px 14px;
|
|
2016
2080
|
font-size: 12px;
|
|
2081
|
+
font-weight: 600;
|
|
2082
|
+
border: none;
|
|
2083
|
+
border-radius: 6px;
|
|
2084
|
+
background: transparent;
|
|
2017
2085
|
color: var(--text-secondary);
|
|
2018
|
-
|
|
2086
|
+
cursor: pointer;
|
|
2087
|
+
transition: all 0.15s;
|
|
2088
|
+
display: flex;
|
|
2089
|
+
align-items: center;
|
|
2090
|
+
gap: 6px;
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
.tab-btn:hover { background: var(--bg-hover); color: var(--text-primary); }
|
|
2094
|
+
.tab-btn.active { background: var(--bg-secondary); color: var(--text-primary); box-shadow: 0 1px 3px rgba(0,0,0,0.15); }
|
|
2095
|
+
|
|
2096
|
+
.history-count {
|
|
2097
|
+
font-size: 10px;
|
|
2098
|
+
min-width: 18px;
|
|
2099
|
+
height: 18px;
|
|
2100
|
+
display: inline-flex;
|
|
2101
|
+
align-items: center;
|
|
2102
|
+
justify-content: center;
|
|
2019
2103
|
background: var(--bg-hover);
|
|
2020
|
-
border-radius:
|
|
2104
|
+
border-radius: 9px;
|
|
2105
|
+
color: var(--text-secondary);
|
|
2021
2106
|
}
|
|
2022
2107
|
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2108
|
+
/* ── Debug Switch ── */
|
|
2109
|
+
.debug-switch {
|
|
2110
|
+
display: flex;
|
|
2111
|
+
align-items: center;
|
|
2112
|
+
gap: 6px;
|
|
2113
|
+
cursor: pointer;
|
|
2114
|
+
user-select: none;
|
|
2028
2115
|
}
|
|
2029
2116
|
|
|
2030
|
-
.
|
|
2031
|
-
|
|
2117
|
+
.debug-switch input { display: none; }
|
|
2118
|
+
|
|
2119
|
+
.debug-switch-track {
|
|
2120
|
+
width: 32px;
|
|
2121
|
+
height: 18px;
|
|
2122
|
+
border-radius: 9px;
|
|
2123
|
+
background: var(--bg-hover);
|
|
2124
|
+
position: relative;
|
|
2125
|
+
transition: background 0.2s;
|
|
2032
2126
|
}
|
|
2033
2127
|
|
|
2034
|
-
.
|
|
2035
|
-
background: var(--
|
|
2128
|
+
.debug-switch input:checked + .debug-switch-track {
|
|
2129
|
+
background: var(--console-orange);
|
|
2036
2130
|
}
|
|
2037
2131
|
|
|
2038
|
-
.
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2132
|
+
.debug-switch-thumb {
|
|
2133
|
+
position: absolute;
|
|
2134
|
+
top: 2px;
|
|
2135
|
+
left: 2px;
|
|
2136
|
+
width: 14px;
|
|
2137
|
+
height: 14px;
|
|
2138
|
+
border-radius: 50%;
|
|
2139
|
+
background: white;
|
|
2140
|
+
transition: transform 0.2s;
|
|
2141
|
+
box-shadow: 0 1px 2px rgba(0,0,0,0.2);
|
|
2044
2142
|
}
|
|
2045
2143
|
|
|
2046
|
-
.
|
|
2047
|
-
|
|
2048
|
-
border-radius: var(--radius-sm);
|
|
2049
|
-
background: var(--bg-tertiary);
|
|
2050
|
-
overflow: hidden;
|
|
2051
|
-
margin-bottom: var(--spacing-md);
|
|
2144
|
+
.debug-switch input:checked + .debug-switch-track .debug-switch-thumb {
|
|
2145
|
+
transform: translateX(14px);
|
|
2052
2146
|
}
|
|
2053
2147
|
|
|
2054
|
-
.
|
|
2055
|
-
|
|
2148
|
+
.debug-switch-label {
|
|
2149
|
+
font-size: 12px;
|
|
2150
|
+
font-weight: 500;
|
|
2151
|
+
color: var(--text-secondary);
|
|
2056
2152
|
}
|
|
2057
2153
|
|
|
2058
|
-
|
|
2059
|
-
|
|
2154
|
+
/* ── Close Button ── */
|
|
2155
|
+
.console-close-btn {
|
|
2156
|
+
width: 28px;
|
|
2157
|
+
height: 28px;
|
|
2158
|
+
border: none;
|
|
2159
|
+
border-radius: 8px;
|
|
2160
|
+
background: transparent;
|
|
2161
|
+
color: var(--text-secondary);
|
|
2162
|
+
font-size: 18px;
|
|
2163
|
+
cursor: pointer;
|
|
2164
|
+
display: flex;
|
|
2165
|
+
align-items: center;
|
|
2166
|
+
justify-content: center;
|
|
2167
|
+
transition: all 0.15s;
|
|
2168
|
+
line-height: 1;
|
|
2060
2169
|
}
|
|
2061
2170
|
|
|
2062
|
-
.
|
|
2063
|
-
|
|
2171
|
+
.console-close-btn:hover {
|
|
2172
|
+
background: rgba(239,68,68,0.15);
|
|
2173
|
+
color: var(--console-red);
|
|
2064
2174
|
}
|
|
2065
2175
|
|
|
2066
|
-
|
|
2067
|
-
|
|
2176
|
+
/* ── Body ── */
|
|
2177
|
+
.console-body {
|
|
2178
|
+
flex: 1 1 0;
|
|
2179
|
+
min-height: 0;
|
|
2180
|
+
overflow: hidden;
|
|
2181
|
+
position: relative;
|
|
2068
2182
|
}
|
|
2069
2183
|
|
|
2070
|
-
.
|
|
2071
|
-
|
|
2184
|
+
.console-tab-content {
|
|
2185
|
+
display: none;
|
|
2186
|
+
height: 100%;
|
|
2187
|
+
overflow-y: auto;
|
|
2188
|
+
overflow-x: hidden;
|
|
2189
|
+
padding: 16px 20px;
|
|
2072
2190
|
}
|
|
2073
2191
|
|
|
2074
|
-
.
|
|
2075
|
-
|
|
2192
|
+
.console-tab-content.active { display: block; }
|
|
2193
|
+
|
|
2194
|
+
/* ── Entry ── */
|
|
2195
|
+
.console-entry {
|
|
2196
|
+
border-radius: 10px;
|
|
2197
|
+
background: var(--bg-tertiary);
|
|
2198
|
+
margin-bottom: 10px;
|
|
2199
|
+
border: 1px solid var(--border-color);
|
|
2200
|
+
overflow: hidden;
|
|
2201
|
+
position: relative;
|
|
2202
|
+
transition: border-color 0.2s;
|
|
2076
2203
|
}
|
|
2077
2204
|
|
|
2078
|
-
.
|
|
2079
|
-
|
|
2205
|
+
.console-entry::before {
|
|
2206
|
+
content: '';
|
|
2207
|
+
position: absolute;
|
|
2208
|
+
left: 0;
|
|
2209
|
+
top: 0;
|
|
2210
|
+
bottom: 0;
|
|
2211
|
+
width: 3px;
|
|
2212
|
+
background: var(--entry-accent, var(--border-color));
|
|
2213
|
+
border-radius: 3px 0 0 3px;
|
|
2080
2214
|
}
|
|
2081
2215
|
|
|
2082
|
-
.
|
|
2216
|
+
.console-entry:hover { border-color: color-mix(in srgb, var(--entry-accent) 30%, var(--border-color)); }
|
|
2217
|
+
|
|
2218
|
+
.entry-row {
|
|
2219
|
+
padding: 10px 14px;
|
|
2220
|
+
cursor: pointer;
|
|
2083
2221
|
display: flex;
|
|
2084
2222
|
align-items: center;
|
|
2085
|
-
gap:
|
|
2223
|
+
gap: 8px;
|
|
2224
|
+
user-select: none;
|
|
2225
|
+
list-style: none;
|
|
2226
|
+
background: transparent;
|
|
2227
|
+
transition: background 0.1s;
|
|
2086
2228
|
}
|
|
2087
2229
|
|
|
2088
|
-
.
|
|
2230
|
+
.entry-row:hover { background: var(--bg-hover); }
|
|
2231
|
+
.entry-row::-webkit-details-marker { display: none; }
|
|
2232
|
+
|
|
2233
|
+
.entry-chevron {
|
|
2234
|
+
width: 16px;
|
|
2235
|
+
height: 16px;
|
|
2089
2236
|
display: flex;
|
|
2090
2237
|
align-items: center;
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
opacity: 0.6;
|
|
2096
|
-
transition: opacity 0.2s;
|
|
2238
|
+
justify-content: center;
|
|
2239
|
+
font-size: 10px;
|
|
2240
|
+
color: var(--text-muted);
|
|
2241
|
+
transition: transform 0.2s;
|
|
2097
2242
|
}
|
|
2098
2243
|
|
|
2099
|
-
.
|
|
2100
|
-
|
|
2101
|
-
}
|
|
2244
|
+
.entry-chevron::after { content: '▶'; }
|
|
2245
|
+
details[open] > .entry-row .entry-chevron { transform: rotate(90deg); }
|
|
2102
2246
|
|
|
2103
|
-
.
|
|
2104
|
-
width:
|
|
2105
|
-
height:
|
|
2106
|
-
|
|
2247
|
+
.entry-dot {
|
|
2248
|
+
width: 8px;
|
|
2249
|
+
height: 8px;
|
|
2250
|
+
border-radius: 50%;
|
|
2251
|
+
background: var(--entry-accent, var(--text-muted));
|
|
2252
|
+
flex-shrink: 0;
|
|
2107
2253
|
}
|
|
2108
2254
|
|
|
2109
|
-
.
|
|
2110
|
-
display: grid;
|
|
2111
|
-
grid-template-columns: 1fr 1fr;
|
|
2112
|
-
gap: 4px 16px;
|
|
2113
|
-
margin-bottom: 8px;
|
|
2114
|
-
}
|
|
2255
|
+
.entry-icon { font-size: 14px; flex-shrink: 0; }
|
|
2115
2256
|
|
|
2116
|
-
.
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2257
|
+
.entry-title {
|
|
2258
|
+
font-weight: 600;
|
|
2259
|
+
font-size: 13px;
|
|
2260
|
+
color: var(--text-primary);
|
|
2261
|
+
white-space: nowrap;
|
|
2262
|
+
overflow: hidden;
|
|
2263
|
+
text-overflow: ellipsis;
|
|
2123
2264
|
}
|
|
2124
2265
|
|
|
2125
|
-
.
|
|
2266
|
+
.entry-spacer { flex: 1; }
|
|
2267
|
+
|
|
2268
|
+
.entry-badge {
|
|
2269
|
+
font-size: 11px;
|
|
2270
|
+
padding: 2px 8px;
|
|
2271
|
+
background: var(--bg-primary);
|
|
2272
|
+
border-radius: 10px;
|
|
2126
2273
|
color: var(--text-secondary);
|
|
2127
2274
|
font-weight: 500;
|
|
2275
|
+
white-space: nowrap;
|
|
2128
2276
|
}
|
|
2129
2277
|
|
|
2130
|
-
.
|
|
2131
|
-
|
|
2278
|
+
.entry-time {
|
|
2279
|
+
font-size: 11px;
|
|
2280
|
+
color: var(--text-muted);
|
|
2132
2281
|
font-family: "Consolas", "Monaco", monospace;
|
|
2282
|
+
white-space: nowrap;
|
|
2133
2283
|
}
|
|
2134
2284
|
|
|
2135
|
-
.
|
|
2136
|
-
|
|
2285
|
+
.entry-body {
|
|
2286
|
+
padding: 12px 14px;
|
|
2287
|
+
border-top: 1px solid var(--border-color);
|
|
2137
2288
|
}
|
|
2138
2289
|
|
|
2139
|
-
.
|
|
2290
|
+
.entry-code {
|
|
2291
|
+
margin: 0;
|
|
2292
|
+
padding: 10px 12px;
|
|
2293
|
+
background: var(--bg-primary);
|
|
2294
|
+
border: 1px solid var(--border-color);
|
|
2295
|
+
border-radius: 8px;
|
|
2296
|
+
font-family: "Consolas", "Monaco", "Courier New", monospace;
|
|
2297
|
+
font-size: 12px;
|
|
2298
|
+
line-height: 1.6;
|
|
2299
|
+
white-space: pre-wrap;
|
|
2300
|
+
word-break: break-word;
|
|
2301
|
+
max-height: 340px;
|
|
2302
|
+
overflow-y: auto;
|
|
2303
|
+
color: var(--text-primary);
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2306
|
+
/* ── Debug Body ── */
|
|
2307
|
+
.dbg-body { font-size: 12px; }
|
|
2308
|
+
|
|
2309
|
+
.dbg-grid {
|
|
2310
|
+
display: grid;
|
|
2311
|
+
grid-template-columns: 1fr 1fr;
|
|
2312
|
+
gap: 4px;
|
|
2313
|
+
margin-bottom: 10px;
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2316
|
+
.dbg-cell {
|
|
2317
|
+
display: flex;
|
|
2318
|
+
justify-content: space-between;
|
|
2319
|
+
padding: 4px 10px;
|
|
2320
|
+
border-radius: 6px;
|
|
2321
|
+
background: var(--bg-primary);
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2324
|
+
.dbg-key { color: var(--text-secondary); font-weight: 500; }
|
|
2325
|
+
.dbg-val { color: var(--text-primary); font-family: "Consolas","Monaco",monospace; }
|
|
2326
|
+
|
|
2327
|
+
.dbg-section-title {
|
|
2140
2328
|
font-size: 12px;
|
|
2141
2329
|
font-weight: 600;
|
|
2142
2330
|
color: var(--text-secondary);
|
|
2143
|
-
margin
|
|
2331
|
+
margin: 10px 0 4px;
|
|
2144
2332
|
}
|
|
2145
2333
|
|
|
2146
|
-
.
|
|
2334
|
+
.dbg-table {
|
|
2147
2335
|
width: 100%;
|
|
2148
2336
|
border-collapse: collapse;
|
|
2149
2337
|
font-size: 12px;
|
|
2150
|
-
font-family: "Consolas",
|
|
2338
|
+
font-family: "Consolas","Monaco",monospace;
|
|
2339
|
+
margin-bottom: 8px;
|
|
2151
2340
|
}
|
|
2152
2341
|
|
|
2153
|
-
.
|
|
2154
|
-
.debug-table td {
|
|
2342
|
+
.dbg-table th, .dbg-table td {
|
|
2155
2343
|
padding: 4px 8px;
|
|
2156
2344
|
border: 1px solid var(--border-color);
|
|
2157
2345
|
text-align: left;
|
|
2158
2346
|
}
|
|
2159
2347
|
|
|
2160
|
-
.
|
|
2161
|
-
|
|
2162
|
-
color: var(--text-secondary);
|
|
2163
|
-
font-weight: 600;
|
|
2164
|
-
}
|
|
2165
|
-
|
|
2166
|
-
.debug-table td {
|
|
2167
|
-
color: var(--text-primary);
|
|
2168
|
-
}
|
|
2348
|
+
.dbg-table th { background: var(--bg-hover); color: var(--text-secondary); font-weight: 600; }
|
|
2349
|
+
.dbg-table td { color: var(--text-primary); }
|
|
2169
2350
|
|
|
2170
|
-
.
|
|
2351
|
+
.dbg-error {
|
|
2171
2352
|
margin-top: 8px;
|
|
2172
|
-
padding:
|
|
2173
|
-
background: rgba(239,
|
|
2174
|
-
border: 1px solid rgba(239,
|
|
2175
|
-
border-radius:
|
|
2353
|
+
padding: 8px 12px;
|
|
2354
|
+
background: rgba(239,68,68,0.08);
|
|
2355
|
+
border: 1px solid rgba(239,68,68,0.25);
|
|
2356
|
+
border-radius: 8px;
|
|
2176
2357
|
font-size: 12px;
|
|
2177
|
-
color: var(--
|
|
2358
|
+
color: var(--console-red);
|
|
2178
2359
|
}
|
|
2179
2360
|
|
|
2180
|
-
|
|
2181
|
-
|
|
2361
|
+
/* ── History ── */
|
|
2362
|
+
.history-empty {
|
|
2363
|
+
text-align: center;
|
|
2364
|
+
padding: 48px 20px;
|
|
2365
|
+
color: var(--text-muted);
|
|
2366
|
+
font-size: 14px;
|
|
2182
2367
|
}
|
|
2183
2368
|
|
|
2184
|
-
.
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
list-style: none;
|
|
2192
|
-
background: var(--bg-hover);
|
|
2369
|
+
.history-card {
|
|
2370
|
+
border: 1px solid var(--border-color);
|
|
2371
|
+
border-radius: 10px;
|
|
2372
|
+
background: var(--bg-tertiary);
|
|
2373
|
+
margin-bottom: 8px;
|
|
2374
|
+
overflow: hidden;
|
|
2375
|
+
transition: border-color 0.2s;
|
|
2193
2376
|
}
|
|
2194
2377
|
|
|
2195
|
-
.
|
|
2196
|
-
display: none;
|
|
2197
|
-
}
|
|
2378
|
+
.history-card:hover { border-color: var(--console-blue); }
|
|
2198
2379
|
|
|
2199
|
-
.
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2380
|
+
.history-card-header {
|
|
2381
|
+
display: flex;
|
|
2382
|
+
align-items: center;
|
|
2383
|
+
gap: 8px;
|
|
2384
|
+
padding: 10px 14px;
|
|
2385
|
+
cursor: pointer;
|
|
2386
|
+
transition: background 0.15s;
|
|
2204
2387
|
}
|
|
2205
2388
|
|
|
2206
|
-
|
|
2207
|
-
transform: rotate(90deg);
|
|
2208
|
-
}
|
|
2389
|
+
.history-card-header:hover { background: var(--bg-hover); }
|
|
2209
2390
|
|
|
2210
|
-
.
|
|
2211
|
-
font-size: 14px;
|
|
2212
|
-
}
|
|
2391
|
+
.history-status { font-size: 14px; }
|
|
2213
2392
|
|
|
2214
|
-
.
|
|
2393
|
+
.history-session-title {
|
|
2215
2394
|
flex: 1;
|
|
2216
2395
|
font-weight: 600;
|
|
2217
2396
|
font-size: 13px;
|
|
2218
2397
|
color: var(--text-primary);
|
|
2398
|
+
overflow: hidden;
|
|
2399
|
+
text-overflow: ellipsis;
|
|
2400
|
+
white-space: nowrap;
|
|
2219
2401
|
}
|
|
2220
2402
|
|
|
2221
|
-
.
|
|
2403
|
+
.history-time {
|
|
2222
2404
|
font-size: 11px;
|
|
2223
2405
|
color: var(--text-muted);
|
|
2224
|
-
font-family: "Consolas",
|
|
2406
|
+
font-family: "Consolas","Monaco",monospace;
|
|
2225
2407
|
}
|
|
2226
2408
|
|
|
2227
|
-
.
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2409
|
+
.history-card-meta {
|
|
2410
|
+
display: flex;
|
|
2411
|
+
gap: 12px;
|
|
2412
|
+
padding: 0 14px 10px;
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2415
|
+
.history-meta-item {
|
|
2416
|
+
font-size: 11px;
|
|
2232
2417
|
color: var(--text-secondary);
|
|
2233
2418
|
}
|
|
2234
2419
|
|
|
2235
|
-
.
|
|
2236
|
-
padding:
|
|
2420
|
+
.history-card-entries {
|
|
2421
|
+
padding: 8px 12px 12px;
|
|
2237
2422
|
border-top: 1px solid var(--border-color);
|
|
2238
2423
|
}
|
|
2239
2424
|
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2425
|
+
/* ── Footer ── */
|
|
2426
|
+
.console-footer {
|
|
2427
|
+
display: flex;
|
|
2428
|
+
justify-content: space-between;
|
|
2429
|
+
align-items: center;
|
|
2430
|
+
padding: 10px 20px;
|
|
2431
|
+
border-top: 1px solid var(--border-color);
|
|
2432
|
+
background: var(--bg-tertiary);
|
|
2433
|
+
border-radius: 0 0 16px 16px;
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2436
|
+
.console-stats {
|
|
2247
2437
|
font-size: 12px;
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2438
|
+
color: var(--text-muted);
|
|
2439
|
+
font-family: "Consolas","Monaco",monospace;
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
.console-btn-close {
|
|
2443
|
+
padding: 6px 20px;
|
|
2444
|
+
font-size: 13px;
|
|
2445
|
+
font-weight: 600;
|
|
2446
|
+
border: 1px solid var(--border-color);
|
|
2447
|
+
border-radius: 8px;
|
|
2448
|
+
background: var(--bg-primary);
|
|
2253
2449
|
color: var(--text-primary);
|
|
2450
|
+
cursor: pointer;
|
|
2451
|
+
transition: all 0.15s;
|
|
2254
2452
|
}
|
|
2255
2453
|
|
|
2256
|
-
.
|
|
2257
|
-
display: flex;
|
|
2258
|
-
justify-content: flex-end;
|
|
2259
|
-
padding: var(--spacing-md) var(--spacing-lg);
|
|
2260
|
-
border-top: 1px solid var(--border-color);
|
|
2261
|
-
background: var(--bg-tertiary);
|
|
2262
|
-
border-radius: 0 0 var(--radius-lg) var(--radius-lg);
|
|
2263
|
-
}
|
|
2454
|
+
.console-btn-close:hover { background: var(--bg-hover); }
|