@hirohsu/user-web-feedback 2.8.17 → 2.8.19
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/cli.cjs +84 -234
- package/dist/index.cjs +84 -234
- package/dist/static/modules/conversation-panel.js +132 -47
- package/dist/static/modules/feedback-handler.js +37 -8
- package/dist/static/modules/timer-controller.js +6 -0
- package/dist/static/style.css +106 -0
- package/package.json +1 -1
|
@@ -1,26 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* conversation-panel.js
|
|
3
3
|
* 對話面板元件 - 顯示 AI 對話流程
|
|
4
|
-
* 支援
|
|
4
|
+
* 支援 7 種對話條目類型: prompt, thinking, tool, result, ai, error, debug
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { escapeHtml } from './ui-helpers.js';
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
* 對話條目類型
|
|
11
|
-
*/
|
|
12
9
|
export const ConversationEntryType = {
|
|
13
10
|
PROMPT: 'prompt',
|
|
14
11
|
THINKING: 'thinking',
|
|
15
12
|
TOOL: 'tool',
|
|
16
13
|
RESULT: 'result',
|
|
17
14
|
AI: 'ai',
|
|
18
|
-
ERROR: 'error'
|
|
15
|
+
ERROR: 'error',
|
|
16
|
+
DEBUG: 'debug'
|
|
19
17
|
};
|
|
20
18
|
|
|
21
|
-
/**
|
|
22
|
-
* 對話條目視覺配置
|
|
23
|
-
*/
|
|
24
19
|
const entryConfig = {
|
|
25
20
|
prompt: {
|
|
26
21
|
icon: '📤',
|
|
@@ -57,12 +52,15 @@ const entryConfig = {
|
|
|
57
52
|
title: '錯誤',
|
|
58
53
|
className: 'entry-error',
|
|
59
54
|
borderColor: 'var(--accent-red)'
|
|
55
|
+
},
|
|
56
|
+
debug: {
|
|
57
|
+
icon: '🐛',
|
|
58
|
+
title: 'Debug',
|
|
59
|
+
className: 'entry-debug',
|
|
60
|
+
borderColor: 'var(--accent-orange, #f97316)'
|
|
60
61
|
}
|
|
61
62
|
};
|
|
62
63
|
|
|
63
|
-
/**
|
|
64
|
-
* 建立對話面板容器
|
|
65
|
-
*/
|
|
66
64
|
export function createConversationPanel() {
|
|
67
65
|
const panel = document.createElement('div');
|
|
68
66
|
panel.id = 'conversationPanel';
|
|
@@ -73,14 +71,18 @@ export function createConversationPanel() {
|
|
|
73
71
|
<span class="icon">💬</span>
|
|
74
72
|
<span id="conversationTitle">AI 對話</span>
|
|
75
73
|
</div>
|
|
76
|
-
<div class="conversation-
|
|
77
|
-
<
|
|
78
|
-
|
|
74
|
+
<div class="conversation-header-right">
|
|
75
|
+
<div class="conversation-mode">
|
|
76
|
+
<span class="mode-indicator" id="conversationModeIndicator"></span>
|
|
77
|
+
<span id="conversationMode">準備中</span>
|
|
78
|
+
</div>
|
|
79
|
+
<label class="debug-toggle" title="顯示 Debug 資訊">
|
|
80
|
+
<input type="checkbox" id="debugToggle">
|
|
81
|
+
<span>🐛</span>
|
|
82
|
+
</label>
|
|
79
83
|
</div>
|
|
80
84
|
</div>
|
|
81
|
-
<div class="conversation-body" id="conversationBody">
|
|
82
|
-
<!-- 對話條目會動態添加 -->
|
|
83
|
-
</div>
|
|
85
|
+
<div class="conversation-body" id="conversationBody"></div>
|
|
84
86
|
<div class="conversation-footer">
|
|
85
87
|
<button type="button" id="closeConversation" class="btn btn-secondary">關閉</button>
|
|
86
88
|
</div>
|
|
@@ -88,9 +90,6 @@ export function createConversationPanel() {
|
|
|
88
90
|
return panel;
|
|
89
91
|
}
|
|
90
92
|
|
|
91
|
-
/**
|
|
92
|
-
* 建立對話條目元素
|
|
93
|
-
*/
|
|
94
93
|
export function createConversationEntry(type, content, options = {}) {
|
|
95
94
|
const config = entryConfig[type] || entryConfig.ai;
|
|
96
95
|
const entry = document.createElement('div');
|
|
@@ -98,7 +97,7 @@ export function createConversationEntry(type, content, options = {}) {
|
|
|
98
97
|
entry.style.borderLeftColor = config.borderColor;
|
|
99
98
|
|
|
100
99
|
const titleText = options.title || config.title;
|
|
101
|
-
const collapsed = options.collapsed ?? (type === 'prompt' || type === 'tool');
|
|
100
|
+
const collapsed = options.collapsed ?? (type === 'prompt' || type === 'tool' || type === 'debug');
|
|
102
101
|
const timestamp = options.timestamp ? formatTimestamp(options.timestamp) : '';
|
|
103
102
|
|
|
104
103
|
let contentHtml = '';
|
|
@@ -126,8 +125,93 @@ export function createConversationEntry(type, content, options = {}) {
|
|
|
126
125
|
}
|
|
127
126
|
|
|
128
127
|
/**
|
|
129
|
-
*
|
|
128
|
+
* 建立 Debug 資訊條目(結構化表格)
|
|
130
129
|
*/
|
|
130
|
+
export function createDebugEntry(debugInfo, options = {}) {
|
|
131
|
+
const config = entryConfig.debug;
|
|
132
|
+
const entry = document.createElement('div');
|
|
133
|
+
entry.className = `conversation-entry ${config.className}`;
|
|
134
|
+
entry.style.borderLeftColor = config.borderColor;
|
|
135
|
+
|
|
136
|
+
const titleText = options.title || config.title;
|
|
137
|
+
const collapsed = options.collapsed ?? true;
|
|
138
|
+
const timestamp = options.timestamp ? formatTimestamp(options.timestamp) : '';
|
|
139
|
+
|
|
140
|
+
let bodyHtml = '<div class="debug-info-grid">';
|
|
141
|
+
|
|
142
|
+
if (debugInfo.elapsedMs !== undefined) {
|
|
143
|
+
bodyHtml += debugRow('⏱️ 耗時', `${debugInfo.elapsedMs} ms`);
|
|
144
|
+
}
|
|
145
|
+
if (debugInfo.model) {
|
|
146
|
+
bodyHtml += debugRow('🧠 模型', debugInfo.model);
|
|
147
|
+
}
|
|
148
|
+
if (debugInfo.temperature !== undefined) {
|
|
149
|
+
bodyHtml += debugRow('🌡️ Temperature', debugInfo.temperature);
|
|
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`);
|
|
159
|
+
}
|
|
160
|
+
if (debugInfo.componentCount !== undefined) {
|
|
161
|
+
bodyHtml += debugRow('🧩 組件數量', debugInfo.componentCount);
|
|
162
|
+
}
|
|
163
|
+
if (debugInfo.mcpToolsCount !== undefined) {
|
|
164
|
+
bodyHtml += debugRow('🔧 MCP 工具數', debugInfo.mcpToolsCount);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
bodyHtml += '</div>';
|
|
168
|
+
|
|
169
|
+
if (debugInfo.sections && debugInfo.sections.length > 0) {
|
|
170
|
+
bodyHtml += '<div class="debug-sections">';
|
|
171
|
+
bodyHtml += '<div class="debug-section-title">📋 提示詞區段順序(實際送出)</div>';
|
|
172
|
+
bodyHtml += '<table class="debug-table"><thead><tr><th>#</th><th>區段名稱</th><th>順序</th><th>長度</th></tr></thead><tbody>';
|
|
173
|
+
debugInfo.sections.forEach((s, i) => {
|
|
174
|
+
bodyHtml += `<tr><td>${i + 1}</td><td>${escapeHtml(s.name)}</td><td>${s.order}</td><td>${s.length} chars</td></tr>`;
|
|
175
|
+
});
|
|
176
|
+
bodyHtml += '</tbody></table></div>';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (debugInfo.promptConfigs && debugInfo.promptConfigs.length > 0) {
|
|
180
|
+
bodyHtml += '<div class="debug-sections">';
|
|
181
|
+
bodyHtml += '<div class="debug-section-title">⚙️ 提示詞配置(設定值)</div>';
|
|
182
|
+
bodyHtml += '<table class="debug-table"><thead><tr><th>ID</th><th>啟用</th><th>第一次</th><th>第二次</th></tr></thead><tbody>';
|
|
183
|
+
debugInfo.promptConfigs.forEach(c => {
|
|
184
|
+
const enabledIcon = c.enabled ? '✅' : '❌';
|
|
185
|
+
bodyHtml += `<tr><td>${escapeHtml(c.id)}</td><td>${enabledIcon}</td><td>${c.firstOrder}</td><td>${c.secondOrder}</td></tr>`;
|
|
186
|
+
});
|
|
187
|
+
bodyHtml += '</tbody></table></div>';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (debugInfo.error) {
|
|
191
|
+
bodyHtml += `<div class="debug-error">❌ 錯誤: ${escapeHtml(debugInfo.error)}</div>`;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
entry.innerHTML = `
|
|
195
|
+
<details ${collapsed ? '' : 'open'}>
|
|
196
|
+
<summary class="entry-summary">
|
|
197
|
+
<span class="entry-icon">${config.icon}</span>
|
|
198
|
+
<span class="entry-title">${titleText}</span>
|
|
199
|
+
${timestamp ? `<span class="entry-timestamp">${timestamp}</span>` : ''}
|
|
200
|
+
${options.badge ? `<span class="entry-badge">${options.badge}</span>` : ''}
|
|
201
|
+
</summary>
|
|
202
|
+
<div class="entry-body debug-body">
|
|
203
|
+
${bodyHtml}
|
|
204
|
+
</div>
|
|
205
|
+
</details>
|
|
206
|
+
`;
|
|
207
|
+
|
|
208
|
+
return entry;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function debugRow(label, value) {
|
|
212
|
+
return `<div class="debug-row"><span class="debug-label">${label}</span><span class="debug-value">${escapeHtml(String(value))}</span></div>`;
|
|
213
|
+
}
|
|
214
|
+
|
|
131
215
|
export function addConversationEntry(type, content, options = {}) {
|
|
132
216
|
const body = document.getElementById('conversationBody');
|
|
133
217
|
if (!body) return null;
|
|
@@ -135,13 +219,25 @@ export function addConversationEntry(type, content, options = {}) {
|
|
|
135
219
|
const entry = createConversationEntry(type, content, options);
|
|
136
220
|
body.appendChild(entry);
|
|
137
221
|
body.scrollTop = body.scrollHeight;
|
|
222
|
+
return entry;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function addDebugEntry(debugInfo, options = {}) {
|
|
226
|
+
const body = document.getElementById('conversationBody');
|
|
227
|
+
if (!body) return null;
|
|
228
|
+
|
|
229
|
+
const entry = createDebugEntry(debugInfo, options);
|
|
230
|
+
body.appendChild(entry);
|
|
231
|
+
body.scrollTop = body.scrollHeight;
|
|
232
|
+
|
|
233
|
+
const debugToggle = document.getElementById('debugToggle');
|
|
234
|
+
if (debugToggle && !debugToggle.checked) {
|
|
235
|
+
entry.style.display = 'none';
|
|
236
|
+
}
|
|
138
237
|
|
|
139
238
|
return entry;
|
|
140
239
|
}
|
|
141
240
|
|
|
142
|
-
/**
|
|
143
|
-
* 清空對話面板
|
|
144
|
-
*/
|
|
145
241
|
export function clearConversationPanel() {
|
|
146
242
|
const body = document.getElementById('conversationBody');
|
|
147
243
|
if (body) {
|
|
@@ -149,9 +245,6 @@ export function clearConversationPanel() {
|
|
|
149
245
|
}
|
|
150
246
|
}
|
|
151
247
|
|
|
152
|
-
/**
|
|
153
|
-
* 更新對話面板模式顯示
|
|
154
|
-
*/
|
|
155
248
|
export function updateConversationMode(mode, cliTool = null) {
|
|
156
249
|
const modeElement = document.getElementById('conversationMode');
|
|
157
250
|
const indicator = document.getElementById('conversationModeIndicator');
|
|
@@ -176,9 +269,6 @@ export function updateConversationMode(mode, cliTool = null) {
|
|
|
176
269
|
}
|
|
177
270
|
}
|
|
178
271
|
|
|
179
|
-
/**
|
|
180
|
-
* 更新對話面板標題
|
|
181
|
-
*/
|
|
182
272
|
export function updateConversationTitle(title) {
|
|
183
273
|
const titleElement = document.getElementById('conversationTitle');
|
|
184
274
|
if (titleElement) {
|
|
@@ -186,9 +276,6 @@ export function updateConversationTitle(title) {
|
|
|
186
276
|
}
|
|
187
277
|
}
|
|
188
278
|
|
|
189
|
-
/**
|
|
190
|
-
* 顯示對話面板
|
|
191
|
-
*/
|
|
192
279
|
export function showConversationPanel() {
|
|
193
280
|
let panel = document.getElementById('aiConversationPanel');
|
|
194
281
|
if (!panel) {
|
|
@@ -202,14 +289,21 @@ export function showConversationPanel() {
|
|
|
202
289
|
if (closeBtn) {
|
|
203
290
|
closeBtn.onclick = hideConversationPanel;
|
|
204
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
|
+
}
|
|
205
302
|
}
|
|
206
303
|
panel.style.display = 'flex';
|
|
207
304
|
clearConversationPanel();
|
|
208
305
|
}
|
|
209
306
|
|
|
210
|
-
/**
|
|
211
|
-
* 隱藏對話面板
|
|
212
|
-
*/
|
|
213
307
|
export function hideConversationPanel() {
|
|
214
308
|
const panel = document.getElementById('aiConversationPanel');
|
|
215
309
|
if (panel) {
|
|
@@ -217,17 +311,11 @@ export function hideConversationPanel() {
|
|
|
217
311
|
}
|
|
218
312
|
}
|
|
219
313
|
|
|
220
|
-
/**
|
|
221
|
-
* 格式化時間戳記
|
|
222
|
-
*/
|
|
223
314
|
function formatTimestamp(timestamp) {
|
|
224
315
|
const date = new Date(timestamp);
|
|
225
316
|
return date.toLocaleTimeString('zh-TW', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
|
226
317
|
}
|
|
227
318
|
|
|
228
|
-
/**
|
|
229
|
-
* 新增思考中動畫條目
|
|
230
|
-
*/
|
|
231
319
|
export function addThinkingEntry(message = 'AI 思考中...') {
|
|
232
320
|
return addConversationEntry(ConversationEntryType.THINKING, message, {
|
|
233
321
|
collapsed: false,
|
|
@@ -235,13 +323,10 @@ export function addThinkingEntry(message = 'AI 思考中...') {
|
|
|
235
323
|
});
|
|
236
324
|
}
|
|
237
325
|
|
|
238
|
-
/**
|
|
239
|
-
* 移除思考中條目
|
|
240
|
-
*/
|
|
241
326
|
export function removeThinkingEntry() {
|
|
242
327
|
const body = document.getElementById('conversationBody');
|
|
243
328
|
if (!body) return;
|
|
244
|
-
|
|
329
|
+
|
|
245
330
|
const thinkingEntries = body.querySelectorAll('.entry-thinking');
|
|
246
331
|
thinkingEntries.forEach(entry => entry.remove());
|
|
247
332
|
}
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
showConversationPanel,
|
|
39
39
|
hideConversationPanel,
|
|
40
40
|
addConversationEntry,
|
|
41
|
+
addDebugEntry,
|
|
41
42
|
clearConversationPanel,
|
|
42
43
|
updateConversationMode,
|
|
43
44
|
updateConversationTitle,
|
|
@@ -170,10 +171,18 @@ export async function generateAIReply() {
|
|
|
170
171
|
const data = await response.json();
|
|
171
172
|
removeThinkingEntry();
|
|
172
173
|
|
|
174
|
+
if (data.debug) {
|
|
175
|
+
addDebugEntry(data.debug, {
|
|
176
|
+
title: "Debug 資訊",
|
|
177
|
+
collapsed: true,
|
|
178
|
+
timestamp: Date.now(),
|
|
179
|
+
badge: data.debug.elapsedMs ? `${data.debug.elapsedMs}ms` : undefined,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
173
183
|
if (data.success) {
|
|
174
184
|
updateConversationMode(data.mode, data.cliTool);
|
|
175
185
|
|
|
176
|
-
// 如果有 fallback 原因,顯示通知
|
|
177
186
|
if (data.fallbackReason) {
|
|
178
187
|
showToast("warning", "模式切換", data.fallbackReason);
|
|
179
188
|
}
|
|
@@ -199,7 +208,6 @@ export async function generateAIReply() {
|
|
|
199
208
|
document.getElementById("feedbackText").value = finalReply;
|
|
200
209
|
updateCharCount();
|
|
201
210
|
|
|
202
|
-
// 如果是 fallback,badge 顯示不同的樣式
|
|
203
211
|
let badge = data.mode === "cli" ? `CLI (${data.cliTool})` : "API";
|
|
204
212
|
if (data.fallbackReason) {
|
|
205
213
|
badge = "API (fallback)";
|
|
@@ -905,6 +913,15 @@ export async function generateAIReplyWithTools() {
|
|
|
905
913
|
const data = await response.json();
|
|
906
914
|
removeThinkingEntry();
|
|
907
915
|
|
|
916
|
+
if (data.debug) {
|
|
917
|
+
addDebugEntry(data.debug, {
|
|
918
|
+
title: `Debug (第 ${round} 輪)`,
|
|
919
|
+
collapsed: true,
|
|
920
|
+
timestamp: Date.now(),
|
|
921
|
+
badge: data.debug.elapsedMs ? `${data.debug.elapsedMs}ms` : undefined,
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
|
|
908
925
|
if (!data.success) {
|
|
909
926
|
addConversationEntry(ConversationEntryType.ERROR, data.error || "AI 回覆失敗", {
|
|
910
927
|
title: "錯誤",
|
|
@@ -917,12 +934,10 @@ export async function generateAIReplyWithTools() {
|
|
|
917
934
|
|
|
918
935
|
updateConversationMode(data.mode, data.cliTool);
|
|
919
936
|
|
|
920
|
-
// 如果有 fallback 原因,顯示通知
|
|
921
937
|
if (data.fallbackReason) {
|
|
922
938
|
showToast("warning", "模式切換", data.fallbackReason);
|
|
923
939
|
}
|
|
924
940
|
|
|
925
|
-
// 如果是 fallback,badge 顯示不同的樣式
|
|
926
941
|
let badgeTools1 = data.mode === "cli" ? `CLI (${data.cliTool})` : "API";
|
|
927
942
|
if (data.fallbackReason) {
|
|
928
943
|
badgeTools1 = "API (fallback)";
|
|
@@ -1092,10 +1107,18 @@ export async function triggerAutoAIReply() {
|
|
|
1092
1107
|
const data = await response.json();
|
|
1093
1108
|
removeThinkingEntry();
|
|
1094
1109
|
|
|
1110
|
+
if (data.debug) {
|
|
1111
|
+
addDebugEntry(data.debug, {
|
|
1112
|
+
title: "Debug 資訊 (自動回覆)",
|
|
1113
|
+
collapsed: true,
|
|
1114
|
+
timestamp: Date.now(),
|
|
1115
|
+
badge: data.debug.elapsedMs ? `${data.debug.elapsedMs}ms` : undefined,
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1095
1119
|
if (data.success) {
|
|
1096
1120
|
updateConversationMode(data.mode, data.cliTool);
|
|
1097
1121
|
|
|
1098
|
-
// 如果有 fallback 原因,顯示通知
|
|
1099
1122
|
if (data.fallbackReason) {
|
|
1100
1123
|
showToast("warning", "模式切換", data.fallbackReason);
|
|
1101
1124
|
}
|
|
@@ -1108,7 +1131,6 @@ export async function triggerAutoAIReply() {
|
|
|
1108
1131
|
finalReply = "以下為我的回覆:\n" + data.reply;
|
|
1109
1132
|
}
|
|
1110
1133
|
|
|
1111
|
-
// 如果是 fallback,badge 顯示不同的樣式
|
|
1112
1134
|
let badgeAuto1 = data.mode === "cli" ? `CLI (${data.cliTool})` : "API";
|
|
1113
1135
|
if (data.fallbackReason) {
|
|
1114
1136
|
badgeAuto1 = "API (fallback)";
|
|
@@ -1224,6 +1246,15 @@ export async function triggerAutoAIReply() {
|
|
|
1224
1246
|
const data = await response.json();
|
|
1225
1247
|
removeThinkingEntry();
|
|
1226
1248
|
|
|
1249
|
+
if (data.debug) {
|
|
1250
|
+
addDebugEntry(data.debug, {
|
|
1251
|
+
title: `Debug (自動第 ${round} 輪)`,
|
|
1252
|
+
collapsed: true,
|
|
1253
|
+
timestamp: Date.now(),
|
|
1254
|
+
badge: data.debug.elapsedMs ? `${data.debug.elapsedMs}ms` : undefined,
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1227
1258
|
if (!data.success) {
|
|
1228
1259
|
addConversationEntry(ConversationEntryType.ERROR, data.error || "AI 回覆失敗", {
|
|
1229
1260
|
title: "錯誤",
|
|
@@ -1236,12 +1267,10 @@ export async function triggerAutoAIReply() {
|
|
|
1236
1267
|
|
|
1237
1268
|
updateConversationMode(data.mode, data.cliTool);
|
|
1238
1269
|
|
|
1239
|
-
// 如果有 fallback 原因,顯示通知
|
|
1240
1270
|
if (data.fallbackReason) {
|
|
1241
1271
|
showToast("warning", "模式切換", data.fallbackReason);
|
|
1242
1272
|
}
|
|
1243
1273
|
|
|
1244
|
-
// 如果是 fallback,badge 顯示不同的樣式
|
|
1245
1274
|
let badgeAuto2 = data.mode === "cli" ? `CLI (${data.cliTool})` : "API";
|
|
1246
1275
|
if (data.fallbackReason) {
|
|
1247
1276
|
badgeAuto2 = "API (fallback)";
|
|
@@ -108,6 +108,12 @@ export function startCloseCountdown() {
|
|
|
108
108
|
setCloseCountdownInterval(null);
|
|
109
109
|
console.log("倒數結束,關閉頁面");
|
|
110
110
|
window.close();
|
|
111
|
+
setTimeout(() => {
|
|
112
|
+
document.body.innerHTML = `
|
|
113
|
+
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100vh;font-family:system-ui;color:#888;">
|
|
114
|
+
<p style="font-size:1.2rem;">✅ 回應已送出,您可以關閉此分頁。</p>
|
|
115
|
+
</div>`;
|
|
116
|
+
}, 300);
|
|
111
117
|
}
|
|
112
118
|
}, 1000);
|
|
113
119
|
|
package/dist/static/style.css
CHANGED
|
@@ -2075,6 +2075,112 @@ textarea.form-control {
|
|
|
2075
2075
|
border-left-color: var(--accent-red);
|
|
2076
2076
|
}
|
|
2077
2077
|
|
|
2078
|
+
.conversation-entry.entry-debug {
|
|
2079
|
+
border-left-color: var(--accent-orange, #f97316);
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
.conversation-header-right {
|
|
2083
|
+
display: flex;
|
|
2084
|
+
align-items: center;
|
|
2085
|
+
gap: var(--spacing-sm);
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
.debug-toggle {
|
|
2089
|
+
display: flex;
|
|
2090
|
+
align-items: center;
|
|
2091
|
+
gap: 4px;
|
|
2092
|
+
cursor: pointer;
|
|
2093
|
+
font-size: 14px;
|
|
2094
|
+
user-select: none;
|
|
2095
|
+
opacity: 0.6;
|
|
2096
|
+
transition: opacity 0.2s;
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
.debug-toggle:hover {
|
|
2100
|
+
opacity: 1;
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2103
|
+
.debug-toggle input[type="checkbox"] {
|
|
2104
|
+
width: 14px;
|
|
2105
|
+
height: 14px;
|
|
2106
|
+
cursor: pointer;
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
.debug-info-grid {
|
|
2110
|
+
display: grid;
|
|
2111
|
+
grid-template-columns: 1fr 1fr;
|
|
2112
|
+
gap: 4px 16px;
|
|
2113
|
+
margin-bottom: 8px;
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
.debug-row {
|
|
2117
|
+
display: flex;
|
|
2118
|
+
justify-content: space-between;
|
|
2119
|
+
padding: 3px 8px;
|
|
2120
|
+
border-radius: 4px;
|
|
2121
|
+
background: var(--bg-primary);
|
|
2122
|
+
font-size: 12px;
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
.debug-label {
|
|
2126
|
+
color: var(--text-secondary);
|
|
2127
|
+
font-weight: 500;
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
.debug-value {
|
|
2131
|
+
color: var(--text-primary);
|
|
2132
|
+
font-family: "Consolas", "Monaco", monospace;
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
.debug-sections {
|
|
2136
|
+
margin-top: 8px;
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2139
|
+
.debug-section-title {
|
|
2140
|
+
font-size: 12px;
|
|
2141
|
+
font-weight: 600;
|
|
2142
|
+
color: var(--text-secondary);
|
|
2143
|
+
margin-bottom: 4px;
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
.debug-table {
|
|
2147
|
+
width: 100%;
|
|
2148
|
+
border-collapse: collapse;
|
|
2149
|
+
font-size: 12px;
|
|
2150
|
+
font-family: "Consolas", "Monaco", monospace;
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
.debug-table th,
|
|
2154
|
+
.debug-table td {
|
|
2155
|
+
padding: 4px 8px;
|
|
2156
|
+
border: 1px solid var(--border-color);
|
|
2157
|
+
text-align: left;
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
.debug-table th {
|
|
2161
|
+
background: var(--bg-hover);
|
|
2162
|
+
color: var(--text-secondary);
|
|
2163
|
+
font-weight: 600;
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
.debug-table td {
|
|
2167
|
+
color: var(--text-primary);
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
.debug-error {
|
|
2171
|
+
margin-top: 8px;
|
|
2172
|
+
padding: 6px 10px;
|
|
2173
|
+
background: rgba(239, 68, 68, 0.1);
|
|
2174
|
+
border: 1px solid rgba(239, 68, 68, 0.3);
|
|
2175
|
+
border-radius: 4px;
|
|
2176
|
+
font-size: 12px;
|
|
2177
|
+
color: var(--accent-red);
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
.debug-body {
|
|
2181
|
+
font-size: 12px;
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2078
2184
|
.entry-summary {
|
|
2079
2185
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
2080
2186
|
cursor: pointer;
|