@hirohsu/user-web-feedback 2.8.9 → 2.8.11

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.
Files changed (34) hide show
  1. package/README.md +953 -953
  2. package/dist/cli.cjs +33309 -46041
  3. package/dist/index.cjs +35773 -48507
  4. package/dist/static/app.js +385 -385
  5. package/dist/static/components/navbar.css +406 -406
  6. package/dist/static/components/navbar.html +49 -49
  7. package/dist/static/components/navbar.js +211 -211
  8. package/dist/static/dashboard.css +495 -495
  9. package/dist/static/dashboard.html +95 -95
  10. package/dist/static/dashboard.js +540 -540
  11. package/dist/static/favicon.svg +27 -27
  12. package/dist/static/index.html +541 -541
  13. package/dist/static/logs.html +376 -376
  14. package/dist/static/logs.js +442 -442
  15. package/dist/static/mcp-settings.html +797 -797
  16. package/dist/static/mcp-settings.js +884 -884
  17. package/dist/static/modules/app-core.js +124 -124
  18. package/dist/static/modules/conversation-panel.js +247 -247
  19. package/dist/static/modules/feedback-handler.js +1420 -1420
  20. package/dist/static/modules/image-handler.js +155 -155
  21. package/dist/static/modules/log-viewer.js +296 -296
  22. package/dist/static/modules/mcp-manager.js +474 -474
  23. package/dist/static/modules/prompt-manager.js +364 -364
  24. package/dist/static/modules/settings-manager.js +299 -299
  25. package/dist/static/modules/socket-manager.js +170 -170
  26. package/dist/static/modules/state-manager.js +352 -352
  27. package/dist/static/modules/timer-controller.js +243 -243
  28. package/dist/static/modules/ui-helpers.js +246 -246
  29. package/dist/static/settings.html +426 -426
  30. package/dist/static/settings.js +767 -767
  31. package/dist/static/style.css +2156 -2156
  32. package/dist/static/terminals.html +357 -357
  33. package/dist/static/terminals.js +321 -321
  34. package/package.json +95 -95
@@ -1,247 +1,247 @@
1
- /**
2
- * conversation-panel.js
3
- * 對話面板元件 - 顯示 AI 對話流程
4
- * 支援 6 種對話條目類型: prompt, thinking, tool, result, ai, error
5
- */
6
-
7
- import { escapeHtml } from './ui-helpers.js';
8
-
9
- /**
10
- * 對話條目類型
11
- */
12
- export const ConversationEntryType = {
13
- PROMPT: 'prompt',
14
- THINKING: 'thinking',
15
- TOOL: 'tool',
16
- RESULT: 'result',
17
- AI: 'ai',
18
- ERROR: 'error'
19
- };
20
-
21
- /**
22
- * 對話條目視覺配置
23
- */
24
- const entryConfig = {
25
- prompt: {
26
- icon: '📤',
27
- title: '提示詞',
28
- className: 'entry-prompt',
29
- borderColor: 'var(--accent-blue)'
30
- },
31
- thinking: {
32
- icon: '🤔',
33
- title: 'AI 思考中',
34
- className: 'entry-thinking',
35
- borderColor: 'var(--accent-yellow)'
36
- },
37
- tool: {
38
- icon: '🔧',
39
- title: '工具呼叫',
40
- className: 'entry-tool',
41
- borderColor: 'var(--accent-purple, #a855f7)'
42
- },
43
- result: {
44
- icon: '📥',
45
- title: '工具結果',
46
- className: 'entry-result',
47
- borderColor: 'var(--accent-cyan, #06b6d4)'
48
- },
49
- ai: {
50
- icon: '🤖',
51
- title: 'AI 回覆',
52
- className: 'entry-ai',
53
- borderColor: 'var(--accent-green)'
54
- },
55
- error: {
56
- icon: '❌',
57
- title: '錯誤',
58
- className: 'entry-error',
59
- borderColor: 'var(--accent-red)'
60
- }
61
- };
62
-
63
- /**
64
- * 建立對話面板容器
65
- */
66
- export function createConversationPanel() {
67
- const panel = document.createElement('div');
68
- panel.id = 'conversationPanel';
69
- panel.className = 'conversation-panel';
70
- panel.innerHTML = `
71
- <div class="conversation-header">
72
- <div class="conversation-title">
73
- <span class="icon">💬</span>
74
- <span id="conversationTitle">AI 對話</span>
75
- </div>
76
- <div class="conversation-mode">
77
- <span class="mode-indicator" id="conversationModeIndicator"></span>
78
- <span id="conversationMode">準備中</span>
79
- </div>
80
- </div>
81
- <div class="conversation-body" id="conversationBody">
82
- <!-- 對話條目會動態添加 -->
83
- </div>
84
- <div class="conversation-footer">
85
- <button type="button" id="closeConversation" class="btn btn-secondary">關閉</button>
86
- </div>
87
- `;
88
- return panel;
89
- }
90
-
91
- /**
92
- * 建立對話條目元素
93
- */
94
- export function createConversationEntry(type, content, options = {}) {
95
- const config = entryConfig[type] || entryConfig.ai;
96
- const entry = document.createElement('div');
97
- entry.className = `conversation-entry ${config.className}`;
98
- entry.style.borderLeftColor = config.borderColor;
99
-
100
- const titleText = options.title || config.title;
101
- const collapsed = options.collapsed ?? (type === 'prompt' || type === 'tool');
102
- const timestamp = options.timestamp ? formatTimestamp(options.timestamp) : '';
103
-
104
- let contentHtml = '';
105
- if (typeof content === 'string') {
106
- contentHtml = `<pre class="entry-content">${escapeHtml(content)}</pre>`;
107
- } else if (content && typeof content === 'object') {
108
- contentHtml = `<pre class="entry-content">${escapeHtml(JSON.stringify(content, null, 2))}</pre>`;
109
- }
110
-
111
- entry.innerHTML = `
112
- <details ${collapsed ? '' : 'open'}>
113
- <summary class="entry-summary">
114
- <span class="entry-icon">${config.icon}</span>
115
- <span class="entry-title">${titleText}</span>
116
- ${timestamp ? `<span class="entry-timestamp">${timestamp}</span>` : ''}
117
- ${options.badge ? `<span class="entry-badge">${options.badge}</span>` : ''}
118
- </summary>
119
- <div class="entry-body">
120
- ${contentHtml}
121
- </div>
122
- </details>
123
- `;
124
-
125
- return entry;
126
- }
127
-
128
- /**
129
- * 新增對話條目到面板
130
- */
131
- export function addConversationEntry(type, content, options = {}) {
132
- const body = document.getElementById('conversationBody');
133
- if (!body) return null;
134
-
135
- const entry = createConversationEntry(type, content, options);
136
- body.appendChild(entry);
137
- body.scrollTop = body.scrollHeight;
138
-
139
- return entry;
140
- }
141
-
142
- /**
143
- * 清空對話面板
144
- */
145
- export function clearConversationPanel() {
146
- const body = document.getElementById('conversationBody');
147
- if (body) {
148
- body.innerHTML = '';
149
- }
150
- }
151
-
152
- /**
153
- * 更新對話面板模式顯示
154
- */
155
- export function updateConversationMode(mode, cliTool = null) {
156
- const modeElement = document.getElementById('conversationMode');
157
- const indicator = document.getElementById('conversationModeIndicator');
158
-
159
- if (modeElement) {
160
- if (mode === 'cli' && cliTool) {
161
- modeElement.textContent = `CLI (${cliTool})`;
162
- } else if (mode === 'api') {
163
- modeElement.textContent = 'API';
164
- } else {
165
- modeElement.textContent = mode;
166
- }
167
- }
168
-
169
- if (indicator) {
170
- indicator.className = 'mode-indicator';
171
- if (mode === 'cli') {
172
- indicator.classList.add('mode-cli');
173
- } else if (mode === 'api') {
174
- indicator.classList.add('mode-api');
175
- }
176
- }
177
- }
178
-
179
- /**
180
- * 更新對話面板標題
181
- */
182
- export function updateConversationTitle(title) {
183
- const titleElement = document.getElementById('conversationTitle');
184
- if (titleElement) {
185
- titleElement.textContent = title;
186
- }
187
- }
188
-
189
- /**
190
- * 顯示對話面板
191
- */
192
- export function showConversationPanel() {
193
- let panel = document.getElementById('aiConversationPanel');
194
- if (!panel) {
195
- panel = document.createElement('div');
196
- panel.id = 'aiConversationPanel';
197
- panel.className = 'ai-conversation-overlay';
198
- panel.appendChild(createConversationPanel());
199
- document.body.appendChild(panel);
200
-
201
- const closeBtn = panel.querySelector('#closeConversation');
202
- if (closeBtn) {
203
- closeBtn.onclick = hideConversationPanel;
204
- }
205
- }
206
- panel.style.display = 'flex';
207
- clearConversationPanel();
208
- }
209
-
210
- /**
211
- * 隱藏對話面板
212
- */
213
- export function hideConversationPanel() {
214
- const panel = document.getElementById('aiConversationPanel');
215
- if (panel) {
216
- panel.style.display = 'none';
217
- }
218
- }
219
-
220
- /**
221
- * 格式化時間戳記
222
- */
223
- function formatTimestamp(timestamp) {
224
- const date = new Date(timestamp);
225
- return date.toLocaleTimeString('zh-TW', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
226
- }
227
-
228
- /**
229
- * 新增思考中動畫條目
230
- */
231
- export function addThinkingEntry(message = 'AI 思考中...') {
232
- return addConversationEntry(ConversationEntryType.THINKING, message, {
233
- collapsed: false,
234
- badge: '⏳'
235
- });
236
- }
237
-
238
- /**
239
- * 移除思考中條目
240
- */
241
- export function removeThinkingEntry() {
242
- const body = document.getElementById('conversationBody');
243
- if (!body) return;
244
-
245
- const thinkingEntries = body.querySelectorAll('.entry-thinking');
246
- thinkingEntries.forEach(entry => entry.remove());
247
- }
1
+ /**
2
+ * conversation-panel.js
3
+ * 對話面板元件 - 顯示 AI 對話流程
4
+ * 支援 6 種對話條目類型: prompt, thinking, tool, result, ai, error
5
+ */
6
+
7
+ import { escapeHtml } from './ui-helpers.js';
8
+
9
+ /**
10
+ * 對話條目類型
11
+ */
12
+ export const ConversationEntryType = {
13
+ PROMPT: 'prompt',
14
+ THINKING: 'thinking',
15
+ TOOL: 'tool',
16
+ RESULT: 'result',
17
+ AI: 'ai',
18
+ ERROR: 'error'
19
+ };
20
+
21
+ /**
22
+ * 對話條目視覺配置
23
+ */
24
+ const entryConfig = {
25
+ prompt: {
26
+ icon: '📤',
27
+ title: '提示詞',
28
+ className: 'entry-prompt',
29
+ borderColor: 'var(--accent-blue)'
30
+ },
31
+ thinking: {
32
+ icon: '🤔',
33
+ title: 'AI 思考中',
34
+ className: 'entry-thinking',
35
+ borderColor: 'var(--accent-yellow)'
36
+ },
37
+ tool: {
38
+ icon: '🔧',
39
+ title: '工具呼叫',
40
+ className: 'entry-tool',
41
+ borderColor: 'var(--accent-purple, #a855f7)'
42
+ },
43
+ result: {
44
+ icon: '📥',
45
+ title: '工具結果',
46
+ className: 'entry-result',
47
+ borderColor: 'var(--accent-cyan, #06b6d4)'
48
+ },
49
+ ai: {
50
+ icon: '🤖',
51
+ title: 'AI 回覆',
52
+ className: 'entry-ai',
53
+ borderColor: 'var(--accent-green)'
54
+ },
55
+ error: {
56
+ icon: '❌',
57
+ title: '錯誤',
58
+ className: 'entry-error',
59
+ borderColor: 'var(--accent-red)'
60
+ }
61
+ };
62
+
63
+ /**
64
+ * 建立對話面板容器
65
+ */
66
+ export function createConversationPanel() {
67
+ const panel = document.createElement('div');
68
+ panel.id = 'conversationPanel';
69
+ panel.className = 'conversation-panel';
70
+ panel.innerHTML = `
71
+ <div class="conversation-header">
72
+ <div class="conversation-title">
73
+ <span class="icon">💬</span>
74
+ <span id="conversationTitle">AI 對話</span>
75
+ </div>
76
+ <div class="conversation-mode">
77
+ <span class="mode-indicator" id="conversationModeIndicator"></span>
78
+ <span id="conversationMode">準備中</span>
79
+ </div>
80
+ </div>
81
+ <div class="conversation-body" id="conversationBody">
82
+ <!-- 對話條目會動態添加 -->
83
+ </div>
84
+ <div class="conversation-footer">
85
+ <button type="button" id="closeConversation" class="btn btn-secondary">關閉</button>
86
+ </div>
87
+ `;
88
+ return panel;
89
+ }
90
+
91
+ /**
92
+ * 建立對話條目元素
93
+ */
94
+ export function createConversationEntry(type, content, options = {}) {
95
+ const config = entryConfig[type] || entryConfig.ai;
96
+ const entry = document.createElement('div');
97
+ entry.className = `conversation-entry ${config.className}`;
98
+ entry.style.borderLeftColor = config.borderColor;
99
+
100
+ const titleText = options.title || config.title;
101
+ const collapsed = options.collapsed ?? (type === 'prompt' || type === 'tool');
102
+ const timestamp = options.timestamp ? formatTimestamp(options.timestamp) : '';
103
+
104
+ let contentHtml = '';
105
+ if (typeof content === 'string') {
106
+ contentHtml = `<pre class="entry-content">${escapeHtml(content)}</pre>`;
107
+ } else if (content && typeof content === 'object') {
108
+ contentHtml = `<pre class="entry-content">${escapeHtml(JSON.stringify(content, null, 2))}</pre>`;
109
+ }
110
+
111
+ entry.innerHTML = `
112
+ <details ${collapsed ? '' : 'open'}>
113
+ <summary class="entry-summary">
114
+ <span class="entry-icon">${config.icon}</span>
115
+ <span class="entry-title">${titleText}</span>
116
+ ${timestamp ? `<span class="entry-timestamp">${timestamp}</span>` : ''}
117
+ ${options.badge ? `<span class="entry-badge">${options.badge}</span>` : ''}
118
+ </summary>
119
+ <div class="entry-body">
120
+ ${contentHtml}
121
+ </div>
122
+ </details>
123
+ `;
124
+
125
+ return entry;
126
+ }
127
+
128
+ /**
129
+ * 新增對話條目到面板
130
+ */
131
+ export function addConversationEntry(type, content, options = {}) {
132
+ const body = document.getElementById('conversationBody');
133
+ if (!body) return null;
134
+
135
+ const entry = createConversationEntry(type, content, options);
136
+ body.appendChild(entry);
137
+ body.scrollTop = body.scrollHeight;
138
+
139
+ return entry;
140
+ }
141
+
142
+ /**
143
+ * 清空對話面板
144
+ */
145
+ export function clearConversationPanel() {
146
+ const body = document.getElementById('conversationBody');
147
+ if (body) {
148
+ body.innerHTML = '';
149
+ }
150
+ }
151
+
152
+ /**
153
+ * 更新對話面板模式顯示
154
+ */
155
+ export function updateConversationMode(mode, cliTool = null) {
156
+ const modeElement = document.getElementById('conversationMode');
157
+ const indicator = document.getElementById('conversationModeIndicator');
158
+
159
+ if (modeElement) {
160
+ if (mode === 'cli' && cliTool) {
161
+ modeElement.textContent = `CLI (${cliTool})`;
162
+ } else if (mode === 'api') {
163
+ modeElement.textContent = 'API';
164
+ } else {
165
+ modeElement.textContent = mode;
166
+ }
167
+ }
168
+
169
+ if (indicator) {
170
+ indicator.className = 'mode-indicator';
171
+ if (mode === 'cli') {
172
+ indicator.classList.add('mode-cli');
173
+ } else if (mode === 'api') {
174
+ indicator.classList.add('mode-api');
175
+ }
176
+ }
177
+ }
178
+
179
+ /**
180
+ * 更新對話面板標題
181
+ */
182
+ export function updateConversationTitle(title) {
183
+ const titleElement = document.getElementById('conversationTitle');
184
+ if (titleElement) {
185
+ titleElement.textContent = title;
186
+ }
187
+ }
188
+
189
+ /**
190
+ * 顯示對話面板
191
+ */
192
+ export function showConversationPanel() {
193
+ let panel = document.getElementById('aiConversationPanel');
194
+ if (!panel) {
195
+ panel = document.createElement('div');
196
+ panel.id = 'aiConversationPanel';
197
+ panel.className = 'ai-conversation-overlay';
198
+ panel.appendChild(createConversationPanel());
199
+ document.body.appendChild(panel);
200
+
201
+ const closeBtn = panel.querySelector('#closeConversation');
202
+ if (closeBtn) {
203
+ closeBtn.onclick = hideConversationPanel;
204
+ }
205
+ }
206
+ panel.style.display = 'flex';
207
+ clearConversationPanel();
208
+ }
209
+
210
+ /**
211
+ * 隱藏對話面板
212
+ */
213
+ export function hideConversationPanel() {
214
+ const panel = document.getElementById('aiConversationPanel');
215
+ if (panel) {
216
+ panel.style.display = 'none';
217
+ }
218
+ }
219
+
220
+ /**
221
+ * 格式化時間戳記
222
+ */
223
+ function formatTimestamp(timestamp) {
224
+ const date = new Date(timestamp);
225
+ return date.toLocaleTimeString('zh-TW', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
226
+ }
227
+
228
+ /**
229
+ * 新增思考中動畫條目
230
+ */
231
+ export function addThinkingEntry(message = 'AI 思考中...') {
232
+ return addConversationEntry(ConversationEntryType.THINKING, message, {
233
+ collapsed: false,
234
+ badge: '⏳'
235
+ });
236
+ }
237
+
238
+ /**
239
+ * 移除思考中條目
240
+ */
241
+ export function removeThinkingEntry() {
242
+ const body = document.getElementById('conversationBody');
243
+ if (!body) return;
244
+
245
+ const thinkingEntries = body.querySelectorAll('.entry-thinking');
246
+ thinkingEntries.forEach(entry => entry.remove());
247
+ }